docs: FastAPI-only monitor stack and integrated hotspot proxy

Remove references to PHP backend and standalone adn-proxy from monitor
documentation; point hotspot proxy setup to adn-server.yaml.
pull/1/head
Rodrigo Pérez 4 months ago
parent b2daf46f96
commit cdf486feb4

@ -43,12 +43,13 @@ More: [Introduction](server/user-guide/introduction.md).
## ADN Monitor
Dashboard, WebSocket live view, optional **PHP API**, **MySQL** self-service, and **hotspot proxy** — see [Monitor overview](monitor/index.md).
Dashboard, WebSocket live view, FastAPI API, **MySQL** self-service — see [Monitor overview](monitor/index.md). **Hotspot proxy** is integrated in **adn-server**.
| I want to… | Start here |
|------------|------------|
| `adn-server.yaml` — integrated `PROXY` / `SELF_SERVICE` | [Hotspot proxy (integrated)](server/user-guide/hotspot-proxy.md) |
| `adn-monitor.yaml`, legacy `adn-proxy.yaml`, layout | [Monitor configuration](monitor/configuration.md), [Hotspot proxy](monitor/hotspot-proxy.md) |
| `adn-monitor.yaml`, layout | [Monitor configuration](monitor/configuration.md) |
| Integrated hotspot proxy | [Hotspot proxy](server/user-guide/hotspot-proxy.md) |
| Standalone hotspot proxy (UDP port range) | [Hotspot proxy — standalone](monitor/hotspot-proxy.md#standalone-proxy-legacy-adn-monitor-repo) |
| Self-service | [Self-service](monitor/self-service.md) |
| How it connects to the server | [Monitoring and reports](server/user-guide/monitoring.md) |

@ -4,59 +4,52 @@
Under `monitor/src/adn_monitor/`:
- **Domain** — value objects, errors, opcode types.
- **Application** — `MonitorState`, `process_message` in `monitor_controller.py`, alias service, Last Heard / TG count use cases, time formatting.
- **Infrastructure** — YAML `load_config`, Twisted **TCP client** (`ReportClientFactory`) to the peer server, **WebSocket** factory for the dashboard, MySQL repositories, pickle/json decoders for `CONFIG_SND` / `BRIDGE_SND`.
- **Domain** — value objects, errors, opcode types, `UserSession`.
- **Application** — `MonitorState`, report/dashboard use cases, auth and self-service, alias service, Last Heard / TG count.
- **Infrastructure** — YAML `load_config`, FastAPI (REST + `/ws`), TCP ingest (Twisted thread) or MQTT, MySQL repositories, pickle/json decoders.
The monitor **connects outbound** to **`ADN_CONNECTION.ADN_IP:ADN_PORT`** and receives length-prefixed (netstring-style) messages. It updates in-memory **CTABLE** (masters/peers/OpenBridge) and **BTABLE** (bridges), and persists **BRDG_EVENT** outcomes when MySQL is configured.
Composition root: `infrastructure/fastapi/composition.py` (`build_monitor_api`).
## Report protocol (from the peer server)
## Unified process (`monitor.py`)
Same opcodes as documented for the server: **CONFIG_SND**, **BRIDGE_SND**, **BRDG_EVENT**, etc. The monitor decodes and applies them in `process_message` — see [Monitoring and reports](../server/user-guide/monitoring.md).
Single uvicorn/FastAPI process:
## WebSocket
| Route / role | Content |
|--------------|---------|
| `/api/*` | Dashboard config, auth, self-service, alias/status proxies |
| `/ws` | `conf,<group>` protocol — CTABLE, Last Heard, voice |
| Ingest (background) | `MONITOR_APP.INGEST`: `tcp` (client to `ADN_CONNECTION`) or `mqtt` |
| Aliases (background) | Download → import with staging + RENAME |
`monitor.py` runs a Twisted **WebSocket** on **`WEBSOCKET_SERVER.WEBSOCKET_PORT`**, pushing JSON snapshots at **`FREQUENCY`** so the React app updates without polling for core state.
Report ingest connects outbound to **`ADN_CONNECTION.ADN_IP:ADN_PORT`** (TCP) or subscribes to MQTT topics. It updates **CTABLE** / **BTABLE** and persists Last Heard when MySQL is configured.
## PHP backend
## Report protocol (from the peer server)
- **Slim 4** front controller: `backend/public/index.php`.
- Loads **`adn-monitor.yaml`** via **`ADN_CONFIG_PATH`** (same as monitor).
- **`/api/config/dashboard`** — title, language, feature flags (`selfService`, `showConsole`, …) from **`DASHBOARD`**.
- **`/api/auth/*`** — session cookie auth when **SELF_SERVICE** DB is available.
- **`/api/self-service/*`** — device options (see [Self-service](self-service.md)).
- **`/api/aliases/*`** — optional proxy to TG/bridge list URLs from **ALIASES**.
Legacy pickle, v1 HELLO, and v2 slim (`dashboard_state` + `voice_event`) — see [Monitoring and reports](../server/user-guide/monitoring.md).
## Frontend
- **Vite + React** under `frontend/`; build produces static assets served by nginx/Apache or similar.
- Uses **`API_BASE`** (build-time) to reach the PHP API and **WebSocket URL** for live data.
- Dev: Vite proxies `/api` and `/ws` to `MONITOR_APP.LISTEN_PORT`.
- Production: nginx serves `frontend/dist/` and proxies `/api` + `/ws` to the same FastAPI port.
## Hotspot proxy
**Integrated (default):** **`adn-server.py`** runs UDP fan-in from **`PROXY.LISTEN_PORT`** into **`PROXY.TARGET_SYSTEM`**; **`SELF_SERVICE`** in **`adn-server.yaml`** drives **RPTO** from MySQL **`Clients`**. See [Hotspot proxy (integrated)](../server/user-guide/hotspot-proxy.md).
**Standalone (legacy, adn-monitor repo):**
- Entry: `proxy/proxy.py`; package `src/adn_proxy/` (domain / application / infrastructure).
- Reads **`PROXY`** and **`SELF_SERVICE`** from **`adn-proxy.yaml`** by default (or from a combined monitor YAML via **`ADN_CONFIG_PATH`** — see [Hotspot proxy](hotspot-proxy.md#configuration-file)).
- For each hotspot client, allocates a UDP port in **`PORT`…`PORT+GENERATOR-1`** and forwards to **`MASTER`**.
- When **self-service** updates **`Clients.options`** and sets **`modified=1`**, the proxy sends **RPTO** to the **master** on a timer (~10 s).
**Details:** [Hotspot proxy](hotspot-proxy.md) (integrated vs standalone, config keys, startup).
The standalone **`adn-proxy`** process was removed from the **adn-monitor** repository; use integrated **`PROXY`** only.
## Typical deployment topology
```text
[Hotspots] --UDP--> [Proxy :LISTEN_PORT] --UDP--> [Peer server :PORT..PORT+GENERATOR-1]
[Hotspots] --UDP--> [adn-server PROXY] --UDP--> [peer MASTER]
|
v
MySQL (Clients)
[Peer server :REPORT_PORT] <--- TCP --- [monitor.py : connects as client]
[Peer server :REPORT_PORT] <--- TCP or MQTT --- [monitor.py ingest]
[Browser] --HTTPS--> [PHP API + static frontend]
[Browser] --WS----> [monitor WebSocket :9000]
[Browser] --HTTPS--> [Nginx: static frontend + proxy /api,/ws --> MONITOR_APP.LISTEN_PORT]
```
---

@ -1,8 +1,8 @@
# Configuration (`adn-monitor.yaml`)
This document describes **`adn-monitor.yaml`**, used by the **Python monitor** (`monitor/monitor.py`) and the **PHP backend** (`backend/public/index.php`). Default path is usually **`monitor/adn-monitor.yaml`** (override with **`ADN_CONFIG_PATH`**).
This document describes **`adn-monitor.yaml`**, used by **`monitor/monitor.py`** (FastAPI: REST, WebSocket, report ingest). Default path is usually **`monitor/adn-monitor.yaml`** (override with **`ADN_CONFIG_PATH`**).
**Integrated hotspot proxy:** **`PROXY`** and **`SELF_SERVICE`** in **`adn-server.yaml`** (see [Hotspot proxy (integrated)](../server/user-guide/hotspot-proxy.md)). **Standalone (legacy):** **`proxy/adn-proxy.yaml`** — see [Hotspot proxy](hotspot-proxy.md). **`SELF_SERVICE`** (MySQL / PBKDF2) must stay **identical** across **`adn-server.yaml`**, **`adn-monitor.yaml`**, and legacy **`adn-proxy.yaml`** when used.
**Hotspot proxy** is configured in **`adn-server.yaml`** (`PROXY` + `SELF_SERVICE`) — see [Hotspot proxy (integrated)](../server/user-guide/hotspot-proxy.md). **`SELF_SERVICE`** (MySQL / PBKDF2) must stay **identical** between **`adn-server.yaml`** and **`adn-monitor.yaml`**.
The example shipped in the **adn-monitor** repo (`monitor/adn-monitor.yaml.example`) is the template for the monitor; keys below match that file and `monitor/src/adn_monitor/infrastructure/config_loader.py` (internal names may differ).
@ -37,31 +37,13 @@ Must match the **ADN DMR Peer Server** reporting configuration.
## `SELF_SERVICE`
MySQL credentials and PBKDF2 parameters for **login** and **`Clients`** table access. **PBKDF2_SALT** and **PBKDF2_ITERATIONS** must match **`hotspot_proxy_self_service.py`** (or your password-registration tool) so stored password hashes verify in PHP and Python.
MySQL credentials and PBKDF2 parameters for **login**, **self-service**, and **`Clients`** table access. **PBKDF2_SALT** and **PBKDF2_ITERATIONS** must match **`hotspot_proxy_self_service.py`** (or your password-registration tool) so stored password hashes verify in the monitor API and **adn-server** integrated proxy.
| Key | Meaning |
|-----|---------|
| **USE_SELFSERVICE** | Used by the **proxy** config loader to enable DB-backed options / self-service paths (see proxy README). |
| **DB_SERVER**, **DB_USERNAME**, **DB_PASSWORD**, **DB_NAME**, **DB_PORT** | MySQL connection for **`Clients`** (and related) tables. |
If the PHP backend cannot connect, **auth** and **self-service** API routes are not registered (see `backend/public/index.php`).
---
## `PROXY`
Hotspot **UDP proxy** — full guide: [Hotspot proxy](hotspot-proxy.md). **Integrated** deployments use **`PROXY`** in **`adn-server.yaml`** (fan-in, no port range). **Standalone** layouts keep these keys in **`proxy/adn-proxy.yaml`** (or a legacy combined file via **`ADN_CONFIG_PATH`**).
**Standalone summary:** **`PORT`** + **`GENERATOR`** must match **`SYSTEM.PORT`** + **`SYSTEM.GENERATOR`** in `adn-server`; each client is forwarded to **`MASTER`** at one UDP port in **`PORT`…`PORT+GENERATOR-1`** (see also [Architecture](architecture.md)).
| Key | Meaning |
|-----|---------|
| **MASTER** | Peer server host (IP or DNS; resolved at proxy startup). |
| **LISTEN_PORT** / **LISTEN_IP** | Where the proxy accepts hotspot UDP (empty IP often means all interfaces). |
| **PORT** / **DESTPORT_START** | Base UDP port on **`MASTER`** (same as server SYSTEM **PORT**). |
| **GENERATOR** | Count of consecutive UDP ports on **`MASTER`** (same integer as server SYSTEM **GENERATOR**). |
| **TIMEOUT**, **STATS**, **DEBUG**, **CLIENT_INFO** | Behaviour and logging. |
| **BLACK_LIST** / **IP_BLACK_LIST** | Optional block lists. |
If MySQL is unavailable, **auth** and **self-service** API routes are not registered.
---
@ -73,7 +55,7 @@ Comma-separated **network IDs** (as strings). Traffic from those OpenBridge sour
## `ALIASES`
Similar idea to the peer server: download **peer / subscriber / TGID** JSON and optional checksums. Keys include **PATH**, **\*_FILE**, **\*_URL**, **STALE_HOURS**, **REVIEW_INTERVAL_MINUTES**, **CHECKSUM_***, **TG_LIST_URL**, **BRIDGE_LIST_URL** (backend proxy for frontend pages).
Similar idea to the peer server: download **peer / subscriber / TGID** JSON and optional checksums. Keys include **PATH**, **\*_FILE**, **\*_URL**, **STALE_HOURS**, **REVIEW_INTERVAL_MINUTES**, **CHECKSUM_***, **TG_LIST_URL**, **BRIDGE_LIST_URL** (API proxy for frontend pages).
---
@ -85,18 +67,24 @@ Similar idea to the peer server: download **peer / subscriber / TGID** JSON and
| **LOG_FILE** | Monitor log filename (e.g. `adn-monitor.log`). |
| **LOG_LEVEL** | e.g. `INFO`, `DEBUG`. |
The **hotspot proxy** log filename is set in **`proxy/adn-proxy.yaml`** under **LOGGER** as **`PROXY_LOG_FILE`** (see [Hotspot proxy](hotspot-proxy.md)).
---
## `WEBSOCKET_SERVER`
## `MONITOR_APP`
| Key | Meaning |
|-----|---------|
| **WEBSOCKET_PORT** | Port for Twisted WebSocket pushing JSON state to browsers. |
| **FREQUENCY** | Push interval (seconds). |
| **LISTEN_HOST** | Bind address (`""` = all IPv4). |
| **LISTEN_PORT** | HTTP port (e.g. `8080`): `/api/*` and `/ws`. |
| **INGEST** | `tcp` (client to `ADN_CONNECTION`) or `mqtt` (broker topics). |
| **MQTT** | Required when `INGEST: mqtt` (`URL`, `TOPIC_PREFIX`, `QOS`). |
| **FREQUENCY** | Background periodic resync (seconds); live updates are event-driven. |
| **CLIENT_TIMEOUT** | Drop idle WS clients after N seconds (`0` = disable). |
| **USE_SSL**, **SSL_PATH**, **SSL_CERTIFICATE**, **SSL_PRIVATEKEY** | Optional WSS. |
| **CORS_ORIGINS** | Allowed origins for dev (optional). |
In production, Nginx proxies `/api` and `/ws` to **LISTEN_PORT**. No separate WebSocket port is needed.
Obsolete **`WEBSOCKET_SERVER`** YAML (Twisted on a separate port) is ignored; use **`MONITOR_APP`**.
---
@ -107,7 +95,7 @@ The **hotspot proxy** log filename is set in **`proxy/adn-proxy.yaml`** under **
| **DASHTITLE** | Header title. |
| **BACKGROUND** | Use `bk.jpg` background if `true`. |
| **LANGUAGE** | Default UI language (`en`, `es`, …). |
| **SELF_SERVICE** | If `true`, the UI can show the **Self-service** nav entry (backend must expose API + DB). |
| **SELF_SERVICE** | If `true`, the UI can show the **Self-service** nav entry (monitor API + MySQL required). |
| **SHOW_CONSOLE** | Show console page (call start/end messages). |
| **MIN_DURATION** | Minimum call duration (seconds) for **dashboard** Last Heard table (Last Heard page may still show shorter). |
| **nav_links**, **footer**, **news** | Optional structured links / marquee items. |
@ -116,9 +104,8 @@ The **hotspot proxy** log filename is set in **`proxy/adn-proxy.yaml`** under **
## Environment
- **`ADN_CONFIG_PATH`**: Absolute path to **`adn-monitor.yaml`** for the **monitor** and **PHP backend**.
- **`ADN_PROXY_CONFIG_PATH`** (optional): Absolute path to **`adn-proxy.yaml`** for the hotspot proxy. If unset, the proxy falls back to **`ADN_CONFIG_PATH`** (legacy combined file), then to **`proxy/adn-proxy.yaml`** by default — details in [Hotspot proxy](hotspot-proxy.md#configuration-file).
- Backend may use **`API_BASE_PATH`** if the API is mounted under a prefix.
- **`ADN_CONFIG_PATH`**: Absolute path to **`adn-monitor.yaml`** for **`monitor.py`**.
- Project root **`.env`**: `VITE_API_BASE`, `VITE_DEFAULT_LANGUAGE` (frontend build); auto-loaded by `monitor.py` and `db_bootstrap.py`.
---

@ -1,138 +1,8 @@
# Hotspot proxy
# Hotspot proxy (moved)
## Integrated proxy (current default)
The **standalone** `adn-proxy` process and **`proxy/`** tree were **removed** from the **adn-monitor** repository.
**ADN DMR Peer Server** ships an **integrated hotspot proxy** in **`adn-server.py`**. Configure **`PROXY`** and **`SELF_SERVICE`** in **`adn-server.yaml`** — see [Hotspot proxy (integrated)](../server/user-guide/hotspot-proxy.md).
Use the **integrated hotspot proxy** in **adn-server**:
- Hotspots connect to **`PROXY.LISTEN_PORT`** only (fan-in).
- Traffic injects into **`PROXY.TARGET_SYSTEM`** (inject-only MASTER, **`MAX_PEERS`**).
- MySQL self-service uses the same **`Clients`** table as this monitor stack.
- **Disable** standalone **`adn-proxy`** on the same host to avoid **`LISTEN_PORT`** conflicts.
---
## Standalone proxy (legacy, adn-monitor repo)
The **adn-monitor** repository still contains a **standalone UDP relay** (`proxy/proxy.py`). It maps each hotspot to a **dedicated destination port** on the peer server host (port **range** + **`GENERATOR`**). Use this layout only when you deliberately keep proxy separate from **`adn-server`**.
Source layout: `proxy/proxy.py`, package `proxy/src/adn_proxy/` (clean architecture). **GPL v3** (derivative of Simon Adlem, G7RZU’s original proxy).
### Why it also ships with the monitor
- **Same deployment** as the dashboard stack: **`adn-monitor.yaml`**, **`adn-proxy.yaml`**, **PHP**, **MySQL**.
- Historical split: peer server = radio core; proxy = optional UDP front on a port **range**.
- New ADN deployments should prefer the **integrated** proxy unless you maintain an existing **`adn-proxy`** unit.
---
## Configuration file {#configuration-file}
The **standalone** proxy does **not** use `adn-server.yaml` for its own process. It reads YAML that contains **`PROXY`**, **`SELF_SERVICE`**, and **`LOGGER`** (proxy log). The **integrated** proxy reads those blocks from **`adn-server.yaml`** instead.
### Resolution order
| Priority | Source | Purpose |
|----------|--------|---------|
| 1 | **`python proxy/proxy.py --config /path/to/file.yaml`** | Overrides config path for this process only. |
| 2 | **`ADN_PROXY_CONFIG_PATH`** | Optional env: absolute path to **`adn-proxy.yaml`** (typical dedicated proxy config). |
| 3 | **`ADN_CONFIG_PATH`** | Legacy: absolute path to a **combined** file (e.g. **`adn-monitor.yaml`** with **PROXY** embedded — same as monitor/backend). |
| 4 | **Default** | **`proxy/adn-proxy.yaml`** next to `proxy/proxy.py` when neither env var is set. |
Copy **`proxy/adn-proxy.example.yaml`** to **`proxy/adn-proxy.yaml`** and edit. **`SELF_SERVICE`** must match **`monitor/adn-monitor.yaml`** (same DB credentials and PBKDF2 parameters).
Sections read from whichever file is chosen:
- **`PROXY`** — listen address, master host, destination port **range**, timeouts, debug, block lists.
- **`SELF_SERVICE`** — MySQL and **`USE_SELFSERVICE`** (for **`Clients`** table, RPTO / options).
- **`LOGGER`** — **`LOG_PATH`** and **`PROXY_LOG_FILE`** (separate from **`LOG_FILE`** in `adn-monitor.yaml` for `monitor.py`).
Optional **environment** overrides (see `proxy/README.md` in the repo): e.g. **`ADN_PROXY_DEBUG`**, **`ADN_PROXY_LISTENPORT`**.
---
## `PROXY` keys (in `adn-proxy.yaml`, or legacy combined YAML)
| Key | Role |
|-----|------|
| **MASTER** | IP or **hostname** of the **ADN DMR Peer Server** host. Resolved to an IPv4 address at startup (Twisted requires an IP for `write()`). |
| **LISTEN_PORT** | UDP port where **hotspots** connect **to the proxy** (the address users configure on the hotspot). |
| **LISTEN_IP** | Empty often means all interfaces; otherwise bind to this address. |
| **PORT** / **DESTPORT_START** | Base UDP port on **`MASTER`** (alias **DESTPORT_START**); must match **`SYSTEM.PORT`** in `adn-server`. |
| **GENERATOR** | Same integer as **`SYSTEM.GENERATOR`**; UDP ports **`PORT`…`PORT+GENERATOR-1`** on **`MASTER`** (one per proxied hotspot session). |
| **TIMEOUT** | Idle / session timeout (seconds). |
| **STATS** | Extra statistics logging. |
| **DEBUG** | Verbose packet logging (or use **`ADN_PROXY_DEBUG=1`**). |
| **CLIENT_INFO** | Per-client info in logs. |
| **BLACK_LIST** / **IP_BLACK_LIST** | Block radio IDs or source IPs. |
Internal config keys (after load) use mixed-case names (`Master`, `ListenPort`, …) — see `adn_proxy.infrastructure.config_loader`.
---
## Peer server (`adn-server.yaml`) must cover the port range
The proxy forwards traffic to **`MASTER:assigned_port`** for each client, where **assigned_port** is picked from **`PORT`…`PORT+GENERATOR-1`** (same **PORT**/**GENERATOR** semantics as [Server configuration](../server/user-guide/configuration.md)).
The **ADN DMR Peer Server** must **listen on UDP** on **that host** for **every port** in that range (usually via **`GENERATOR`** on one SYSTEM block).
- Align **`PROXY.PORT`** and **`PROXY.GENERATOR`** with **`SYSTEM.PORT`** and **`SYSTEM.GENERATOR`** in **`adn-server.yaml`**.
- Typical setup: one **`MODE: MASTER`** entry with **`GENERATOR`** expanding to `SYSTEM-0`…`SYSTEM-(N-1)` on consecutive UDP ports — see [Server configuration](../server/user-guide/configuration.md).
If the server only listens on e.g. **56400** but the proxy sends to **56401**, that client will not register.
---
## How the process starts
1. Resolve config path (`--config`, **`ADN_PROXY_CONFIG_PATH`**, **`ADN_CONFIG_PATH`**, or default **`proxy/adn-proxy.yaml`**).
2. **`load_config()`** parses YAML → **`PROXY`**, **`SELF_SERVICE`**, **`LOG`**.
3. Optional **MySQL** pool if self-service / DB features are enabled.
4. Twisted **reactor** runs UDP **ProxyProtocol** on **`LISTEN_IP:LISTEN_PORT`**, forwarding to **`MASTER:assigned_dest_port`**.
Run (from adn-monitor root):
```bash
# Dedicated proxy YAML (recommended)
export ADN_PROXY_CONFIG_PATH=/opt/adn-monitor/proxy/adn-proxy.yaml
python proxy/proxy.py
# Or rely on default proxy/adn-proxy.yaml after copying from adn-proxy.example.yaml
python proxy/proxy.py
# Legacy: single combined monitor YAML
export ADN_CONFIG_PATH=/opt/adn-monitor/monitor/adn-monitor.yaml
python proxy/proxy.py
# Or explicit path for one run
python proxy/proxy.py --config /opt/adn-monitor/proxy/adn-proxy.yaml
```
Use **systemd** or another supervisor to run alongside **`monitor.py`** and the **PHP** stack.
---
## RPTO, options, and self-service
The proxy **never** sends **RPTO** directly to the hotspot for self-service updates. It sends **RPTO to the MASTER** (peer server); the server updates bridge/options and the normal HBP path applies.
| Event | Proxy behaviour |
|-------|------------------|
| ~**10 s** after hotspot login (**RPTC**) | Read **`Clients.options`** from DB → **RPTO** → master `(MASTER, dport)`. |
| Every ~**10 s** | Rows with **`modified = 1`** → **RPTO** → master, then clear **`modified`**. |
| Hotspot sends **RPTO** | Forward to master; DB updates as implemented. |
Details: [Self-service](self-service.md) and the **adn-monitor** `proxy/README.md`.
---
## Monitor visibility
Hotspots appear on the dashboard only if the **peer server** sends **TCP reports** to the same host/port as **`ADN_CONNECTION`** in **`adn-monitor.yaml`**. Align **`REPORTS`** on the server with **`ADN_IP` / `ADN_PORT`**. See [Monitoring and reports](../server/user-guide/monitoring.md).
---
## See also
- [Monitor configuration](configuration.md) — **`adn-monitor.yaml`** (dashboard, reports, MySQL for backend/monitor); **`PROXY`** detail above.
- [Architecture](architecture.md) — where the proxy sits in the stack.
- [Self-service](self-service.md) — DB, **`modified`**, RPTO timing.
- [Hotspot proxy (integrated)](../server/user-guide/hotspot-proxy.md) — `PROXY` and `SELF_SERVICE` in **`adn-server.yaml`**
- [Monitor overview](index.md) — dashboard stack (`monitor.py` + React)

@ -1,6 +1,6 @@
# ADN Monitor (overview)
**ADN Monitor** is a separate project from the **ADN DMR Peer Server**, but the two are normally deployed **together**: the server sends **TCP reports** (config, bridges, call events) to the monitor; the monitor drives the **web dashboard** (React) and **WebSocket** live updates. Optional components include the **PHP API** (Slim), **MySQL** (self-service / device registry), and the **hotspot proxy** (UDP between hotspots and the peer server — **integrated in `adn-server.py`** by default; standalone `adn-proxy` remains for legacy layouts).
**ADN Monitor** is a separate project from the **ADN DMR Peer Server**, but the two are normally deployed **together**: the server sends **TCP reports** (or MQTT) to the monitor; the monitor drives the **web dashboard** (React) and **WebSocket** live updates. A single **`monitor.py`** process (FastAPI) serves **REST** (`/api/*`), **WebSocket** (`/ws`), and **report ingest**. Optional: **MySQL** (self-service / Last Heard) and **integrated hotspot proxy** in **`adn-server.py`**.
This chapter documents the **adn-monitor** stack at the same level of detail as the server guides. Source code lives in the **adn-monitor** repository, not in the **adn-server** repository (where this documentation is maintained).
@ -8,20 +8,17 @@ This chapter documents the **adn-monitor** stack at the same level of detail as
| Part | Role |
|------|------|
| **`monitor/monitor.py`** | Python (Twisted): connects to the peer server’s **report TCP** port, decodes netstring payloads (`CONFIG_SND`, `BRIDGE_SND`, `BRDG_EVENT`), maintains **CTABLE** / **BTABLE**, writes **Last Heard** / TG stats to **MySQL** when configured, serves **WebSocket** JSON to the dashboard. |
| **`backend/`** | PHP **Slim** app: `/api/config/dashboard`, auth, optional **self-service** APIs, alias proxies. Reads **`adn-monitor.yaml`** via **`ADN_CONFIG_PATH`**. |
| **`frontend/`** | React (Vite): dashboard UI; consumes backend API + WebSocket. |
| **`proxy/`** | Python (Twisted): **standalone** UDP hotspot proxy (legacy); forwards Homebrew between hotspots and the peer server port range; reads **`Clients`** in MySQL for **RPTO**. Prefer integrated **`PROXY`** in **`adn-server.yaml`** — see [Hotspot proxy](hotspot-proxy.md). |
| **`monitor/monitor.py`** | FastAPI: REST (`/api/*`), WebSocket (`/ws`), TCP or MQTT report ingest, **CTABLE** / Last Heard, self-service MySQL. |
| **`frontend/`** | React (Vite): dashboard UI; same-origin `/api` + `/ws`. |
## Configuration files
| File | Used by | Typical env |
|------|---------|-------------|
| **`adn-server.yaml`** | **`adn-server.py`** (integrated **`PROXY`** / **`SELF_SERVICE`**) | `-c` / default path next to binary |
| **`monitor/adn-monitor.yaml`** | **`monitor.py`**, **PHP backend** | **`ADN_CONFIG_PATH`** |
| **`proxy/adn-proxy.yaml`** | **`proxy/proxy.py`** (legacy standalone) | **`ADN_PROXY_CONFIG_PATH`** (optional; see [Hotspot proxy](hotspot-proxy.md#configuration-file)) |
| **`monitor/adn-monitor.yaml`** | **`monitor.py`** | **`ADN_CONFIG_PATH`** |
**`SELF_SERVICE`** (MySQL / PBKDF2) must **match** between **`adn-server.yaml`** (integrated proxy), **`adn-monitor.yaml`**, and legacy **`adn-proxy.yaml`** when used. **`ADN_CONNECTION`**, dashboard, WebSocket, and aliases live in **`adn-monitor.yaml`**; integrated **`PROXY`** / **`SELF_SERVICE`** live in **`adn-server.yaml`**; standalone proxy settings remain in **`adn-proxy.yaml`**.
**`SELF_SERVICE`** (MySQL / PBKDF2) must **match** between **`adn-server.yaml`** and **`adn-monitor.yaml`**. **`ADN_CONNECTION`**, dashboard, WebSocket, and aliases live in **`adn-monitor.yaml`**; integrated **`PROXY`** lives in **`adn-server.yaml`** — see [Hotspot proxy (integrated)](../server/user-guide/hotspot-proxy.md).
## Link to the peer server
@ -34,7 +31,7 @@ See [Monitoring and reports](../server/user-guide/monitoring.md) for report opco
## See also
- [Hotspot proxy](hotspot-proxy.md) — `PROXY`, peer server port range, how the process loads config and runs
- [Hotspot proxy (integrated)](../server/user-guide/hotspot-proxy.md) — `PROXY` in `adn-server.yaml`
- [Architecture and deployment](architecture.md)
- [Configuration (`adn-monitor.yaml`)](configuration.md)
- [Self-service](self-service.md)

@ -2,14 +2,14 @@
**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), **PHP API** (session + REST), **React** (`/self-service` page), and the **hotspot proxy** (periodic **RPTO** to the master). The **peer server** is the component that ultimately applies **RPTO** / **OPTIONS** to the running bridge and hotspot behaviour.
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 the backend exposes `/api/self-service/*` when DB connects).
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).
@ -19,7 +19,7 @@ It involves **four** pieces: **MySQL** (`Clients` table), **PHP API** (session +
| Endpoint | Purpose |
|----------|---------|
| **`POST /api/auth/login`** | Body: `callsign`, `password`. Verifies **PBKDF2** hash against **`Clients.psswd`** for rows with **`logged_in = 1`**. On success: PHP session with **`user_id`**, **`int_ids`** (all DMR IDs for that callsign). |
| **`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. |
@ -41,7 +41,7 @@ Session lifetime is extended on activity (**SelfServiceController** uses a long
## End-to-end flow (why options reach the hotspot)
1. User saves options in the web UI → **PHP** writes **`Clients.options`** and **`modified = 1`**.
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).

@ -6,7 +6,7 @@
Replace monitor snapshots that today use **pickle** (`CONFIG_SND`, `BRIDGE_SND`) and **CSV strings** (`BRDG_EVENT`) with **typed JSON** that any client can decode.
**Release policy:** **adn-server 1.0.x** + **adn-monitor 1.0.x** = report v1 (frozen tag pair). **2.x** server emits **report v2 only** (no pickle shim, no `dual`); **adn-monitor 2.x** is required on the same release line.
**Release policy:** **adn-server 1.0.x** + **adn-monitor 1.0.x** = report v1 (frozen tag pair). **2.x** server sends **HELLO** with `report_protocol: 2` and **v2 JSON only** (`TOPOLOGY_SND`, `ROUTING_TABLE_SND`, `VOICE_EVENT_SND`, `DELTA_SND`) — no duplicate pickle/CSV frames. **adn-monitor 2.x** is the polyglot consumer: it decodes v1 wire from legacy peers and v2 JSON from **adn-server 2.x**, mapping both into the same dashboard state for the UI. **adn-monitor 1.0.0** cannot decode v2 opcodes — upgrade the monitor line for **adn-server 2.x**.
## Transport (unchanged)

@ -10,8 +10,7 @@ Configuration lives in **`adn-server.yaml`** under **`PROXY`** and optional **`S
| Deployment | What to run |
|------------|-------------|
| **Typical ADN stack** (monitor + dashboard + many Pi-Star hotspots) | **`adn-server.py`** with **`PROXY`** + **`SELF_SERVICE`** — disable the standalone **`adn-proxy`** unit to avoid port clashes on **`PROXY.LISTEN_PORT`**. |
| **Legacy / split config** | Standalone **`proxy/proxy.py`** in the **adn-monitor** repo — see [Hotspot proxy (standalone)](../../monitor/hotspot-proxy.md). |
| **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).
@ -68,7 +67,7 @@ Set **`MAX_PEERS`** on the target MASTER to the maximum concurrent proxied hotsp
## `SELF_SERVICE` keys
Same semantics as **`adn-monitor.yaml`** / legacy **`adn-proxy.yaml`** — shared **`Clients`** table, **`modified`** flag, **RPTO** toward the MASTER.
Same semantics as **`adn-monitor.yaml`** — shared **`Clients`** table, **`modified`** flag, **RPTO** toward the MASTER.
| Key | Role |
|-----|------|
@ -106,15 +105,8 @@ See [Configuration — hot reload](configuration.md#hot-reload-adn-serveryaml).
---
## Standalone proxy (legacy)
The **adn-monitor** repository still ships **`proxy/proxy.py`** for deployments that keep a **separate** UDP relay and **`adn-proxy.yaml`**. Do **not** run both the integrated proxy and standalone **`adn-proxy`** on the same **`LISTEN_PORT`**.
---
## See also
- [Configuration](configuration.md) — full **`adn-server.yaml`** reference.
- [Monitoring and reports](monitoring.md) — TCP reports, dashboard, log rotation.
- [Self-service](../../monitor/self-service.md) — **`Clients`**, **RPTO** timing.
- [Hotspot proxy (standalone)](../../monitor/hotspot-proxy.md) — legacy **`adn-proxy`** layout.

@ -27,7 +27,7 @@ Routing, timers, OpenBridge loop control, and protocol handling are implemented
## Related programs
- **Parrot / playback** — separate entrypoint (`adn-parrot.py`) for record-and-playback; see [Parrot](parrot.md).
- **Standalone hotspot proxy** — legacy `adn-proxy` in the **adn-monitor** repo when not using the integrated proxy; see [Hotspot proxy (standalone)](../../monitor/hotspot-proxy.md).
- **Integrated hotspot proxy** — `PROXY` in **`adn-server.yaml`**; see [Hotspot proxy](hotspot-proxy.md).
## Next steps

@ -16,7 +16,7 @@ Older stacks (**legacy** `adn-dmr-server`-style) may **omit** HELLO. **adn-monit
The **monitor** decodes these messages, updates its **CTABLE** / **BTABLE**, and (when MySQL is configured) persists Last Heard / statistics.
**Full stack:** [ADN Monitor overview](../../monitor/index.md) (Python monitor, WebSocket, PHP API, optional proxy and self-service).
**Full stack:** [ADN Monitor overview](../../monitor/index.md) (FastAPI monitor, WebSocket, self-service).
### Report channel log lines (`adn-monitor` logger)
@ -51,7 +51,6 @@ These processes handle **`SIGUSR2`** by reopening **`logging.FileHandler`** stre
| Process | Typical config keys |
|---------|---------------------|
| **`adn-server`** / **`adn-parrot`** | **`LOGGER.LOG_FILE`** (integrated proxy logs appear in the same file) |
| **`adn-proxy`** (standalone, legacy) | **`LOG.PATH`** + **`LOG.LOG_FILE`** in `adn-proxy.yaml` — omit if using integrated **`PROXY`** in `adn-server.yaml` |
| **`adn-monitor`** | **`LOG.PATH`** + **`LOG.LOG_FILE`** in `adn-monitor.yaml` |
Example **`/etc/logrotate.d/adn`** fragment (adjust paths and service names):
@ -71,7 +70,7 @@ Example **`/etc/logrotate.d/adn`** fragment (adjust paths and service names):
}
```
Repeat **`postrotate`** with **`kill -USR2`** for **`adn-parrot`** and **`adn-monitor`** units if those logs are rotated on the same host. Add **`adn-proxy`** only when you still run the **standalone** proxy (not needed when proxy is integrated into **`adn-server`**). Use the correct **PID** (systemd **`MainPID`**, a pidfile, or **`kill`** targeting the process you manage).
Repeat **`postrotate`** with **`kill -USR2`** for **`adn-parrot`** and **`adn-monitor`** units if those logs are rotated on the same host. Use the correct **PID** (systemd **`MainPID`**, a pidfile, or **`kill`** targeting the process you manage).
## Requirements
@ -82,4 +81,4 @@ Repeat **`postrotate`** with **`kill -USR2`** for **`adn-parrot`** and **`adn-mo
Operators editing **device options** from the dashboard use the **self-service** flow (MySQL **`Clients`**, **RPTO** toward the conference MASTER). In current **ADN DMR Peer Server** deployments this runs **inside `adn-server.py`**: configure **`SELF_SERVICE`** and **`PROXY`** in **`adn-server.yaml`** (see [Hotspot proxy](hotspot-proxy.md)). Dashboard semantics: [Self-service](../../monitor/self-service.md).
Legacy stacks may still use a **standalone** **`adn-proxy`** process and **`adn-proxy.yaml`** — see [Hotspot proxy (standalone)](../../monitor/hotspot-proxy.md). Do not run both on the same **`LISTEN_PORT`**.
Hotspot proxy logs are part of **`adn-server`** when **`PROXY`** is enabled — see [Hotspot proxy (integrated)](hotspot-proxy.md).

@ -42,12 +42,13 @@ Más: [Introducción](server/user-guide/introduction.md).
## ADN Monitor
Panel, **WebSocket** en vivo, **API PHP** opcional, **MySQL** self-service y **proxy hotspot** — ver [Descripción general del monitor](monitor/index.md).
Panel, **WebSocket** en vivo, API FastAPI, **MySQL** self-service — ver [Descripción general del monitor](monitor/index.md). El **proxy hotspot** está integrado en **adn-server**.
| Quiero… | Empieza aquí |
|---------|----------------|
| `adn-server.yaml` — `PROXY` / `SELF_SERVICE` integrados | [Proxy hotspot (integrado)](server/user-guide/hotspot-proxy.md) |
| `adn-monitor.yaml`, `adn-proxy.yaml` legado, despliegue | [Configuración del monitor](monitor/configuration.md), [Proxy hotspot](monitor/hotspot-proxy.md) |
| `adn-monitor.yaml`, despliegue | [Configuración del monitor](monitor/configuration.md) |
| Proxy hotspot integrado | [Proxy hotspot](server/user-guide/hotspot-proxy.md) |
| Proxy hotspot (UDP, `PROXY`, rango de puertos) | [Proxy hotspot](monitor/hotspot-proxy.md) |
| Self-service | [Self-service](monitor/self-service.md) |
| Cómo encaja con el servidor | [Monitor e informes](server/user-guide/monitoring.md) |

@ -4,59 +4,50 @@
Bajo `monitor/src/adn_monitor/`:
- **Dominio** — objetos de valor, errores, tipos de opcode.
- **Aplicación** — `MonitorState`, `process_message` en `monitor_controller.py`, servicio de alias, casos de uso Last Heard / conteo TG, formato de hora.
- **Infraestructura** — `load_config` YAML, cliente Twisted **TCP** (`ReportClientFactory`) al peer server, fábrica **WebSocket** para el panel, repositorios MySQL, decodificadores pickle/json para `CONFIG_SND` / `BRIDGE_SND`.
- **Dominio** — entidades, errores, `Result`, opcodes, `UserSession`.
- **Aplicación** — `MonitorState`, casos de uso (auth, self-service, informes, dashboard), puertos (`AuthRepository`, `HttpFetcherPort`, …).
- **Infraestructura** — YAML, FastAPI (REST + `/ws`), ingest TCP (Twisted en hilo) o MQTT, MySQL, decodificadores de informes.
El monitor **sale hacia** **`ADN_CONNECTION.ADN_IP:ADN_PORT`** y recibe mensajes con prefijo de longitud (estilo netstring). Actualiza **CTABLE** (masters/peers/OpenBridge) y **BTABLE** (bridges) en memoria, y persiste resultados de **BRDG_EVENT** cuando MySQL está configurado.
El **composition root** está en `infrastructure/fastapi/composition.py` (`build_monitor_api`).
## Protocolo de informes (desde el peer server)
Los mismos opcodes que en la documentación del servidor: **CONFIG_SND**, **BRIDGE_SND**, **BRDG_EVENT**, etc. El monitor los decodifica y aplica en `process_message` — ver [Monitor e informes](../server/user-guide/monitoring.md).
## Proceso unificado (`monitor.py`)
## WebSocket
Un solo proceso uvicorn/FastAPI:
`monitor.py` ejecuta un **WebSocket** Twisted en **`WEBSOCKET_SERVER.WEBSOCKET_PORT`**, enviando instantáneas JSON a **`FREQUENCY`** para que la app React actualice sin polling del estado principal.
| Ruta / rol | Contenido |
|------------|-----------|
| `/api/*` | Config dashboard, auth, self-service, proxy alias/status |
| `/ws` | Protocolo `conf,<grupo>` — CTABLE, Last Heard, voz |
| Ingest (fondo) | `MONITOR_APP.INGEST`: `tcp` (cliente a `ADN_CONNECTION`) **o** `mqtt` (tópicos `state` + `voice_event`) |
| Alias (fondo) | Descarga → `FILES.PATH`; import con **staging + RENAME** (commits cada 10k en staging; swap breve); merge con commits cada 2k; **PK en `id`** |
## Backend PHP
## Protocolo de informes (desde el peer server)
- **Slim 4** front controller: `backend/public/index.php`.
- Carga **`adn-monitor.yaml`** vía **`ADN_CONFIG_PATH`** (igual que el monitor).
- **`/api/config/dashboard`** — título, idioma, flags (`selfService`, `showConsole`, …) desde **`DASHBOARD`**.
- **`/api/auth/*`** — sesión por cookie cuando hay BD **SELF_SERVICE**.
- **`/api/self-service/*`** — opciones de dispositivo (ver [Self-service](self-service.md)).
- **`/api/aliases/*`** — proxy opcional a URLs de listas TG/bridge desde **ALIASES**.
Modos de ingest según el peer: legacy (pickle), v1 HELLO, v2 slim (`dashboard_state` + `voice_event`). Ver [Monitor e informes](../server/user-guide/monitoring.md).
## Frontend
- **Vite + React** bajo `frontend/`; el build genera estáticos servidos por nginx/Apache o similar.
- Usa **`API_BASE`** (build) para la API PHP y **URL del WebSocket** para datos en vivo.
- **Vite + React** en `frontend/`; build → `frontend/dist/`.
- Desarrollo: Vite hace proxy de `/api` y `/ws` a `MONITOR_APP.LISTEN_PORT` (p. ej. 8080).
- Producción: Nginx sirve estáticos y proxifica `/api` + `/ws` al mismo puerto FastAPI.
## Proxy hotspot
**Integrado (predeterminado):** **`adn-server.py`** ejecuta fan-in UDP desde **`PROXY.LISTEN_PORT`** hacia **`PROXY.TARGET_SYSTEM`**; **`SELF_SERVICE`** en **`adn-server.yaml`** impulsa **RPTO** desde MySQL **`Clients`**. Ver [Proxy hotspot (integrado)](../server/user-guide/hotspot-proxy.md).
**Independiente (legado, repo adn-monitor):**
- Entrada: `proxy/proxy.py`; paquete `src/adn_proxy/` (dominio / aplicación / infraestructura).
- Lee **`PROXY`** y **`SELF_SERVICE`** desde **`adn-proxy.yaml`** por defecto (o YAML combinado del monitor vía **`ADN_CONFIG_PATH`** — ver [Proxy hotspot](hotspot-proxy.md#configuration-file)).
- Por cada cliente hotspot, asigna un puerto UDP en **`PORT`…`PORT+GENERATOR-1`** y reenvía a **`MASTER`**.
- Cuando **self-service** actualiza **`Clients.options`** y pone **`modified=1`**, el proxy envía **RPTO** al **master** en un temporizador (~10 s).
**Integrado (predeterminado):** `adn-server.py` con `PROXY` + `SELF_SERVICE` en `adn-server.yaml`. Ver [Proxy hotspot integrado](../server/user-guide/hotspot-proxy.md).
**Detalle:** [Proxy hotspot](hotspot-proxy.md) (integrado vs independiente, claves, arranque).
El proceso **`adn-proxy`** independiente se eliminó del repositorio **adn-monitor**; usa solo **`PROXY`** integrado.
## Topología típica de despliegue
## Topología típica
```text
[Hotspots] --UDP--> [Proxy :LISTEN_PORT] --UDP--> [Peer server :PORT..PORT+GENERATOR-1]
[Hotspots] --UDP--> [adn-server PROXY] --UDP--> [peer MASTER]
|
v
MySQL (Clients)
[Peer server :REPORT_PORT] <--- TCP --- [monitor.py : cliente que conecta]
[Peer :REPORT_PORT] <--- TCP o MQTT --- [monitor.py ingest]
[Navegador] --HTTPS--> [API PHP + frontend estático]
[Navegador] --WS----> [WebSocket del monitor :9000]
[Navegador] --HTTPS--> [Nginx: frontend/dist + proxy /api,/ws --> :8080]
```
---

@ -1,8 +1,8 @@
# Configuración (`adn-monitor.yaml`)
Este documento describe **`adn-monitor.yaml`**, usado por el **monitor Python** (`monitor/monitor.py`) y el **backend PHP** (`backend/public/index.php`). La ruta por defecto suele ser **`monitor/adn-monitor.yaml`** (sobrescribible con **`ADN_CONFIG_PATH`**).
Este documento describe **`adn-monitor.yaml`**, usado por **`monitor.py`**. La ruta por defecto suele ser **`monitor/adn-monitor.yaml`** (sobrescribible con **`ADN_CONFIG_PATH`**).
**Proxy hotspot integrado:** **`PROXY`** y **`SELF_SERVICE`** en **`adn-server.yaml`** (ver [Proxy hotspot (integrado)](../server/user-guide/hotspot-proxy.md)). **Independiente (legado):** **`proxy/adn-proxy.yaml`** — ver [Proxy hotspot](hotspot-proxy.md). **`SELF_SERVICE`** (MySQL / PBKDF2) debe ser **idéntica** entre **`adn-server.yaml`**, **`adn-monitor.yaml`** y **`adn-proxy.yaml`** legado si se usa.
**Proxy hotspot** se configura en **`adn-server.yaml`** (`PROXY` + `SELF_SERVICE`) — ver [Proxy hotspot integrado](../server/user-guide/hotspot-proxy.md). **`SELF_SERVICE`** (MySQL / PBKDF2) debe ser **idéntica** entre **`adn-server.yaml`** y **`adn-monitor.yaml`**.
El ejemplo del repositorio **adn-monitor** (`monitor/adn-monitor.yaml.example`) es la plantilla del monitor; las claves siguientes coinciden con ese fichero y `monitor/src/adn_monitor/infrastructure/config_loader.py` (los nombres internos pueden diferir).
@ -19,7 +19,7 @@ El ejemplo del repositorio **adn-monitor** (`monitor/adn-monitor.yaml.example`)
| **EMPTY_MASTERS** | Mostrar masters sin peers. |
| **TGCOUNT_INC** | Activar página / estadísticas de conteo de TG. |
| **TGCOUNT_ROWS** | Filas para el conteo de TG. |
| **TIMEZONE** | Zona horaria IANA (p. ej. `America/Santiago`) para mostrar; vacío usa la hora local del servidor. |
| **TIMEZONE** | Zona horaria IANA (p. ej. `America/Santiago`): fechas Last Heard en pantalla; **TG Count** usa el **día calendario en esa zona** (medianoche local = nuevo día de estadísticas). Vacío → día UTC. |
---
@ -37,31 +37,13 @@ Debe coincidir con la configuración de informes del **ADN DMR Peer Server**.
## `SELF_SERVICE`
Credenciales MySQL y parámetros PBKDF2 para **login** y acceso a la tabla **`Clients`**. **PBKDF2_SALT** y **PBKDF2_ITERATIONS** deben coincidir con **`hotspot_proxy_self_service.py`** (o tu herramienta de registro de contraseñas) para que los hashes verifiquen en PHP y Python.
Credenciales MySQL y parámetros PBKDF2 para **login**, **self-service** y acceso a la tabla **`Clients`**. **PBKDF2_SALT** y **PBKDF2_ITERATIONS** deben coincidir con **`hotspot_proxy_self_service.py`** (o tu herramienta de registro de contraseñas) para que los hashes verifiquen en la API del monitor y en el proxy integrado de **adn-server**.
| Clave | Significado |
|-------|-------------|
| **USE_SELFSERVICE** | Lo usa el cargador de config del **proxy** para rutas con BD / self-service (ver README del proxy). |
| **DB_SERVER**, **DB_USERNAME**, **DB_PASSWORD**, **DB_NAME**, **DB_PORT** | Conexión MySQL para **`Clients`** (y tablas relacionadas). |
Si el backend PHP no puede conectar, las rutas de **auth** y **self-service** no se registran (ver `backend/public/index.php`).
---
## `PROXY`
**Proxy UDP hotspot** — guía completa: [Proxy hotspot](hotspot-proxy.md). Los despliegues **integrados** usan **`PROXY`** en **`adn-server.yaml`** (fan-in, sin rango de puertos). Los layouts **independientes** mantienen estas claves en **`proxy/adn-proxy.yaml`** (o fichero combinado legado vía **`ADN_CONFIG_PATH`**).
**Resumen independiente:** **`PORT`** + **`GENERATOR`** deben coincidir con **`SYSTEM.PORT`** + **`SYSTEM.GENERATOR`** en `adn-server`; cada cliente se reenvía a **`MASTER`** en un puerto UDP dentro de **`PORT`…`PORT+GENERATOR-1`** (ver también [Arquitectura](architecture.md)).
| Clave | Significado |
|-------|-------------|
| **MASTER** | Host del peer server (IP o DNS; resuelto al arrancar el proxy). |
| **LISTEN_PORT** / **LISTEN_IP** | Dónde el proxy acepta UDP del hotspot (IP vacía suele significar todas las interfaces). |
| **PORT** / **DESTPORT_START** | Puerto UDP base en **`MASTER`** (el mismo que **PORT** del SYSTEM en el servidor). |
| **GENERATOR** | Cantidad de puertos UDP consecutivos en **`MASTER`** (el mismo entero que **GENERATOR** del SYSTEM en el servidor). |
| **TIMEOUT**, **STATS**, **DEBUG**, **CLIENT_INFO** | Comportamiento y registro. |
| **BLACK_LIST** / **IP_BLACK_LIST** | Listas de bloqueo opcionales. |
Si MySQL no está disponible, las rutas de **auth** y **self-service** no se registran.
---
@ -85,18 +67,25 @@ Misma idea que en el peer server: descargar JSON de **peer / subscriber / TGID**
| **LOG_FILE** | Nombre del log del monitor (p. ej. `adn-monitor.log`). |
| **LOG_LEVEL** | p. ej. `INFO`, `DEBUG`. |
El nombre del fichero de log del **proxy hotspot** se define en **`proxy/adn-proxy.yaml`** dentro de **LOGGER** como **`PROXY_LOG_FILE`** (ver [Proxy hotspot](hotspot-proxy.md)).
---
## `WEBSOCKET_SERVER`
## `MONITOR_APP`
Proceso unificado **`monitor.py`**: REST y WebSocket en el **mismo** host/puerto.
| Clave | Significado |
|-------|-------------|
| **WEBSOCKET_PORT** | Puerto del WebSocket Twisted que envía estado JSON a los navegadores. |
| **FREQUENCY** | Intervalo de envío (segundos). |
| **LISTEN_HOST** | Interfaz de escucha (`""` = todas las IPv4). |
| **LISTEN_PORT** | Puerto HTTP (p. ej. `8080`): `/api/*` y `/ws`. |
| **INGEST** | `tcp` (cliente a `ADN_CONNECTION`) o `mqtt` (tópicos del broker). |
| **MQTT** | Obligatorio si `INGEST: mqtt` (`URL`, `TOPIC_PREFIX`, `QOS`). |
| **FREQUENCY** | Resync periódico en segundo plano (segundos); las actualizaciones en vivo son por eventos. |
| **CLIENT_TIMEOUT** | Cerrar clientes WS inactivos tras N segundos (`0` = desactivado). |
| **USE_SSL**, **SSL_PATH**, **SSL_CERTIFICATE**, **SSL_PRIVATEKEY** | WSS opcional. |
| **CORS_ORIGINS** | Orígenes permitidos para desarrollo (opcional). |
Nginx en producción proxifica `/api` y `/ws` a **LISTEN_PORT**. No hace falta un puerto WebSocket aparte.
La sección obsoleta **`WEBSOCKET_SERVER`** en YAML (Twisted en puerto distinto) se ignora; usa **`MONITOR_APP`**.
---
@ -107,18 +96,38 @@ El nombre del fichero de log del **proxy hotspot** se define en **`proxy/adn-pro
| **DASHTITLE** | Título de cabecera. |
| **BACKGROUND** | Usar fondo `bk.jpg` si es `true`. |
| **LANGUAGE** | Idioma por defecto de la UI (`en`, `es`, …). |
| **SELF_SERVICE** | Si es `true`, la UI puede mostrar la entrada **Self-service** (el backend debe exponer API + BD). |
| **SELF_SERVICE** | Si es `true`, la UI puede mostrar la entrada **Self-service** (API del monitor + MySQL). |
| **SHOW_CONSOLE** | Mostrar página consola (mensajes inicio/fin de llamada). |
| **MIN_DURATION** | Duración mínima de llamada (segundos) para la tabla Last Heard del **panel** (la página Last Heard puede seguir mostrando más cortas). |
| **nav_links**, **footer**, **news** | Enlaces estructurados / marquee opcionales. |
---
## Esquema MySQL y migraciones
No hay Alembic: el monitor usa **`schema_migrations`** y comprobaciones en **`information_schema`**.
| Comando | Cuándo |
|---------|--------|
| `python db_bootstrap.py --config adn-monitor.yaml --create` | BD nueva |
| `python db_bootstrap.py --config adn-monitor.yaml --update` | BD existente (mismas operaciones, idempotente) |
**`monitor.py`** ejecuta **`ensure_schema`** al arrancar si hay **`SELF_SERVICE`**: `CREATE TABLE IF NOT EXISTS`, migraciones pendientes y limpieza de tablas staging huérfanas (`*_import`, `*_old`). **No borra datos** de `subscriber_ids` ni de `Clients`.
**Import masivo de alias (sin bloquear lecturas):**
- Tablas con **PK en `id`** únicamente (lookups puntuales).
- **Replace:** carga en `{tabla}_import` con **commit cada 10 000 filas** (la tabla live sigue legible); swap atómico `RENAME TABLE` (bloqueo metadata breve).
- **Merge** (ficheros locales): `INSERT IGNORE` con **commit cada 2 000 filas**.
Migraciones: `001_clients_callsign`, `002_clients_options_width`, `003_alias_pk_only`.
---
## Entorno
- **`ADN_CONFIG_PATH`**: ruta absoluta a **`adn-monitor.yaml`** para el **monitor** y el **backend PHP**.
- **`ADN_PROXY_CONFIG_PATH`** (opcional): ruta absoluta a **`adn-proxy.yaml`** para el proxy hotspot. Si no está definida, el proxy usa **`ADN_CONFIG_PATH`** (fichero combinado legado), luego **`proxy/adn-proxy.yaml`** por defecto — detalles en [Proxy hotspot](hotspot-proxy.md#configuration-file).
- El backend puede usar **`API_BASE_PATH`** si la API va bajo un prefijo.
- **`ADN_CONFIG_PATH`**: ruta absoluta a **`adn-monitor.yaml`** para **`monitor.py`**.
- **`.env`** en la raíz del repo: `VITE_API_BASE`, `VITE_DEFAULT_LANGUAGE` (build del frontend); se carga automáticamente en `monitor.py` y `db_bootstrap.py`.
---

@ -1,138 +1,8 @@
# Proxy hotspot
# Proxy hotspot (trasladado)
## Proxy integrado (predeterminado actual)
El proceso **`adn-proxy`** independiente y el árbol **`proxy/`** se **eliminaron** del repositorio **adn-monitor**.
**ADN DMR Peer Server** incluye un **proxy hotspot integrado** en **`adn-server.py`**. Configura **`PROXY`** y **`SELF_SERVICE`** en **`adn-server.yaml`** — ver [Proxy hotspot (integrado)](../server/user-guide/hotspot-proxy.md).
Usa el **proxy hotspot integrado** en **adn-server**:
- Los hotspots se conectan solo a **`PROXY.LISTEN_PORT`** (fan-in).
- El tráfico se inyecta en **`PROXY.TARGET_SYSTEM`** (MASTER solo inyección, **`MAX_PEERS`**).
- El self-service MySQL usa la misma tabla **`Clients`** que este stack del monitor.
- **Desactiva** **`adn-proxy`** independiente en el mismo host para evitar conflictos en **`LISTEN_PORT`**.
---
## Proxy independiente (legado, repo adn-monitor)
El repositorio **adn-monitor** sigue teniendo un **relé UDP independiente** (`proxy/proxy.py`). Asigna a cada hotspot un **puerto de destino dedicado** en el host del peer server (rango de puertos + **`GENERATOR`**). Usa este layout solo si mantienes el proxy separado de **`adn-server`** a propósito.
Estructura: `proxy/proxy.py`, paquete `proxy/src/adn_proxy/` (arquitectura limpia). **GPL v3** (derivado del proxy original de Simon Adlem, G7RZU).
### Por qué también va con el monitor {#why-it-ships-with-the-monitor-not-inside-the-peer-server}
- **Mismo despliegue** que el panel: **`adn-monitor.yaml`**, **`adn-proxy.yaml`**, **PHP**, **MySQL**.
- Separación histórica: peer server = núcleo radio; proxy = frontal UDP opcional en un **rango** de puertos.
- Los despliegues ADN nuevos deben preferir el proxy **integrado** salvo que mantengas una unidad **`adn-proxy`** existente.
---
## Fichero de configuración {#configuration-file}
El proxy **independiente** **no** usa `adn-server.yaml` para su propio proceso. Lee un YAML que incluye **`PROXY`**, **`SELF_SERVICE`** y **`LOGGER`** (log del proxy). El proxy **integrado** lee esos bloques desde **`adn-server.yaml`**.
### Orden de resolución
| Prioridad | Origen | Uso |
|-----------|--------|-----|
| 1 | **`python proxy/proxy.py --config /ruta/fichero.yaml`** | Sobrescribe la ruta solo para este proceso. |
| 2 | **`ADN_PROXY_CONFIG_PATH`** | Variable opcional: ruta absoluta a **`adn-proxy.yaml`** (config dedicada del proxy). |
| 3 | **`ADN_CONFIG_PATH`** | Legado: ruta a un **fichero combinado** (p. ej. **`adn-monitor.yaml`** con **PROXY** incrustado — igual que monitor/backend). |
| 4 | **Por defecto** | **`proxy/adn-proxy.yaml`** junto a `proxy/proxy.py` si no hay variables de entorno. |
Copia **`proxy/adn-proxy.example.yaml`** a **`proxy/adn-proxy.yaml`** y edita. **`SELF_SERVICE`** debe coincidir con **`monitor/adn-monitor.yaml`** (mismas credenciales MySQL y PBKDF2).
Secciones leídas del fichero elegido:
- **`PROXY`** — dirección de escucha, host master, **rango** de puertos de destino, timeouts, debug, listas negras.
- **`SELF_SERVICE`** — MySQL y **`USE_SELFSERVICE`** (tabla **`Clients`**, RPTO / opciones).
- **`LOGGER`** — **`LOG_PATH`** y **`PROXY_LOG_FILE`** (separado del **`LOG_FILE`** de `adn-monitor.yaml` para `monitor.py`).
**Entorno** opcional (ver `proxy/README.md` en el repo): p. ej. **`ADN_PROXY_DEBUG`**, **`ADN_PROXY_LISTENPORT`**.
---
## Claves `PROXY` (en `adn-proxy.yaml`, o YAML combinado legado)
| Clave | Rol |
|-------|-----|
| **MASTER** | IP o **nombre de host** del **ADN DMR Peer Server**. Se resuelve a IPv4 al arrancar (Twisted necesita IP para `write()`). |
| **LISTEN_PORT** | Puerto UDP donde los **hotspots** se conectan **al proxy** (lo que configuran en el hotspot). |
| **LISTEN_IP** | Vacío suele significar todas las interfaces; si no, enlazar a esa dirección. |
| **PORT** / **DESTPORT_START** | Puerto UDP base en **`MASTER`** (alias **DESTPORT_START**); debe coincidir con **`SYSTEM.PORT`** en `adn-server`. |
| **GENERATOR** | El mismo entero que **`SYSTEM.GENERATOR`**; puertos UDP **`PORT`…`PORT+GENERATOR-1`** en **`MASTER`** (uno por sesión de hotspot proxy). |
| **TIMEOUT** | Tiempo de inactividad / sesión (segundos). |
| **STATS** | Registro extra de estadísticas. |
| **DEBUG** | Log detallado de paquetes (o **`ADN_PROXY_DEBUG=1`**). |
| **CLIENT_INFO** | Información por cliente en logs. |
| **BLACK_LIST** / **IP_BLACK_LIST** | Bloquear IDs de radio o IPs de origen. |
Las claves internas tras la carga usan nombres mixtos (`Master`, `ListenPort`, …) — ver `adn_proxy.infrastructure.config_loader`.
---
## El peer server (`adn-server.yaml`) debe cubrir el rango de puertos
El proxy reenvía tráfico a **`MASTER:puerto_asignado`** por cliente; **puerto_asignado** se elige en **`PORT`…`PORT+GENERATOR-1`** (la misma semántica de **PORT**/**GENERATOR** que en [Configuración del servidor](../server/user-guide/configuration.md)).
El **ADN DMR Peer Server** debe **escuchar UDP** en **ese host** en **cada puerto** de ese rango (normalmente mediante **`GENERATOR`** en un bloque SYSTEM).
- Alinea **`PROXY.PORT`** y **`PROXY.GENERATOR`** con **`SYSTEM.PORT`** y **`SYSTEM.GENERATOR`** en **`adn-server.yaml`**.
- Configuración típica: un **`MODE: MASTER`** con **`GENERATOR`** que expande a `SYSTEM-0`…`SYSTEM-(N-1)` en puertos UDP consecutivos — ver [Configuración del servidor](../server/user-guide/configuration.md).
Si el servidor solo escucha p. ej. en **56400** pero el proxy envía a **56401**, ese cliente no se registrará.
---
## Cómo arranca el proceso
1. Resolver ruta de config (`--config`, **`ADN_PROXY_CONFIG_PATH`**, **`ADN_CONFIG_PATH`**, o por defecto **`proxy/adn-proxy.yaml`**).
2. **`load_config()`** parsea YAML → **`PROXY`**, **`SELF_SERVICE`**, **`LOG`**.
3. Pool **MySQL** opcional si self-service / funciones de BD están activas.
4. El **reactor** Twisted ejecuta UDP **ProxyProtocol** en **`LISTEN_IP:LISTEN_PORT`**, reenviando a **`MASTER:puerto_destino_asignado`**.
Ejecución (desde la raíz de adn-monitor):
```bash
# YAML dedicado del proxy (recomendado)
export ADN_PROXY_CONFIG_PATH=/opt/adn-monitor/proxy/adn-proxy.yaml
python proxy/proxy.py
# O usar por defecto proxy/adn-proxy.yaml tras copiar desde adn-proxy.example.yaml
python proxy/proxy.py
# Legado: un solo YAML combinado con el monitor
export ADN_CONFIG_PATH=/opt/adn-monitor/monitor/adn-monitor.yaml
python proxy/proxy.py
# O ruta explícita para una ejecución
python proxy/proxy.py --config /opt/adn-monitor/proxy/adn-proxy.yaml
```
Usar **systemd** u otro supervisor junto a **`monitor.py`** y la pila **PHP**.
---
## RPTO, opciones y self-service
El proxy **nunca** envía **RPTO** directamente al hotspot para actualizaciones self-service. Envía **RPTO al MASTER** (peer server); el servidor actualiza bridges/opciones y aplica el flujo HBP normal.
| Evento | Comportamiento del proxy |
|--------|---------------------------|
| ~**10 s** tras login del hotspot (**RPTC**) | Leer **`Clients.options`** de la BD → **RPTO** → master `(MASTER, dport)`. |
| Cada ~**10 s** | Filas con **`modified = 1`** → **RPTO** → master, luego limpiar **`modified`**. |
| El hotspot envía **RPTO** | Reenvío al master; actualizaciones de BD según implementación. |
Detalle: [Self-service](self-service.md) y **`proxy/README.md`** en adn-monitor.
---
## Visibilidad en el monitor
Los hotspots aparecen en el panel solo si el **peer server** envía **informes TCP** al mismo host/puerto que **`ADN_CONNECTION`** en **`adn-monitor.yaml`**. Alinea **`REPORTS`** en el servidor con **`ADN_IP` / `ADN_PORT`**. Ver [Monitor e informes](../server/user-guide/monitoring.md).
---
## Ver también
- [Configuración del monitor](configuration.md) — **`adn-monitor.yaml`** (panel, informes, MySQL para backend/monitor); detalle **`PROXY`** arriba.
- [Arquitectura](architecture.md) — dónde encaja el proxy en la pila.
- [Self-service](self-service.md) — BD, **`modified`**, temporización RPTO.
- [Proxy hotspot integrado](../server/user-guide/hotspot-proxy.md) — `PROXY` y `SELF_SERVICE` en **`adn-server.yaml`**
- [Descripción general del monitor](index.md) — pila del panel (`monitor.py` + React)

@ -1,6 +1,6 @@
# ADN Monitor (descripción general)
**ADN Monitor** es un proyecto distinto del **ADN DMR Peer Server**, pero ambos suelen desplegarse **juntos**: el servidor envía **informes TCP** (config, bridges, eventos de llamada) al monitor; el monitor alimenta el **panel web** (React) y las actualizaciones **WebSocket**. Los componentes opcionales incluyen la **API PHP** (Slim), **MySQL** (self-service / registro de dispositivos) y el **proxy hotspot** (UDP entre hotspots y el peer server — **integrado en `adn-server.py`** por defecto; `adn-proxy` independiente queda para layouts legados).
**ADN Monitor** es un proyecto distinto del **ADN DMR Peer Server**, pero ambos suelen desplegarse **juntos**: el servidor envía **informes TCP** (o MQTT) al monitor; el monitor alimenta el **panel web** (React) y las actualizaciones **WebSocket**. Un único proceso **`monitor.py`** (FastAPI) sirve **REST** (`/api/*`), **WebSocket** (`/ws`) e **ingest** de informes. Opcional: **MySQL** (self-service / Last Heard) y **proxy hotspot** integrado en **`adn-server.py`**.
Este capítulo documenta la pila **adn-monitor** con el mismo nivel de detalle que las guías del servidor. El código fuente está en el repositorio **adn-monitor**, no en el repositorio **adn-server** (donde se mantiene esta documentación).
@ -8,20 +8,17 @@ Este capítulo documenta la pila **adn-monitor** con el mismo nivel de detalle q
| Parte | Rol |
|-------|-----|
| **`monitor/monitor.py`** | Python (Twisted): se conecta al **puerto TCP de informes** del peer server, decodifica cargas netstring (`CONFIG_SND`, `BRIDGE_SND`, `BRDG_EVENT`), mantiene **CTABLE** / **BTABLE**, escribe **Last Heard** / estadísticas de TG en **MySQL** si está configurado, sirve JSON **WebSocket** al panel. |
| **`backend/`** | PHP **Slim**: `/api/config/dashboard`, auth, APIs **self-service** opcionales, proxy de alias. Lee **`adn-monitor.yaml`** vía **`ADN_CONFIG_PATH`**. |
| **`frontend/`** | React (Vite): UI del panel; consume API del backend + WebSocket. |
| **`proxy/`** | Python (Twisted): proxy hotspot UDP **independiente** (legado); reenvía Homebrew entre hotspots y el rango de puertos del peer server; lee **`Clients`** en MySQL para **RPTO**. Preferir **`PROXY`** integrado en **`adn-server.yaml`** — ver [Proxy hotspot](hotspot-proxy.md). |
| **`monitor/monitor.py`** | FastAPI: REST (`/api/*`), WebSocket (`/ws`), ingest TCP o MQTT, estado **CTABLE** / Last Heard, self-service MySQL. |
| **`frontend/`** | React (Vite): UI del panel; mismo origen `/api` + `/ws`. |
## Ficheros de configuración
| Fichero | Quién lo usa | Variable típica |
|---------|----------------|-----------------|
| **`adn-server.yaml`** | **`adn-server.py`** (**`PROXY`** / **`SELF_SERVICE`** integrados) | `-c` / ruta por defecto junto al binario |
| **`monitor/adn-monitor.yaml`** | **`monitor.py`**, **backend PHP** | **`ADN_CONFIG_PATH`** |
| **`proxy/adn-proxy.yaml`** | **`proxy/proxy.py`** (independiente legado) | **`ADN_PROXY_CONFIG_PATH`** (opcional; ver [Proxy hotspot](hotspot-proxy.md#configuration-file)) |
| **`monitor/adn-monitor.yaml`** | **`monitor.py`** | **`ADN_CONFIG_PATH`** |
**`SELF_SERVICE`** (MySQL / PBKDF2) debe **coincidir** entre **`adn-server.yaml`** (proxy integrado), **`adn-monitor.yaml`** y **`adn-proxy.yaml`** legado si se usa. **`ADN_CONNECTION`**, panel, WebSocket y alias van en **`adn-monitor.yaml`**; **`PROXY`** / **`SELF_SERVICE`** integrados van en **`adn-server.yaml`**; el proxy independiente sigue en **`adn-proxy.yaml`**.
**`SELF_SERVICE`** (MySQL / PBKDF2) debe **coincidir** entre **`adn-server.yaml`** y **`adn-monitor.yaml`**. **`ADN_CONNECTION`**, panel, WebSocket y alias van en **`adn-monitor.yaml`**; **`PROXY`** integrado va en **`adn-server.yaml`** — ver [Proxy hotspot integrado](../server/user-guide/hotspot-proxy.md).
## Enlace con el peer server
@ -34,7 +31,7 @@ Ver [Monitor e informes](../server/user-guide/monitoring.md) para los opcodes de
## Ver también
- [Proxy hotspot](hotspot-proxy.md) — `PROXY`, rango de puertos del peer, carga de config y arranque
- [Proxy hotspot (integrado)](../server/user-guide/hotspot-proxy.md) — `PROXY` en `adn-server.yaml`
- [Arquitectura e implantación](architecture.md)
- [Configuración (`adn-monitor.yaml`)](configuration.md)
- [Self-service](self-service.md)

@ -2,14 +2,14 @@
**Self-service** permite al dueño de un hotspot **iniciar sesión** en el panel, **editar las opciones de su dispositivo** (listas estáticas de TG, reflector por defecto, temporizador, idioma, etc.) y que esas opciones se **envíen al ADN DMR Peer Server** sin editar YAML a mano en el servidor.
Intervienen **cuatro** piezas: **MySQL** (tabla `Clients`), **API PHP** (sesión + REST), **React** (página `/self-service`) y el **proxy hotspot** (**RPTO** periódico al master). El **peer server** es quien aplica al final **RPTO** / **OPTIONS** al bridge en ejecución y al comportamiento del hotspot.
Intervienen **cuatro** piezas: **MySQL** (tabla `Clients`), **API del monitor** (FastAPI, sesión + REST), **React** (página `/self-service`) y el **proxy hotspot integrado** en **adn-server** (**RPTO** periódico al master). El **peer server** es quien aplica al final **RPTO** / **OPTIONS** al bridge en ejecución y al comportamiento del hotspot.
---
## Requisitos previos
1. Bloque **`SELF_SERVICE`** en **`adn-monitor.yaml`** con credenciales **MySQL** válidas y parámetros **PBKDF2** alineados con tu herramienta de contraseñas (misma sal/iteraciones que **`hotspot_proxy_self_service.py`** cuando se use).
2. **`DASHBOARD.SELF_SERVICE: true`** para que la UI muestre la entrada **Self-service** (y el backend exponga `/api/self-service/*` cuando la BD conecta).
2. **`DASHBOARD.SELF_SERVICE: true`** para que la UI muestre la entrada **Self-service** (y `monitor.py` exponga `/api/self-service/*` cuando la BD conecta).
3. Tabla **`Clients`** con filas: **`callsign`**, **`int_id`** (ID DMR), **`psswd`** (hex PBKDF2-SHA256), **`options`** (línea `KEY=value` separada por `;`), **`logged_in`**, **`host`**, **`modified`**, etc. (ver esquema / migraciones en el repo adn-monitor).
4. El tráfico del hotspot debe pasar por el **proxy** si dependes de **`modified`** y del envío **RPTO** (ver flujo abajo).
@ -19,7 +19,7 @@ Intervienen **cuatro** piezas: **MySQL** (tabla `Clients`), **API PHP** (sesión
| Endpoint | Uso |
|----------|-----|
| **`POST /api/auth/login`** | Cuerpo: `callsign`, `password`. Verifica hash **PBKDF2** contra **`Clients.psswd`** en filas con **`logged_in = 1`**. Éxito: sesión PHP con **`user_id`**, **`int_ids`** (todos los DMR ID de ese indicativo). |
| **`POST /api/auth/login`** | Cuerpo: `callsign`, `password`. Verifica hash **PBKDF2** contra **`Clients.psswd`** en filas con **`logged_in = 1`**. Éxito: cookie de sesión con **`user_id`**, **`int_ids`** (todos los DMR ID de ese indicativo). |
| **`GET /api/auth/login-by-ip`** | Opcional: una coincidencia de usuario por **`Clients.host`** = IP del cliente (misma forma de sesión). |
| **`POST /api/auth/logout`** | Cierra sesión. |
| **`GET /api/auth/me`** | Devuelve `{ callsign, int_ids, selected_int_id }` para React. |
@ -41,7 +41,7 @@ La sesión se prolonga con actividad (**SelfServiceController** usa un timeout l
## Flujo extremo a extremo (cómo llegan las opciones al hotspot)
1. El usuario guarda opciones en la web → **PHP** escribe **`Clients.options`** y **`modified = 1`**.
1. El usuario guarda opciones en la web → la **API del monitor** escribe **`Clients.options`** y **`modified = 1`**.
2. El **proxy hotspot** ejecuta **`send_opts`** en bucle (~cada **10 s**). Para filas con **`modified = 1`**, lee opciones de la BD, envía **RPTO** **al peer server** en **`(MASTER, puerto_destino_asignado)`**, luego limpia **`modified`** en la BD.
3. El **ADN DMR Peer Server** recibe **RPTO** en la pata MASTER y actualiza su estado **OPTIONS** / bridge (mismo camino que un refresco normal de registro del hotspot).
4. El servidor envía la señalización adecuada al **hotspot** para que TG estáticos / reflector / temporizador surtan efecto **sin** reinicio completo del hotspot (el comportamiento coincide con el flujo HBP **OPTIONS** del servidor).

@ -10,8 +10,7 @@ La configuración está en **`adn-server.yaml`**, bloques **`PROXY`** y opcional
| Despliegue | Qué ejecutar |
|------------|--------------|
| **Stack ADN habitual** (monitor + panel + muchos hotspots Pi-Star) | **`adn-server.py`** con **`PROXY`** + **`SELF_SERVICE`** — desactiva la unidad **`adn-proxy`** independiente para evitar conflicto en **`PROXY.LISTEN_PORT`**. |
| **Legado / config separada** | **`proxy/proxy.py`** en el repo **adn-monitor** — ver [Proxy hotspot (independiente)](../../monitor/hotspot-proxy.md). |
| **Stack ADN habitual** (monitor + panel + muchos hotspots Pi-Star) | **`adn-server.py`** con **`PROXY`** + **`SELF_SERVICE`**. |
El proxy integrado usa **fan-in**: los hotspots solo necesitan **`PROXY.LISTEN_PORT`** (p. ej. **62031**). El **MASTER** destino es **solo inyección** — **no** abre su propio puerto UDP para ese system (sin rango de puertos por hotspot en el host del servidor).
@ -68,13 +67,13 @@ Define **`MAX_PEERS`** en el MASTER destino como máximo de hotspots simultáneo
## Claves `SELF_SERVICE`
Misma semántica que **`adn-monitor.yaml`** / **`adn-proxy.yaml`** legado — tabla **`Clients`** compartida, flag **`modified`**, **RPTO** hacia el MASTER.
Misma semántica que **`adn-monitor.yaml`** — tabla **`Clients`** compartida, flag **`modified`**, **RPTO** hacia el MASTER.
| Clave | Rol |
|-------|-----|
| **USE_SELFSERVICE** | Activa sincronización de opciones con MySQL (`true` / `false`). |
| **DB_SERVER**, **DB_USERNAME**, **DB_PASSWORD**, **DB_NAME**, **DB_PORT** | Conexión MySQL. |
| **PBKDF2_SALT**, **PBKDF2_ITERATIONS** | Deben **coincidir** con monitor/backend para el hash de contraseñas. |
| **PBKDF2_SALT**, **PBKDF2_ITERATIONS** | Deben **coincidir** con **`adn-monitor.yaml`** para el hash de contraseñas. |
Al arrancar el servidor registra **`(SELF_SERVICE) Database connection test: OK`** y **`(SELF_SERVICE) Enabled`** si el pool conecta. El self-service es **asíncrono**; el reenvío de voz no se bloquea por latencia de BD.
@ -106,15 +105,8 @@ Ver [Configuración — recarga en caliente](configuration.md#recarga-en-calient
---
## Proxy independiente (legado)
El repo **adn-monitor** sigue incluyendo **`proxy/proxy.py`** para despliegues con relay UDP **separado** y **`adn-proxy.yaml`**. **No** ejecutes el proxy integrado y **`adn-proxy`** independiente en el mismo **`LISTEN_PORT`**.
---
## Ver también
- [Configuración](configuration.md) — referencia completa de **`adn-server.yaml`**.
- [Monitorización e informes](monitoring.md) — informes TCP, panel, rotación de logs.
- [Self-service](../../monitor/self-service.md) — **`Clients`**, temporización **RPTO**.
- [Proxy hotspot (independiente)](../../monitor/hotspot-proxy.md) — layout legado **`adn-proxy`**.

@ -27,7 +27,7 @@ Enrutado, temporizadores, control de bucle OpenBridge y manejo de protocolo est
## Programas relacionados
- **Parrot / reproducción** — punto de entrada aparte (`adn-parrot.py`) para grabar y reproducir; ver [Parrot](parrot.md).
- **Proxy hotspot independiente** — **`adn-proxy`** legado en el repo **adn-monitor** si no usas el proxy integrado; ver [Proxy hotspot (independiente)](../../monitor/hotspot-proxy.md).
- **Proxy hotspot integrado** — `PROXY` en **`adn-server.yaml`**; ver [Proxy hotspot](hotspot-proxy.md).
## Siguientes pasos

@ -16,7 +16,7 @@ Las pilas antiguas (**legado** estilo `adn-dmr-server`) pueden **omitir** HELLO.
El **monitor** decodifica estos mensajes, actualiza **CTABLE** / **BTABLE** y (con MySQL configurado) persiste Last Heard / estadísticas.
**Pila completa:** [Descripción general del ADN Monitor](../../monitor/index.md) (monitor Python, WebSocket, API PHP, proxy y self-service opcionales).
**Pila completa:** [Descripción general del ADN Monitor](../../monitor/index.md) (monitor FastAPI, WebSocket, self-service).
### Líneas de log del canal de informes (logger `adn-monitor`)
@ -51,7 +51,6 @@ Estos procesos tratan **`SIGUSR2`** solo para **reabrir** los ficheros de log (`
| Proceso | Claves típicas de configuración |
|---------|-----------------------------------|
| **`adn-server`** / **`adn-parrot`** | **`LOGGER.LOG_FILE`** (los logs del proxy integrado van al mismo fichero) |
| **`adn-proxy`** (independiente, legado) | **`LOG.PATH`** + **`LOG.LOG_FILE`** en `adn-proxy.yaml` — omitir si usas **`PROXY`** integrado en `adn-server.yaml` |
| **`adn-monitor`** | **`LOG.PATH`** + **`LOG.LOG_FILE`** en `adn-monitor.yaml` |
Ejemplo de fragmento en **`/etc/logrotate.d/adn`** (adaptar rutas y nombres de unidad):
@ -71,7 +70,7 @@ Ejemplo de fragmento en **`/etc/logrotate.d/adn`** (adaptar rutas y nombres de u
}
```
Repite **`postrotate`** con **`kill -USR2`** para **`adn-parrot`** y **`adn-monitor`** si rotas sus logs en el mismo host. Añade **`adn-proxy`** solo si sigues usando el proxy **independiente** (no hace falta con proxy integrado en **`adn-server`**). Usa el **PID** correcto (**`MainPID`** de systemd, pidfile, o el proceso que gestiones).
Repite **`postrotate`** con **`kill -USR2`** para **`adn-parrot`** y **`adn-monitor`** si rotas sus logs en el mismo host. Usa el **PID** correcto (**`MainPID`** de systemd, pidfile, o el proceso que gestiones).
## Requisitos
@ -82,4 +81,4 @@ Repite **`postrotate`** con **`kill -USR2`** para **`adn-parrot`** y **`adn-moni
Los operadores que editan **opciones de dispositivo** desde el panel usan el flujo **self-service** (MySQL **`Clients`**, **RPTO** hacia el MASTER de conferencia). En despliegues actuales de **ADN DMR Peer Server** esto corre **dentro de `adn-server.py`**: configura **`SELF_SERVICE`** y **`PROXY`** en **`adn-server.yaml`** (ver [Proxy hotspot](hotspot-proxy.md)). Semántica del panel: [Self-service](../../monitor/self-service.md).
Los stacks legados pueden seguir usando **`adn-proxy`** independiente y **`adn-proxy.yaml`** — ver [Proxy hotspot (independiente)](../../monitor/hotspot-proxy.md). No ejecutes ambos en el mismo **`LISTEN_PORT`**.
Los logs del proxy hotspot forman parte de **`adn-server`** cuando **`PROXY`** está activo — ver [Proxy hotspot integrado](hotspot-proxy.md).

@ -0,0 +1,33 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>ADN Systems documentation</title>
<script>
(function () {
function detectLang() {
var list = [];
if (typeof navigator.languages !== "undefined" && navigator.languages.length) {
list = Array.prototype.slice.call(navigator.languages);
} else {
var one = navigator.language || navigator.userLanguage || "";
if (one) list = [one];
}
for (var i = 0; i < list.length; i++) {
var tag = String(list[i] || "").toLowerCase();
if (tag === "es" || tag.indexOf("es-") === 0) return "es";
}
return "en";
}
window.location.replace("/docs/" + detectLang() + "/");
})();
</script>
<noscript>
<meta http-equiv="refresh" content="0; url=/docs/en/">
</noscript>
</head>
<body>
<p>Redirecting… <a href="/docs/es/">Español</a> · <a href="/docs/en/">English</a></p>
</body>
</html>
Loading…
Cancel
Save

Powered by TurnKey Linux.