diff --git a/README.md b/README.md index 3d26227..a09f5fb 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,8 @@ GPL v3. Derived from FreeDMR / HBlink. Copy `adn-server.example.yaml` to `adn-server.yaml` and edit with your settings. Production config is not committed. +The example includes an **integrated hotspot proxy** (`PROXY`) and optional **MySQL self-service** (`SELF_SERVICE`). For self-service, install the optional extra: `pip install -e ".[selfservice]"`. See [Hotspot proxy (integrated)](docs/en/server/user-guide/hotspot-proxy.md). Disable standalone **`adn-proxy`** if you use the integrated proxy on the same host. + ### Voice configuration Voice features (announcements, TTS, recording) use a separate config file. Copy `adn-voice.example.yaml` to `adn-voice.yaml` and edit. If the file does not exist, voice features are disabled (no error). Changes are hot-reloaded every 15 seconds. diff --git a/docs/en/README.md b/docs/en/README.md index f461e19..e903f31 100644 --- a/docs/en/README.md +++ b/docs/en/README.md @@ -47,8 +47,9 @@ Dashboard, WebSocket live view, optional **PHP API**, **MySQL** self-service, an | I want to… | Start here | |------------|------------| -| `adn-monitor.yaml`, `adn-proxy.yaml`, layout | [Monitor configuration](monitor/configuration.md), [Hotspot proxy](monitor/hotspot-proxy.md) | -| Hotspot proxy (UDP, `PROXY`, peer port range) | [Hotspot proxy](monitor/hotspot-proxy.md) | +| `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) | +| 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) | diff --git a/docs/en/monitor/architecture.md b/docs/en/monitor/architecture.md index 886fa49..a46ce5b 100644 --- a/docs/en/monitor/architecture.md +++ b/docs/en/monitor/architecture.md @@ -34,14 +34,16 @@ Same opcodes as documented for the server: **CONFIG_SND**, **BRIDGE_SND**, **BRD ## Hotspot proxy -- Entry: `proxy/proxy.py`; package `src/adn_proxy/` (domain / application / infrastructure). -- Reads **`PROXY`** and **`SELF_SERVICE`** from **`adn-proxy.yaml`** by default (or from the same file as the monitor when **`ADN_CONFIG_PATH`** is used without **`ADN_PROXY_CONFIG_PATH`** — see [Hotspot proxy](hotspot-proxy.md#configuration-file)). -- For each hotspot client, allocates a UDP port in **`PORT`…`PORT+GENERATOR-1`** (YAML **`PORT`** + **`GENERATOR`**, matching **`adn-server`**) 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). The **peer server** then applies options to the hotspot path (see [Self-service](self-service.md)). +**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):** -**Why it is not part of the peer server binary:** it shares deployment, **`SELF_SERVICE`** MySQL, and packaging with the monitor stack — see [Why it ships with the monitor](hotspot-proxy.md#why-it-ships-with-the-monitor-not-inside-the-peer-server). +- 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) (config keys, peer server port range, startup). +**Details:** [Hotspot proxy](hotspot-proxy.md) (integrated vs standalone, config keys, startup). ## Typical deployment topology diff --git a/docs/en/monitor/configuration.md b/docs/en/monitor/configuration.md index 00e501f..afb2a2a 100644 --- a/docs/en/monitor/configuration.md +++ b/docs/en/monitor/configuration.md @@ -2,7 +2,7 @@ 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`**). -The **hotspot proxy** loads a **separate** file by default — **`proxy/adn-proxy.yaml`** — see [Hotspot proxy](hotspot-proxy.md). **`SELF_SERVICE`** (MySQL / PBKDF2) must stay **identical** between the two YAML files when both are used. +**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. 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). @@ -50,9 +50,9 @@ If the PHP backend cannot connect, **auth** and **self-service** API routes are ## `PROXY` -Hotspot **UDP proxy** — full guide: [Hotspot proxy](hotspot-proxy.md). In current layouts, these keys live in **`proxy/adn-proxy.yaml`**, not in `adn-monitor.yaml`. **Legacy:** a single file can still contain **PROXY** if the proxy is started with **`ADN_CONFIG_PATH`** pointing at that file (see resolution order in [Hotspot proxy](hotspot-proxy.md#configuration-file)). +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`**). -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)). +**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 | |-----|---------| diff --git a/docs/en/monitor/hotspot-proxy.md b/docs/en/monitor/hotspot-proxy.md index 02ed9a5..3029dfe 100644 --- a/docs/en/monitor/hotspot-proxy.md +++ b/docs/en/monitor/hotspot-proxy.md @@ -1,24 +1,33 @@ # Hotspot proxy -The **hotspot proxy** is part of the **adn-monitor** repository. It is a **UDP relay** between **DMR hotspots** (Homebrew / HBP) and the **ADN DMR Peer Server** MASTER: each connected hotspot is mapped to a **dedicated destination port** on the peer server host so many hotspots can share one public IP without port clashes. +## Integrated proxy (current default) -Source layout: `proxy/proxy.py`, package `proxy/src/adn_proxy/` (clean architecture). **GPL v3** (derivative of Simon Adlem, G7RZU’s original proxy). +**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). + +- 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) -### Why it ships with the monitor (not inside the peer server) +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`**. -There is no single mandatory layout for every deployment, but **today the proxy lives in the adn-monitor repo** on purpose: +Source layout: `proxy/proxy.py`, package `proxy/src/adn_proxy/` (clean architecture). **GPL v3** (derivative of Simon Adlem, G7RZU’s original proxy). -- **Same deployment** as the dashboard stack: **`ADN_CONFIG_PATH`** / **`ADN_PROXY_CONFIG_PATH`**, **`adn-monitor.yaml`** + **`adn-proxy.yaml`**, and usually the same host as **PHP** and **MySQL**. -- **Self-service** ( **`Clients`**, RPTO, **`modified`**) is built around that ecosystem; the peer server binary does not own that database or the **`PROXY`** block. -- **Role split:** the **ADN DMR Peer Server** is the **radio core** (HBP/OpenBridge, bridges, voice, TCP reports). The hotspot proxy is an **optional UDP front** toward a MASTER that already listens on a port **range** — useful when many hotspots share one public address. +### Why it also ships with the monitor -**Bundling the proxy into the peer server** (one binary, one `adn-server.yaml`) is conceivable for packaging, but it implies **merging configuration**, **rethinking self-service wiring**, and extra maintenance — only worth it if you explicitly want a single deployable “all-in-one” server. +- **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 proxy does **not** use `adn-server.yaml`. It reads YAML that contains **`PROXY`**, **`SELF_SERVICE`**, and **`LOGGER`** (proxy log). +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 diff --git a/docs/en/monitor/index.md b/docs/en/monitor/index.md index 9e46de5..6ef3e48 100644 --- a/docs/en/monitor/index.md +++ b/docs/en/monitor/index.md @@ -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). +**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). 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). @@ -11,16 +11,17 @@ This chapter documents the **adn-monitor** stack at the same level of detail as | **`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): UDP **hotspot proxy**; forwards Homebrew between hotspots and the peer server; reads **`Clients`** in MySQL for **RPTO** options (self-service). Loads **`adn-proxy.yaml`** by default (see [Hotspot proxy](hotspot-proxy.md)). | +| **`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). | ## 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`** | **`ADN_PROXY_CONFIG_PATH`** (optional; defaults and legacy fallback in [Hotspot proxy](hotspot-proxy.md#configuration-file)) | +| **`proxy/adn-proxy.yaml`** | **`proxy/proxy.py`** (legacy standalone) | **`ADN_PROXY_CONFIG_PATH`** (optional; see [Hotspot proxy](hotspot-proxy.md#configuration-file)) | -**`SELF_SERVICE`** (MySQL / PBKDF2) must **match** between both YAML files when you split config. **`ADN_CONNECTION`**, dashboard, WebSocket, and aliases live in **`adn-monitor.yaml`**; **`PROXY`** listen/master/range settings live in **`adn-proxy.yaml`** unless you use a **legacy** single file via **`ADN_CONFIG_PATH`** for the proxy. +**`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`**. ## Link to the peer server diff --git a/docs/en/server/development/architecture.md b/docs/en/server/development/architecture.md index 6738f4f..8e9cd34 100644 --- a/docs/en/server/development/architecture.md +++ b/docs/en/server/development/architecture.md @@ -20,6 +20,8 @@ | HBP / OpenBridge UDP | `infrastructure/twisted_adapters/udp_hbp.py` | | Report TCP | `infrastructure/twisted_adapters/report_server.py` (factory), bridge events from use cases | | Voice / TTS | `application/voice_use_cases.py`, `infrastructure/voice/` | +| Hotspot proxy (fan-in) | `infrastructure/proxy/` (`udp_fanin.py`, `runtime.py`), `application/proxy/` use cases | +| Self-service (MySQL) | `infrastructure/proxy/self_service_bridge.py`, `infrastructure/proxy/persistence/` | ## Configuration as shared state diff --git a/docs/en/server/user-guide/configuration.md b/docs/en/server/user-guide/configuration.md index 972d6f3..fa44756 100644 --- a/docs/en/server/user-guide/configuration.md +++ b/docs/en/server/user-guide/configuration.md @@ -33,9 +33,9 @@ With **systemd**, add to your unit file: ExecReload=/bin/kill -HUP $MAINPID ``` -**Reload applies:** `GLOBAL`, `REPORTS`, `ALIASES`, **`LOGGER.LOG_LEVEL`** (without process restart), per-system settings, **new/removed SYSTEMS** (including `GENERATOR` expansion and new OpenBridge legs), and updated bind addresses (listener restart for that system only). +**Reload applies:** `GLOBAL`, `REPORTS`, `ALIASES`, **`LOGGER.LOG_LEVEL`** (without process restart), **`PROXY`** (timeouts, debug, block lists — not bind or target), **`SELF_SERVICE`** (merged; enabling/disabling DB loops needs restart), per-system settings, **new/removed SYSTEMS** (including `GENERATOR` expansion/collapse and new OpenBridge legs), and updated bind addresses (listener restart for that system only). -**Not reloaded:** `adn-voice.yaml` (separate 15 s loop), Python code, subscriber alias files (separate periodic reload). **BRIDGES** table is not rebuilt on reload — restart if bridge rules changed in a way that requires a full reset. +**Not reloaded:** `adn-voice.yaml` (separate 15 s loop), Python code, subscriber alias files (separate periodic reload). **BRIDGES** table is not rebuilt on reload — restart if bridge rules changed in a way that requires a full reset. **`PROXY.LISTEN_PORT`**, **`LISTEN_IP`**, and **`TARGET_SYSTEM`** require a **full restart** to take effect. **Secrets:** Never commit real passphrases, security URLs, or `user_passwords.json` / `encryption_key.secret`. Use placeholders in templates and keep production files local. @@ -115,16 +115,16 @@ These appear mainly on **MASTER** (and often on **PEER**). OpenBridge uses a dif | Key | Meaning | |-----|---------| | **REPEAT** | If true, received traffic can be **repeated** to other connected peers on the MASTER (typical conference behaviour). | -| **MAX_PEERS** | Maximum connected hotspots. | +| **MAX_PEERS** | Maximum connected hotspots. On the **proxy target** MASTER, caps concurrent fan-in sessions. | | **EXPORT_AMBE** | Feature flag for AMBE export (if enabled in build). | | **SINGLE_MODE** | Affects OPTIONS / generator expansion (single-user style). | | **VOICE_IDENT** | Enables periodic **voice ident** when conditions are met (see `IdentUseCases`). | | **TS1_STATIC** / **TS2_STATIC** | Comma-separated static TG lists pushed via OPTIONS handling (see `options_config`). | | **DEFAULT_REFLECTOR** | Default **reflector** number for `#` dial bridges (0 = none). | | **OVERRIDE_IDENT_TG** | Optional TG for voice ident instead of all-call. | -| **GENERATOR** | If **> 1**, this MASTER is expanded into **`NAME-0`**, **`NAME-1`**, … with consecutive ports (see `expand_generator` in code). | +| **GENERATOR** | If **> 1**, this MASTER is expanded into **`NAME-0`**, **`NAME-1`**, … with consecutive ports (see `expand_generator` in code). Legacy standalone **`adn-proxy`** used the same range; the **integrated** proxy instead uses **inject-only** **`PROXY.TARGET_SYSTEM`** (no per-hotspot UDP ports on the server). | -**MASTER** listens for PEER connections; each authenticated peer is stored under **`PEERS`** at runtime. +**MASTER** listens for PEER connections (unless it is the **inject-only** proxy target — see [Hotspot proxy](hotspot-proxy.md)); each authenticated peer is stored under **`PEERS`** at runtime. --- @@ -186,6 +186,28 @@ Details: [Monitoring and reports](monitoring.md). --- +## `PROXY` (integrated hotspot proxy) + +Always started when a **`PROXY`** block is present (see `adn-server.example.yaml`). Hotspots connect to **`LISTEN_PORT`**; traffic is injected into **`TARGET_SYSTEM`**. Full guide: [Hotspot proxy](hotspot-proxy.md). + +| Key | Meaning | +|-----|---------| +| **LISTEN_PORT** / **LISTEN_IP** | UDP bind for hotspot connections. | +| **TARGET_SYSTEM** | **MASTER** system name receiving injected HBP. That system becomes **inject-only** (`IP` / `PORT` removed at load). | +| **TIMEOUT** | Session idle timeout (seconds). | +| **DEBUG** / **CLIENT_INFO** | Logging verbosity. | +| **BLACK_LIST** / **IP_BLACK_LIST** | Block radio IDs or client IPs. | + +Do **not** run standalone **`adn-proxy`** on the same **`LISTEN_PORT`** when the integrated proxy is enabled. + +--- + +## `SELF_SERVICE` (MySQL / dashboard options) + +Optional; requires `pip install -e ".[selfservice]"` when **`USE_SELFSERVICE: true`**. Uses the same **`Clients`** table and PBKDF2 parameters as **adn-monitor**. Keys match the monitor docs — see [Self-service](../../monitor/self-service.md) and [Hotspot proxy](hotspot-proxy.md#self_service-keys). + +--- + ## `LOGGER` Implemented in `infrastructure/logging_config.py` (`setup_logging`). Values are read from the **`LOGGER`** block (or overridden by `--logging` for **LOG_LEVEL** only). @@ -247,3 +269,4 @@ Use the project interpreter (see workspace rules), e.g. `python3.11` from pyenv, - [Bridges and talkgroups](bridges-and-talkgroups.md) — `BRIDGES` semantics. - [Special numbers](special-numbers.md) — reserved TGs and server IDs. - [Parrot](parrot.md) — PEER example (parrot process). +- [Hotspot proxy](hotspot-proxy.md) — integrated **`PROXY`** / **`SELF_SERVICE`**. diff --git a/docs/en/server/user-guide/hotspot-proxy.md b/docs/en/server/user-guide/hotspot-proxy.md new file mode 100644 index 0000000..c68a458 --- /dev/null +++ b/docs/en/server/user-guide/hotspot-proxy.md @@ -0,0 +1,120 @@ +# 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`** — 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). | + +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). + +--- + +## Optional dependency (self-service) + +MySQL self-service requires **`mysqlclient`**: + +```bash +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`): + +```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`** / legacy **`adn-proxy.yaml`** — shared **`Clients`** table, **`modified`** flag, **RPTO** toward the MASTER. + +| Key | Role | +|-----|------| +| **USE_SELFSERVICE** | Enable MySQL-backed options sync (`true` / `false`). | +| **DB_SERVER**, **DB_USERNAME**, **DB_PASSWORD**, **DB_NAME**, **DB_PORT** | MySQL connection. | +| **PBKDF2_SALT**, **PBKDF2_ITERATIONS** | Must **match** monitor/backend for password hashing. | + +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](../../monitor/self-service.md). + +--- + +## 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** bypass the OPTIONS filter and return to the **calling** hotspot (see [Special numbers](special-numbers.md)). + +--- + +## Hot reload (`SIGHUP`) + +**Applied without restart** (active proxy sessions stay up): + +- **`PROXY`:** **TIMEOUT**, **DEBUG**, **CLIENT_INFO**, **BLACK_LIST**, **IP_BLACK_LIST** +- **`SELF_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_SELFSERVICE`** after startup (start/stop MySQL loops) + +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. diff --git a/docs/en/server/user-guide/introduction.md b/docs/en/server/user-guide/introduction.md index 38e78c9..2a9ddab 100644 --- a/docs/en/server/user-guide/introduction.md +++ b/docs/en/server/user-guide/introduction.md @@ -21,16 +21,19 @@ Routing, timers, OpenBridge loop control, and protocol handling are implemented | **HBP protocol** | Authentication, DMRD ingress/egress, repeat to peers, TG filters. | | **OpenBridge** | DMRE ingress, hop limit, loop control (`min(1ST)`), BCSQ/BCKA when enabled. | | **Voice** | AMBE files, scheduled announcements, TTS pipeline, on-demand playback (TG 9991–9999). | -| **Reporting** | TCP netstring channel to **adn-monitor** (and compatible dashboards): config, bridge state, `BRDG_EVENT` call events. | +| **Reporting** | TCP netstring channel to **adn-monitor** (and compatible dashboards): config, bridge state, call events (report v2 JSON). | +| **Hotspot proxy** | Optional integrated UDP fan-in (`PROXY` in `adn-server.yaml`) plus MySQL **self-service** (`SELF_SERVICE`) for dashboard-driven hotspot options. | ## 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). ## Next steps -- [Configuration](configuration.md) — files, `GLOBAL`, **MASTER** / **PEER** / **OPENBRIDGE**, ACLs, reports, aliases, voice merge. +- [Configuration](configuration.md) — files, `GLOBAL`, **MASTER** / **PEER** / **OPENBRIDGE**, ACLs, reports, **`PROXY`**, **`SELF_SERVICE`**, aliases, voice merge. - [Bridges and talkgroups](bridges-and-talkgroups.md) — how `BRIDGES` works. - [Special numbers](special-numbers.md) — TG 4000, information services, echo. -- [ADN Monitor](../../monitor/index.md) — dashboard, `adn-monitor.yaml`, self-service (separate repo, deployed with the server). +- [Hotspot proxy](hotspot-proxy.md) — integrated **`PROXY`** / **`SELF_SERVICE`** in `adn-server.yaml`. +- [ADN Monitor](../../monitor/index.md) — dashboard, `adn-monitor.yaml`, self-service UI (separate repo, deployed with the server). - [Credits & license](attribution.md) — ADN → FreeDMR → hblink3, license. diff --git a/docs/en/server/user-guide/monitoring.md b/docs/en/server/user-guide/monitoring.md index 0476158..2976486 100644 --- a/docs/en/server/user-guide/monitoring.md +++ b/docs/en/server/user-guide/monitoring.md @@ -50,8 +50,8 @@ These processes handle **`SIGUSR2`** by reopening **`logging.FileHandler`** stre | Process | Typical config keys | |---------|---------------------| -| **`adn-server`** / **`adn-parrot`** | **`LOGGER.LOG_FILE`** (see `adn-server.example.yaml`) | -| **`adn-proxy`** | **`LOG.PATH`** + **`LOG.LOG_FILE`** in `adn-proxy.yaml` | +| **`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 +71,7 @@ Example **`/etc/logrotate.d/adn`** fragment (adjust paths and service names): } ``` -Repeat **`postrotate`** with **`kill -USR2`** for **`adn-parrot`**, **`adn-proxy`**, 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). +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). ## Requirements @@ -80,4 +80,6 @@ Repeat **`postrotate`** with **`kill -USR2`** for **`adn-parrot`**, **`adn-proxy ## Self-service and hotspots -Operators editing **device options** from the dashboard use the **self-service** flow (MySQL **`Clients`**, proxy **RPTO**). That is documented under [Self-service](../../monitor/self-service.md); it is **not** part of the peer server binary alone. For **hotspot proxy** configuration (`PROXY` in **`adn-proxy.yaml`** by default), how it binds to the peer server **UDP port range**, and how the process starts, see [Hotspot proxy](../../monitor/hotspot-proxy.md). +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`**. diff --git a/docs/es/README.md b/docs/es/README.md index 3d99388..22aeb50 100644 --- a/docs/es/README.md +++ b/docs/es/README.md @@ -46,7 +46,8 @@ Panel, **WebSocket** en vivo, **API PHP** opcional, **MySQL** self-service y **p | Quiero… | Empieza aquí | |---------|----------------| -| `adn-monitor.yaml`, `adn-proxy.yaml`, despliegue | [Configuración del monitor](monitor/configuration.md), [Proxy hotspot](monitor/hotspot-proxy.md) | +| `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) | | 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) | diff --git a/docs/es/monitor/architecture.md b/docs/es/monitor/architecture.md index bed7ea4..a3e0159 100644 --- a/docs/es/monitor/architecture.md +++ b/docs/es/monitor/architecture.md @@ -34,14 +34,16 @@ Los mismos opcodes que en la documentación del servidor: **CONFIG_SND**, **BRID ## Proxy hotspot -- Entrada: `proxy/proxy.py`; paquete `src/adn_proxy/` (dominio / aplicación / infraestructura). -- Lee **`PROXY`** y **`SELF_SERVICE`** desde **`adn-proxy.yaml`** por defecto (o desde el mismo fichero que el monitor si se usa solo **`ADN_CONFIG_PATH`** sin **`ADN_PROXY_CONFIG_PATH`** — ver [Proxy hotspot](hotspot-proxy.md#configuration-file)). -- Por cada cliente hotspot, asigna un puerto UDP en **`PORT`…`PORT+GENERATOR-1`** (**`PORT`** + **`GENERATOR`** en YAML, alineados con **`adn-server`**) 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). El **peer server** aplica entonces las opciones al camino del hotspot (ver [Self-service](self-service.md)). +**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):** -**Por qué no forma parte del binario del peer server:** comparte implantación, **self-service** MySQL y empaquetado con la pila del monitor — ver [Por qué va con el monitor](hotspot-proxy.md#why-it-ships-with-the-monitor-not-inside-the-peer-server). +- 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). -**Detalle:** [Proxy hotspot](hotspot-proxy.md) (claves de config, rango de puertos del peer, arranque). +**Detalle:** [Proxy hotspot](hotspot-proxy.md) (integrado vs independiente, claves, arranque). ## Topología típica de despliegue diff --git a/docs/es/monitor/configuration.md b/docs/es/monitor/configuration.md index 8d4b174..a90aacd 100644 --- a/docs/es/monitor/configuration.md +++ b/docs/es/monitor/configuration.md @@ -2,7 +2,7 @@ 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`**). -El **proxy hotspot** carga por defecto **otro** fichero — **`proxy/adn-proxy.yaml`** — ver [Proxy hotspot](hotspot-proxy.md). La sección **`SELF_SERVICE`** (MySQL / PBKDF2) debe ser **idéntica** en ambos YAML cuando se usan los dos. +**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. 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). @@ -50,9 +50,9 @@ Si el backend PHP no puede conectar, las rutas de **auth** y **self-service** no ## `PROXY` -**Proxy UDP hotspot** — guía completa: [Proxy hotspot](hotspot-proxy.md). En los despliegues actuales, estas claves están en **`proxy/adn-proxy.yaml`**, no en `adn-monitor.yaml`. **Legado:** un único fichero puede seguir incluyendo **PROXY** si el proxy se arranca con **`ADN_CONFIG_PATH`** apuntando a ese fichero (ver orden de resolución en [Proxy hotspot](hotspot-proxy.md#configuration-file)). +**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: **`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)). +**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 | |-------|-------------| diff --git a/docs/es/monitor/hotspot-proxy.md b/docs/es/monitor/hotspot-proxy.md index 4054ac6..fa229e2 100644 --- a/docs/es/monitor/hotspot-proxy.md +++ b/docs/es/monitor/hotspot-proxy.md @@ -1,24 +1,33 @@ # Proxy hotspot -El **proxy hotspot** forma parte del repositorio **adn-monitor**. Es un **relé UDP** entre **hotspots DMR** (Homebrew / HBP) y el **MASTER** del **ADN DMR Peer Server**: cada hotspot conectado se asigna a un **puerto de destino** dedicado en el host del peer server, de modo que muchos hotspots puedan compartir una IP pública sin choques de puertos. +## Proxy integrado (predeterminado actual) -Estructura: `proxy/proxy.py`, paquete `proxy/src/adn_proxy/` (arquitectura limpia). **GPL v3** (derivado del proxy original de Simon Adlem, G7RZU). +**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). + +- 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) -### Por qué va con el monitor (y no dentro del peer server) {#why-it-ships-with-the-monitor-not-inside-the-peer-server} +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. -No hay un despliegue obligatorio único, pero **hoy el proxy vive en el repo adn-monitor** a propósito: +Estructura: `proxy/proxy.py`, paquete `proxy/src/adn_proxy/` (arquitectura limpia). **GPL v3** (derivado del proxy original de Simon Adlem, G7RZU). -- **Mismo despliegue** que el panel: **`ADN_CONFIG_PATH`** / **`ADN_PROXY_CONFIG_PATH`**, **`adn-monitor.yaml`** + **`adn-proxy.yaml`**, y normalmente el mismo host que **PHP** y **MySQL**. -- El **self-service** (**`Clients`**, RPTO, **`modified`**) está montado sobre ese ecosistema; el binario del peer server no posee esa base de datos ni el bloque **`PROXY`**. -- **División de roles:** el **ADN DMR Peer Server** es el **núcleo de radio** (HBP/OpenBridge, bridges, voz, informes TCP). El proxy hotspot es un **frente UDP opcional** hacia un MASTER que ya escucha en un **rango** de puertos — útil cuando muchos hotspots comparten una dirección pública. +### Por qué también va con el monitor {#why-it-ships-with-the-monitor-not-inside-the-peer-server} -**Integrar el proxy en el peer server** (un binario, un `adn-server.yaml`) es imaginable para empaquetado, pero implica **unificar configuración**, **replantear el cableado de self-service** y más mantenimiento — solo compensa si quieres explícitamente un servidor “todo en uno” desplegable. +- **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 **no** usa `adn-server.yaml`. Lee un YAML que incluye **`PROXY`**, **`SELF_SERVICE`** y **`LOGGER`** (log del proxy). +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 diff --git a/docs/es/monitor/index.md b/docs/es/monitor/index.md index 4cc312d..81bc4b5 100644 --- a/docs/es/monitor/index.md +++ b/docs/es/monitor/index.md @@ -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). +**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). 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). @@ -11,16 +11,17 @@ Este capítulo documenta la pila **adn-monitor** con el mismo nivel de detalle q | **`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; reenvía Homebrew entre hotspots y el peer server; lee **`Clients`** en MySQL para opciones **RPTO** (self-service). Carga **`adn-proxy.yaml`** por defecto (ver [Proxy hotspot](hotspot-proxy.md)). | +| **`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). | ## 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`** | **`ADN_PROXY_CONFIG_PATH`** (opcional; valores por defecto y legado en [Proxy hotspot](hotspot-proxy.md#configuration-file)) | +| **`proxy/adn-proxy.yaml`** | **`proxy/proxy.py`** (independiente legado) | **`ADN_PROXY_CONFIG_PATH`** (opcional; ver [Proxy hotspot](hotspot-proxy.md#configuration-file)) | -**`SELF_SERVICE`** (MySQL / PBKDF2) debe **coincidir** entre ambos YAML cuando separas la config. **`ADN_CONNECTION`**, panel, WebSocket y alias van en **`adn-monitor.yaml`**; los ajustes **`PROXY`** de escucha/master/rango van en **`adn-proxy.yaml`**, salvo que uses un **fichero único legado** vía **`ADN_CONFIG_PATH`** para el proxy. +**`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`**. ## Enlace con el peer server diff --git a/docs/es/server/development/architecture.md b/docs/es/server/development/architecture.md index 45fe416..4409d47 100644 --- a/docs/es/server/development/architecture.md +++ b/docs/es/server/development/architecture.md @@ -20,6 +20,8 @@ | HBP / OpenBridge UDP | `infrastructure/twisted_adapters/udp_hbp.py` | | Informes TCP | `infrastructure/twisted_adapters/report_server.py` (fábrica), eventos de bridge desde casos de uso | | Voz / TTS | `application/voice_use_cases.py`, `infrastructure/voice/` | +| Proxy hotspot (fan-in) | `infrastructure/proxy/` (`udp_fanin.py`, `runtime.py`), casos de uso en `application/proxy/` | +| Self-service (MySQL) | `infrastructure/proxy/self_service_bridge.py`, `infrastructure/proxy/persistence/` | ## Configuración como estado compartido diff --git a/docs/es/server/user-guide/configuration.md b/docs/es/server/user-guide/configuration.md index 1957f4e..5508d43 100644 --- a/docs/es/server/user-guide/configuration.md +++ b/docs/es/server/user-guide/configuration.md @@ -33,9 +33,9 @@ Con **systemd**, en la unidad: ExecReload=/bin/kill -HUP $MAINPID ``` -**Se recarga:** `GLOBAL`, `REPORTS`, `ALIASES`, **`LOGGER.LOG_LEVEL`** (sin reiniciar el proceso), parámetros por system, **systems nuevos/eliminados** (incluida expansión `GENERATOR` y OBP nuevos), y cambios de IP/puerto (solo reinicia el listener de ese system). +**Se recarga:** `GLOBAL`, `REPORTS`, `ALIASES`, **`LOGGER.LOG_LEVEL`** (sin reiniciar el proceso), **`PROXY`** (timeouts, debug, listas de bloqueo — no bind ni destino), **`SELF_SERVICE`** (fusionado; activar/desactivar bucles BD requiere reinicio), parámetros por system, **systems nuevos/eliminados** (incluida expansión/colapso `GENERATOR` y OBP nuevos), y cambios de IP/puerto (solo reinicia el listener de ese system). -**No se recarga:** `adn-voice.yaml` (loop aparte cada 15 s), código Python, ficheros de alias (recarga periódica). La tabla **BRIDGES** no se reconstruye — reinicia si cambiaste reglas de bridge que exijan reset completo. +**No se recarga:** `adn-voice.yaml` (loop aparte cada 15 s), código Python, ficheros de alias (recarga periódica). La tabla **BRIDGES** no se reconstruye — reinicia si cambiaste reglas de bridge que exijan reset completo. **`PROXY.LISTEN_PORT`**, **`LISTEN_IP`** y **`TARGET_SYSTEM`** requieren **reinicio completo** para aplicarse. **Secretos:** no versionar passphrases reales, URLs de seguridad ni `user_passwords.json` / `encryption_key.secret`. Usa placeholders en plantillas y mantén producción en local. @@ -115,16 +115,16 @@ Aparecen principalmente en **MASTER** (y a menudo en **PEER**). OpenBridge usa u | Clave | Significado | |-------|-------------| | **REPEAT** | Si es true, el tráfico recibido puede **repetirse** a otros peers conectados al MASTER (comportamiento típico de conferencia). | -| **MAX_PEERS** | Máximo de hotspots conectados. | +| **MAX_PEERS** | Máximo de hotspots conectados. En el MASTER **destino del proxy**, limita sesiones fan-in simultáneas. | | **EXPORT_AMBE** | Flag de exportación AMBE (si está habilitado en el build). | | **SINGLE_MODE** | Afecta a OPTIONS / expansión del generador (estilo un solo usuario). | | **VOICE_IDENT** | Habilita **identificación por voz** periódica cuando se cumplen condiciones (ver `IdentUseCases`). | | **TS1_STATIC** / **TS2_STATIC** | Listas estáticas de TG separadas por comas, enviadas vía manejo OPTIONS (ver `options_config`). | | **DEFAULT_REFLECTOR** | Número de **reflector** por defecto para bridges de marcado `#` (0 = ninguno). | | **OVERRIDE_IDENT_TG** | TG opcional para ident por voz en lugar de all-call. | -| **GENERATOR** | Si es **> 1**, este MASTER se expande en **`NAME-0`**, **`NAME-1`**, … con puertos consecutivos (ver `expand_generator` en código). | +| **GENERATOR** | Si es **> 1**, este MASTER se expande en **`NAME-0`**, **`NAME-1`**, … con puertos consecutivos (ver `expand_generator` en código). El **`adn-proxy`** independiente legado usaba el mismo rango; el proxy **integrado** usa **`PROXY.TARGET_SYSTEM`** solo inyección (sin puertos UDP por hotspot en el servidor). | -**MASTER** escucha conexiones PEER; cada peer autenticado se guarda en **`PEERS`** en tiempo de ejecución. +**MASTER** escucha conexiones PEER (salvo que sea el destino **solo inyección** del proxy — ver [Proxy hotspot](hotspot-proxy.md)); cada peer autenticado se guarda en **`PEERS`** en tiempo de ejecución. --- @@ -186,6 +186,28 @@ Detalle: [Monitor e informes](monitoring.md). --- +## `PROXY` (proxy hotspot integrado) + +Se arranca siempre que exista un bloque **`PROXY`** (ver `adn-server.example.yaml`). Los hotspots se conectan a **`LISTEN_PORT`**; el tráfico se inyecta en **`TARGET_SYSTEM`**. Guía completa: [Proxy hotspot](hotspot-proxy.md). + +| Clave | Significado | +|-------|-------------| +| **LISTEN_PORT** / **LISTEN_IP** | Bind UDP para conexiones de hotspots. | +| **TARGET_SYSTEM** | Nombre del **MASTER** que recibe HBP inyectado. Ese system pasa a **solo inyección** (`IP` / `PORT` eliminados al cargar). | +| **TIMEOUT** | Timeout de sesión inactiva (segundos). | +| **DEBUG** / **CLIENT_INFO** | Verbosidad de logs. | +| **BLACK_LIST** / **IP_BLACK_LIST** | Bloqueo de IDs de radio o IPs de cliente. | + +**No** ejecutes **`adn-proxy`** independiente en el mismo **`LISTEN_PORT`** si el proxy integrado está activo. + +--- + +## `SELF_SERVICE` (MySQL / opciones del panel) + +Opcional; requiere `pip install -e ".[selfservice]"` con **`USE_SELFSERVICE: true`**. Usa la misma tabla **`Clients`** y parámetros PBKDF2 que **adn-monitor**. Las claves coinciden con la documentación del monitor — ver [Self-service](../../monitor/self-service.md) y [Proxy hotspot](hotspot-proxy.md#claves-self_service). + +--- + ## `LOGGER` Implementado en `infrastructure/logging_config.py` (`setup_logging`). Los valores se leen del bloque **`LOGGER`** (o `--logging` solo para **LOG_LEVEL**). @@ -247,3 +269,4 @@ Usa el intérprete del proyecto (ver reglas del workspace), p. ej. `python3.11` - [Bridges y talkgroups](bridges-and-talkgroups.md) — semántica de `BRIDGES`. - [Números especiales](special-numbers.md) — TG e IDs reservados. - [Parrot](parrot.md) — ejemplo PEER (proceso parrot). +- [Proxy hotspot](hotspot-proxy.md) — **`PROXY`** / **`SELF_SERVICE`** integrados. diff --git a/docs/es/server/user-guide/hotspot-proxy.md b/docs/es/server/user-guide/hotspot-proxy.md new file mode 100644 index 0000000..ea4d85b --- /dev/null +++ b/docs/es/server/user-guide/hotspot-proxy.md @@ -0,0 +1,120 @@ +# Proxy hotspot (integrado) + +**ADN DMR Peer Server** incluye un **proxy hotspot integrado**: un solo proceso (`adn-server.py`) acepta Homebrew (HBP) de muchos hotspots en un único puerto UDP e **inyecta** el tráfico en un **MASTER** configurado. **No** hace falta un proceso **`adn-proxy`** aparte cuando este modo está activo. + +La configuración está en **`adn-server.yaml`**, bloques **`PROXY`** y opcional **`SELF_SERVICE`** (misma tabla MySQL **`Clients`** que **adn-monitor**). + +--- + +## Cuándo usarlo + +| 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). | + +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). + +--- + +## Dependencia opcional (self-service) + +El self-service con MySQL requiere **`mysqlclient`**: + +```bash +pip install -e ".[selfservice]" +``` + +Si **`USE_SELFSERVICE: true`** pero falta **`mysqlclient`**, el arranque falla con un error claro. Pon **`USE_SELFSERVICE: false`** para usar el proxy sin BD (sin actualizaciones **RPTO** desde el panel). + +--- + +## Claves `PROXY` + +| Clave | Rol | +|-------|-----| +| **LISTEN_PORT** | Puerto UDP al que se conectan los **hotspots** (el que configuran en el dispositivo). | +| **LISTEN_IP** | Dirección de bind; vacío = todas las interfaces. | +| **TARGET_SYSTEM** | Nombre del **MASTER** en **`SYSTEMS`** que recibe el HBP inyectado (debe existir y estar **ENABLED**). | +| **TIMEOUT** | Timeout de sesión inactiva (segundos); las sesiones caducadas se eliminan en el MASTER. | +| **DEBUG** | Log detallado de paquetes. | +| **CLIENT_INFO** | Log de conexión/desconexión por ID de radio. | +| **BLACK_LIST** | Bloquea IDs de radio listados. | +| **IP_BLACK_LIST** | Bloquea IPs origen (con caducidad opcional). | + +En **`PROXY`** integrado **no** hay **`MASTER`**, **`PORT`** ni **`GENERATOR`** — eso corresponde al proxy independiente legado. El MASTER destino usa **`MAX_PEERS`** (no un rango UDP) para limitar hotspots simultáneos. + +Ejemplo (de `adn-server.example.yaml`): + +```yaml +PROXY: + LISTEN_PORT: 62031 + LISTEN_IP: "" + TARGET_SYSTEM: SYSTEM + TIMEOUT: 30 + DEBUG: false + CLIENT_INFO: true + BLACK_LIST: [] + IP_BLACK_LIST: {} +``` + +### MASTER destino solo inyección + +Cuando **`PROXY.TARGET_SYSTEM`** apunta a un system (p. ej. **`SYSTEM`**), al arrancar se **eliminan** **`IP`** / **`PORT`** de ese bloque MASTER. Los hotspots nunca se conectan al puerto de conferencia; todo el HBP entra por **`LISTEN_PORT`**. + +Define **`MAX_PEERS`** en el MASTER destino como máximo de hotspots simultáneos (p. ej. **102**). Otros MASTER (**ECHO**, **D-APRS**, etc.) mantienen **`IP`** / **`PORT`** normales si no son el destino del proxy. + +--- + +## Claves `SELF_SERVICE` + +Misma semántica que **`adn-monitor.yaml`** / **`adn-proxy.yaml`** legado — 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. | + +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. + +Detalle del flujo en el panel: [Self-service](../../monitor/self-service.md). + +--- + +## Comportamiento con varios hotspots + +- Cada hotspot autenticado es un **peer** en el MASTER de inyección con sus **OPTIONS** (TG estáticas). **Repeat** y el fan-out del monitor respetan **OPTIONS por peer** — el tráfico de un TG no se envía a peers que no lo tienen seleccionado. +- Los talkgroups **parrot / eco 9990–9999** omiten el filtro OPTIONS y vuelven al hotspot **llamante** (ver [Números especiales](special-numbers.md)). + +--- + +## Recarga en caliente (`SIGHUP`) + +**Se aplica sin reiniciar** (las sesiones activas del proxy se mantienen): + +- **`PROXY`:** **TIMEOUT**, **DEBUG**, **CLIENT_INFO**, **BLACK_LIST**, **IP_BLACK_LIST** +- **`SELF_SERVICE`:** se fusiona en config (cambios de credenciales en nuevas operaciones BD; los bucles no se reinician en reload) + +**Requiere reinicio completo del proceso:** + +- **`PROXY.LISTEN_PORT`** / **`LISTEN_IP`** (el cambio de bind se registra y se ignora en reload) +- **`PROXY.TARGET_SYSTEM`** +- Activar o desactivar **`USE_SELFSERVICE`** tras el arranque + +Ver [Configuración — recarga en caliente](configuration.md#recarga-en-caliente-adn-serveryaml). + +--- + +## 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`**. diff --git a/docs/es/server/user-guide/introduction.md b/docs/es/server/user-guide/introduction.md index d258b21..5b8e64a 100644 --- a/docs/es/server/user-guide/introduction.md +++ b/docs/es/server/user-guide/introduction.md @@ -21,16 +21,19 @@ Enrutado, temporizadores, control de bucle OpenBridge y manejo de protocolo est | **Protocolo HBP** | Autenticación, ingreso/salida DMRD, repetición a peers, filtros TG. | | **OpenBridge** | Ingreso DMRE, límite de saltos, control de bucle (`min(1ST)`), BCSQ/BCKA si están habilitados. | | **Voz** | Ficheros AMBE, anuncios programados, tubería TTS, reproducción bajo demanda (TG 9991–9999). | -| **Informes** | Canal TCP netstring hacia **adn-monitor** (y paneles compatibles): config, estado de bridges, eventos de llamada `BRDG_EVENT`. | +| **Informes** | Canal TCP netstring hacia **adn-monitor** (y paneles compatibles): config, estado de bridges, eventos de llamada (informe v2 JSON). | +| **Proxy hotspot** | Fan-in UDP integrado opcional (`PROXY` en `adn-server.yaml`) y **self-service** MySQL (`SELF_SERVICE`) para opciones de hotspot desde el panel. | ## 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). ## Siguientes pasos -- [Configuración](configuration.md) — ficheros, `GLOBAL`, **MASTER** / **PEER** / **OPENBRIDGE**, ACL, informes, alias, fusión de voz. +- [Configuración](configuration.md) — ficheros, `GLOBAL`, **MASTER** / **PEER** / **OPENBRIDGE**, ACL, informes, **`PROXY`**, **`SELF_SERVICE`**, alias, fusión de voz. - [Bridges y talkgroups](bridges-and-talkgroups.md) — cómo funciona `BRIDGES`. - [Números especiales](special-numbers.md) — TG 4000, servicios de información, eco. -- [ADN Monitor](../../monitor/index.md) — panel, `adn-monitor.yaml`, self-service (repo aparte, desplegado con el servidor). +- [Proxy hotspot](hotspot-proxy.md) — **`PROXY`** / **`SELF_SERVICE`** integrados en `adn-server.yaml`. +- [ADN Monitor](../../monitor/index.md) — panel, `adn-monitor.yaml`, UI self-service (repo aparte, desplegado con el servidor). - [Créditos y licencia](attribution.md) — ADN → FreeDMR → hblink3, licencia. diff --git a/docs/es/server/user-guide/monitoring.md b/docs/es/server/user-guide/monitoring.md index 0cd808c..3c80e2c 100644 --- a/docs/es/server/user-guide/monitoring.md +++ b/docs/es/server/user-guide/monitoring.md @@ -50,8 +50,8 @@ 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`** (ver `adn-server.example.yaml`) | -| **`adn-proxy`** | **`LOG.PATH`** + **`LOG.LOG_FILE`** en `adn-proxy.yaml` | +| **`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 +71,7 @@ Ejemplo de fragmento en **`/etc/logrotate.d/adn`** (adaptar rutas y nombres de u } ``` -Repite **`postrotate`** con **`kill -USR2`** para las unidades **`adn-parrot`**, **`adn-proxy`** y **`adn-monitor`** si rotas sus logs en el mismo host. 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. 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). ## Requisitos @@ -80,4 +80,6 @@ Repite **`postrotate`** con **`kill -USR2`** para las unidades **`adn-parrot`**, ## Self-service y hotspots -Los operadores que editan **opciones de dispositivo** desde el panel usan el flujo **self-service** (MySQL **`Clients`**, proxy **RPTO**). Está documentado en [Self-service](../../monitor/self-service.md); **no** forma parte solo del binario del peer server. Para la configuración del **proxy hotspot** (`PROXY` en **`adn-proxy.yaml`** por defecto), enlace al rango **UDP** del peer server y arranque del proceso, ver [Proxy hotspot](../../monitor/hotspot-proxy.md). +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`**. diff --git a/mkdocs.es.yml b/mkdocs.es.yml index 964b21a..14cead4 100644 --- a/mkdocs.es.yml +++ b/mkdocs.es.yml @@ -56,6 +56,7 @@ nav: - Llamadas privadas: server/user-guide/private-calls.md - Voz, anuncios y TTS: server/user-guide/voice-and-tts.md - Monitor e informes: server/user-guide/monitoring.md + - Proxy hotspot (integrado): server/user-guide/hotspot-proxy.md - Parrot (reproducción): server/user-guide/parrot.md - Créditos y licencia: server/user-guide/attribution.md - Protocolos: diff --git a/mkdocs.yml b/mkdocs.yml index bc59f52..994521a 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -56,6 +56,7 @@ nav: - Private calls: server/user-guide/private-calls.md - Voice, announcements, and TTS: server/user-guide/voice-and-tts.md - Monitoring and reports: server/user-guide/monitoring.md + - Hotspot proxy (integrated): server/user-guide/hotspot-proxy.md - Parrot (playback): server/user-guide/parrot.md - Credits & license: server/user-guide/attribution.md - Protocols: