7.5 KiB
Hotspot proxy (integrated)
ADN DMR Peer Server includes an integrated hotspot proxy: one process (adn-server.py) accepts Homebrew (HBP) from many hotspots on a single UDP port and injects traffic into a configured MASTER system. You do not need a separate adn-proxy process when this mode is enabled.
Configuration lives in adn-server.yaml under PROXY and optional SELF_SERVICE (same MySQL Clients table as adn-monitor).
When to use it
| Deployment | What to run |
|---|---|
| Typical ADN stack (monitor + dashboard + many Pi-Star hotspots) | adn-server.py with PROXY + SELF_SERVICE. |
The integrated proxy uses fan-in: hotspots only need PROXY.LISTEN_PORT (e.g. 62031). The target MASTER is inject-only — it does not bind its own UDP port for that system (no per-hotspot port range on the server host).
flowchart LR
HS1[Hotspot A] -->|UDP HBP| LP[PROXY LISTEN_PORT]
HS2[Hotspot B] -->|UDP HBP| LP
LP -->|inject| MASTER[TARGET_SYSTEM MASTER]
Optional dependency (self-service)
MySQL self-service requires mysqlclient:
pip install -e ".[selfservice]"
If USE_SELFSERVICE: true but mysqlclient is missing, startup fails with a clear error. Set USE_SELFSERVICE: false to run the proxy without DB (no dashboard-driven RPTO updates).
PROXY keys
| Key | Role |
|---|---|
| LISTEN_PORT | UDP port where hotspots connect (the address users configure on the hotspot). |
| LISTEN_IP | Bind address; empty = all interfaces. |
| TARGET_SYSTEM | Name of the SYSTEMS MASTER entry that receives injected HBP (must exist and be ENABLED). |
| TIMEOUT | Idle session timeout (seconds); expired sessions are torn down on the MASTER. |
| DEBUG | Verbose packet logging. |
| CLIENT_INFO | Log connect/disconnect per radio ID. |
| BLACK_LIST | Block listed radio IDs. |
| IP_BLACK_LIST | Block source IPs (with optional expiry). |
There is no MASTER, PORT, or GENERATOR in integrated PROXY — those belong to the legacy standalone proxy. The target MASTER uses MAX_PEERS (not a UDP port range) to cap concurrent hotspot sessions.
Example (from adn-server.example.yaml):
PROXY:
LISTEN_PORT: 62031
LISTEN_IP: ""
TARGET_SYSTEM: SYSTEM
TIMEOUT: 30
DEBUG: false
CLIENT_INFO: true
BLACK_LIST: []
IP_BLACK_LIST: {}
Inject-only target MASTER
When PROXY.TARGET_SYSTEM points at a system (e.g. SYSTEM), startup removes IP / PORT from that MASTER block. Hotspots never connect directly to the conference port; all HBP enters via LISTEN_PORT.
Set MAX_PEERS on the target MASTER to the maximum concurrent proxied hotspots (e.g. 102). Other MASTER systems (e.g. ECHO, D-APRS) keep normal IP / PORT binds if they are not the proxy target.
SELF_SERVICE keys
Same semantics as adn-monitor.yaml — shared Clients table, modified flag, RPTO toward the MASTER.
| Key | Role |
|---|---|
| USE_SELFSERVICE | Enable MySQL-backed options sync (true / false). |
| PBKDF2_SALT, PBKDF2_ITERATIONS | Must match monitor/backend for password hashing. |
MariaDB connection settings live in the top-level DATABASE block (shared with dynamic TG persistence) — see Configuration.
On startup the server logs (SELF_SERVICE) Database connection test: OK and (SELF_SERVICE) Enabled when the pool connects. Self-service runs asynchronously; voice forwarding is not blocked on DB latency.
Details of the dashboard flow: Self-service.
Multi-hotspot behaviour
- Each authenticated hotspot is a peer on the inject-only MASTER with its own OPTIONS (static TGs). Repeat and monitor fan-out respect per-peer OPTIONS — traffic for a TG is not sent to peers that did not select it.
- Parrot / echo talkgroups 9990–9999 are point-to-point: they bypass the OPTIONS filter and return only to the exact peer that originated the call (
RX_PEERon the slot), never to other hotspots of the same user. With a single connected peer, it is delivered to that peer (legacy behaviour). See Echo — Multi-hotspot behaviour and Special numbers.
OPTIONS line behaviour
After login (RPTL → RPTK → RPTC), the proxy starts a 10 s timer waiting for the hotspot's RPTO packet with its OPTIONS line. What the hotspot sends (or does not send) determines who is the source of truth for the peer's static talkgroups:
| Hotspot sends in the RPTO | Who defines the TGs | Behaviour |
|---|---|---|
OPTIONS=PASS=xxxxxx; |
Self-service (dashboard) | The proxy processes the PASS=, verifies the individual password (PBKDF2 against Clients.psswd), marks the peer as authenticated, cancels the 10 s timer, and pushes the DB-configured TGs to the master. The user can log in by password and auto-login by IP on the dashboard. |
OPTIONS= (empty) |
Self-service (dashboard) | The proxy reads the TGs from the DB and injects them to the master. The user can only use auto-login by IP on the dashboard (no password). |
| No RPTO at all (10 s timer expires) | Self-service (dashboard) | The proxy assumes the hotspot has no OPTIONS of its own and falls back to the DB. Same effect as OPTIONS= empty. |
OPTIONS=TS2=730444;SINGLE=0; (content without PASS=) |
The hotspot itself | The master takes the TGs directly from the OPTIONS line. The DB is ignored. The user can only use auto-login by IP on the dashboard (no password). |
Key rule: self-service is the source of truth unless the hotspot sends explicit content (TGs, SINGLE, TIMER, etc.) in its OPTIONS line. In that case, what the hotspot says takes precedence and self-service is ignored.
Individual password and dashboard login
- If the hotspot never sends
PASS=in its RPTO, the user cannot log in to the dashboard with a password. They can only use auto-login by IP (if their IP matchesClients.host). - To enable password login on the dashboard, the hotspot must send
OPTIONS=PASS=your_password;in its configuration (Pi-Star / WPSD / MMDVMoptsfile). The password must match the PBKDF2 hash stored inClients.psswd. - The
PASS=flow is what activates bidirectional sync: the proxy stores the hash, notes themodifiedflag, and pushes the DB-configured TGs to the master.
Hot reload (SIGHUP)
Applied without restart (active proxy sessions stay up):
PROXY: TIMEOUT, DEBUG, CLIENT_INFO, BLACK_LIST, IP_BLACK_LISTSELF_SERVICE: merged into config (credential changes take effect on new DB operations; loops are not restarted on reload)
Requires full process restart:
PROXY.LISTEN_PORT/LISTEN_IP(bind change is logged and ignored at reload)PROXY.TARGET_SYSTEM- Enabling or disabling
USE_SELFSERVICEafter startup (start/stop MySQL loops)
See Configuration — hot reload.
See also
- Configuration — full
adn-server.yamlreference. - Monitoring and reports — TCP reports, dashboard, log rotation.
- Self-service —
Clients, RPTO timing.