You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ADN-DMR-Peer-Server/docs/en/monitor/self-service.md

6.6 KiB

Self-service

Self-service lets a hotspot owner log in to the dashboard, edit their device options (static TG lists, default reflector, timer, language, etc.), and have those options pushed to the ADN DMR Peer Server without manually editing YAML on the server.

It involves four pieces: MySQL (Clients table), monitor API (FastAPI session + REST), React (/self-service page), and the integrated hotspot proxy in adn-server (periodic RPTO to the master). The peer server is the component that ultimately applies RPTO / OPTIONS to the running bridge and hotspot behaviour.


Prerequisites

  1. SELF_SERVICE block in adn-monitor.yaml with valid MySQL credentials and PBKDF2 parameters matching your password tooling (same salt/iterations as hotspot_proxy_self_service.py when used).
  2. DASHBOARD.SELF_SERVICE: true so the UI shows the Self-service menu entry (and monitor.py exposes /api/self-service/* when DB connects).
  3. Clients table populated with rows: callsign, int_id (DMR ID), psswd (PBKDF2-SHA256 hex), options (semicolon-separated KEY=value), logged_in, host, modified, etc. (see adn-monitor DB schema / migration scripts in the repo).
  4. Hotspot traffic should pass through the proxy if you rely on modified and RPTO push (see flow below).

Authentication

Endpoint Purpose
POST /api/auth/login Body: callsign, password. Verifies PBKDF2 hash against Clients.psswd for rows with logged_in = 1. On success: session cookie with user_id, int_ids (all DMR IDs for that callsign).
GET /api/auth/login-by-ip Optional: single user match for Clients.host = client IP (same session shape).
POST /api/auth/logout Clears session.
GET /api/auth/me Returns { callsign, int_ids, selected_int_id } for the React app.

Session lifetime is extended on activity (SelfServiceController uses a long inactivity timeout).


Device API (after login)

Method Path Purpose
GET /api/self-service/device?int_id= Load Clients row for that int_id (must be in session int_ids). Returns JSON: int_id, callsign, mode, options parsed into TS1/TS2 lists, DIAL, VOICE, LANG, SINGLE, TIMER.
POST /api/self-service/device/options Body: int_id, options string (Homebrew OPTIONS line). Must end with ;. Max length 4096. Updates DB: Clients.options, sets modified = 1.
GET /api/self-service/device/modified?int_id= Returns **`{ modified: 0
POST /api/self-service/device/select Body: int_id — set session selected_int_id when the user has multiple devices.

End-to-end flow (why options reach the hotspot)

sequenceDiagram
  participant UI as React /self-service
  participant API as monitor FastAPI
  participant DB as MySQL Clients
  participant SRV as adn-server PROXY
  participant HS as Hotspot

  UI->>API: POST /api/self-service/device/options
  API->>DB: UPDATE options, modified = 1
  Note over SRV: send_opts loop ~every 10 s
  SRV->>DB: read rows with modified = 1
  SRV->>SRV: RPTO on MASTER leg
  SRV->>HS: OPTIONS via HBP path
  SRV->>DB: clear modified
  1. User saves options in the web UI → monitor API writes Clients.options and modified = 1.
  2. The hotspot proxy runs send_opts on a loop (~every 10 s). For rows with modified = 1, it reads options from the DB, sends RPTO (options) to the peer server at (MASTER, assigned_dest_port), then clears modified in the DB.
  3. The ADN DMR Peer Server receives RPTO on the MASTER leg and updates its OPTIONS / bridge state (same path as a normal hotspot registration refresh).
  4. The server pushes the appropriate signalling to the hotspot so static TG / reflector / timer settings take effect without a full hotspot restart (exact behaviour matches the HBP OPTIONS flow in the server).

Important: the proxy sends RPTO to the master only, not to the hotspot directly. If the proxy is not in the path, you must ensure another mechanism applies OPTIONS or you run the server without this proxy path.

Prerequisite: the hotspot must send PASS= in its OPTIONS line for password login and bidirectional DB sync. If the hotspot sends explicit content (TGs, SINGLE, etc.) without PASS=, the server takes options directly from that line and the DB row is ignored. If the hotspot sends no RPTO at all (timer expires) or an empty OPTIONS, the server falls back to the database. See Hotspot proxy — OPTIONS line behaviour.


logged_in reconciliation

The logged_in flag gates both password and IP-based login: only rows with logged_in = 1 can authenticate on the dashboard. The peer server keeps this flag accurate by reconciling it against actually connected peers every 120 s (the lst_seen loop):

  • Peers currently connected to the inject-only MASTER get logged_in = 1.
  • All other rows get logged_in = 0.
  • The loop starts with now=True, so the first tick at boot clears every stale flag immediately — after a server restart, hotspots that did not reconnect cannot authenticate via login-by-IP.

This replaced the legacy hourly clean_tbl (24 h idle sweep), which left logged_in = 1 for peers no longer connected after a restart.


Password hashing

AuthenticateUser uses:

hash_pbkdf2('sha256', password, PBKDF2_SALT, PBKDF2_ITERATIONS)

stored as hex in Clients.psswd. The same parameters must be used wherever passwords are registered (e.g. hotspot_proxy_self_service.py in the adn-dmr-server / tooling repo).


UI

  • Route /self-service in the React app (SelfService.tsx): loads /api/auth/me, then device details for selected_int_id, edit options string, save.
  • External SelfCare link (e.g. selfcare.adn.systems) may appear in the nav as a separate product — not the same as this local self-service.

See also

Powered by TurnKey Linux.