From a3e99a2da9aa7f870bf41f4fb2adfa7e646cb269 Mon Sep 17 00:00:00 2001 From: Cort Buffington Date: Sun, 12 Jul 2026 13:42:08 -0500 Subject: [PATCH] docs: refresh for the SERVER/OUTBOUND rename, OBP_BRIDGES, and Unix-socket feed Documentation audit for the recent changes: * MODE terminology MASTER->SERVER, PEER/CLIENT->OUTBOUND corrected across README, INSTALL, CONFIGURING, ACLS, and the dashboard docs (the code only accepts SERVER/OUTBOUND/OPENBRIDGE, so the old names in docs were wrong). * README gains a top-of-file breaking-change warning (OBP routing moved to OBP_BRIDGES; system-mode rename) with a pointer to the migration tool. * CONFIGURING documents REPORT_TRANSPORT/REPORT_SOCKET (Unix-socket feed) and a "Migrating from the old inline form" section for tools/migrate_obp_rules.py. * dashboard/README documents HBLINK_TRANSPORT/HBLINK_SOCKET and corrects the stale "changes appear on the next snapshot" claim (connect/disconnect are granular pushes now). Co-Authored-By: Claude Opus 4.8 --- ACLS.md | 8 ++++---- CONFIGURING.md | 26 ++++++++++++++++++++------ INSTALL.md | 11 ++++++----- README.md | 14 ++++++++++---- dashboard/FIELDS.md | 14 +++++++------- dashboard/README.md | 18 ++++++++++++------ 6 files changed, 59 insertions(+), 32 deletions(-) diff --git a/ACLS.md b/ACLS.md index 56017c8..2dcff43 100644 --- a/ACLS.md +++ b/ACLS.md @@ -79,7 +79,7 @@ HBlink3 applies four kinds of ACL. Each is a separate config key. | Key | Gates | Value domain | Notes | |-----|-------|--------------|-------| -| `REG_ACL` | **Repeater/peer registration** (login) | peer IDs, `1 … 4294967295` | Only meaningful on **server (master)** systems. Peers/OpenBridge don't register. **Always enforced** — see §5. | +| `REG_ACL` | **Repeater/peer registration** (login) | peer IDs, `1 … 4294967295` | Only meaningful on **SERVER** systems. Outbound/OpenBridge links don't accept registrations. **Always enforced** — see §5. | | `SUB_ACL` | **Subscriber** (the source radio ID of a call) | subscriber IDs, `1 … 16776415` | Applied to every voice/data stream. | | `TGID_TS1_ACL` | **Destination talkgroup on Timeslot 1** | talkgroup IDs, `1 … 16776415` | | | `TGID_TS2_ACL` | **Destination talkgroup on Timeslot 2** | talkgroup IDs, `1 … 16776415` | | @@ -200,11 +200,11 @@ TGID_TS1_ACL: PERMIT:2,9,3120 TGID_TS2_ACL: PERMIT:3120,31205 ``` -**Registration control on a master.** Accept only two specific repeater IDs onto -this master; reject all other login attempts: +**Registration control on a server.** Accept only two specific repeater IDs onto +this server; reject all other login attempts: ```ini -[MASTER-1] +[SERVER-1] MODE: SERVER REG_ACL: PERMIT:311111,311222 ``` diff --git a/CONFIGURING.md b/CONFIGURING.md index 3db7dfe..26d7e4b 100644 --- a/CONFIGURING.md +++ b/CONFIGURING.md @@ -4,7 +4,7 @@ HBlink3's conference bridge (`bridge.py`) is driven by two files: | File | What it defines | |---|---| -| `hblink.cfg` | The program itself: global timers, reporting, logging, alias lookups, and one stanza per **system** (a Server you host, a Client you dial out as, or an OpenBridge link). | +| `hblink.cfg` | The program itself: global timers, reporting, logging, alias lookups, and one stanza per **system** (a Server you host, an Outbound system you dial out as, or an OpenBridge link). | | `rules.py` | The **routing**: which systems talk to which, on what timeslot/talkgroup, and the talkgroup triggers/timers that turn bridges on and off. | This document is the full field reference. The sample files (`hblink-SAMPLE.cfg`, `rules_SAMPLE.py`) carry only terse inline notes and point back here. @@ -22,12 +22,12 @@ INI format (`configparser`). Section names in `[BRACKETS]`; `KEY: value` pairs. | Field | Meaning | |---|---| | `PATH` | Working directory for runtime files. `./` (the program's own directory) is normal. | -| `PING_TIME` | Seconds between keepalive pings on Server/Client connections. | +| `PING_TIME` | Seconds between keepalive pings on Server and Outbound connections. | | `MAX_MISSED` | Missed pings before a connection is declared dead and torn down. | | `PING_LOSS_WINDOW` | Optional (default `5`, range `1`–`60`). Minutes over which the dashboard measures each repeater's **ping loss** — the share of expected keepalive pings that went missing. Larger = smoother / longer memory. Surfaces a repeater that stays connected but has a lossy link (choppy audio). A repeater that merely pings less often is not counted as lossy. | | `PING_LOSS_WARN` | Optional (default `5`). Ping-loss **percent** at or above which the dashboard turns a repeater's callsign gold. `0` disables the flag. | | `USE_ACL` | `True`/`False` top-level on/off switch for the **global** ACLs below. System-level ACLs are independent of this. | -| `REG_ACL` | Registration ACL — which Client **radio IDs** may log in to any Server you host. ACL syntax below. | +| `REG_ACL` | Registration ACL — which **repeater radio IDs** may log in to any Server you host. ACL syntax below. | | `SUB_ACL` | Subscriber ACL — which **subscriber IDs** (the person keying up) may pass traffic, globally. | | `TGID_TS1_ACL` / `TGID_TS2_ACL` | Talkgroup ACLs applied to timeslot 1 / timeslot 2 traffic, globally. | @@ -42,7 +42,7 @@ DENY:1-5,3120101 # block the range 1–5 and the single ID 3120101 PERMIT:3100-3199,9 # allow only this range and ID 9; deny everything else ``` -A global ACL and a system ACL both apply — traffic must pass **both**. Registration ACLs are only meaningful for `SERVER` systems (a Client or OpenBridge link never registers). See **[ACLS.md](ACLS.md)** for the full ACL model — PERMIT/DENY precedence, how ranges are matched, and worked examples. +A global ACL and a system ACL both apply — traffic must pass **both**. Registration ACLs are only meaningful for `SERVER` systems (an Outbound or OpenBridge link never accepts registrations). See **[ACLS.md](ACLS.md)** for the full ACL model — PERMIT/DENY precedence, how ranges are matched, and worked examples. ## `[REPORTS]` @@ -52,8 +52,12 @@ Feeds the real-time dashboard. See [`dashboard/`](dashboard/). |---|---| | `REPORT` | `True`/`False` — emit the event stream at all. | | `REPORT_INTERVAL` | Seconds between periodic status reports. | -| `REPORT_PORT` | TCP port the report service listens on. | -| `REPORT_CLIENTS` | Comma-separated client IPs allowed to connect (e.g. `127.0.0.1`). | +| `REPORT_TRANSPORT` | Optional (default `tcp`). `tcp` = listen on a TCP port (dashboard may be remote); `unix` = listen on a local Unix-domain socket instead. A same-box dashboard on a Unix socket can't be broken by a NIC flap or connection-tracking eviction, so use `unix` when the dashboard runs on the same host. | +| `REPORT_PORT` | TCP port the report service listens on (when `REPORT_TRANSPORT` = `tcp`). | +| `REPORT_SOCKET` | Filesystem path for the listening socket (when `REPORT_TRANSPORT` = `unix`), e.g. `/tmp/hblink3-report.sock`. Access is governed by filesystem permissions; `REPORT_CLIENTS` is not used on a Unix socket. | +| `REPORT_CLIENTS` | Comma-separated client IPs allowed to connect (e.g. `127.0.0.1`) — TCP only. | + +> The dashboard has matching `HBLINK_TRANSPORT` (`tcp`/`unix`) and `HBLINK_SOCKET` settings — see [dashboard/README.md](dashboard/README.md). Point the dashboard at the same transport the daemon serves. ## `[LOGGER]` @@ -239,6 +243,16 @@ Read one row as **"on this OpenBridge, this TGID *is* this bridge."** That singl `OBP_BRIDGES` is optional — omit it (or leave it `{}`) if you run no OpenBridges. +### Migrating from the old inline form + +Earlier HBlink3 configured OpenBridge systems as inline `BRIDGES` members. To convert an existing `rules.py`, run the migration tool from the repo root: + +```bash +python tools/migrate_obp_rules.py --config hblink.cfg --rules rules.py --out rules.py.migrated +``` + +It reads `hblink.cfg` to learn which systems are `OPENBRIDGE`, lifts each inline OBP member into `OBP_BRIDGES`, prints a summary of what moved, flags any ingress-fork **ERROR** or renumber **WARNING**, and writes a new file (it never overwrites your `rules.py`). Review the output — comments from the original are not carried over — then replace `rules.py` with it. + ## `UNIT` ```python diff --git a/INSTALL.md b/INSTALL.md index 76e0793..ab59991 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -38,11 +38,11 @@ Edit `hblink.cfg`: - **[LOGGER]** — log file, handlers, and level. - **[ALIASES]** — optional download of repeater/subscriber ID files from radioid.net. - **System stanzas** — one per connection. Set `MODE` to: - - `MASTER` — accept incoming repeaters/hotspots - - `PEER` — connect outward to another master + - `SERVER` — accept incoming repeaters/hotspots (formerly `MASTER`) + - `OUTBOUND` — connect outward to another server (formerly `PEER`/`CLIENT`) - `OPENBRIDGE` — link to Brandmeister / DMR+ (IPSC2) - Duplicate a stanza for each system; the stanza name (e.g. `[MASTER-1]`) must be unique. + Duplicate a stanza for each system; the stanza name (e.g. `[SERVER-1]`) must be unique. If you are running `bridge.py`, also create the rules file: @@ -53,9 +53,10 @@ cp rules_SAMPLE.py rules.py `rules.py` defines: - **`BRIDGES`** — each conference bridge and the system / timeslot / talkgroup that belongs to it, with optional ON/OFF/RESET trigger talkgroups and timeouts. +- **`OBP_BRIDGES`** — OpenBridge (`OPENBRIDGE`) systems only: a per-OBP `{bridge: TGID}` table (not inline `BRIDGES` members). See [CONFIGURING.md](CONFIGURING.md#obp_bridges). - **`UNIT`** — the systems permitted to exchange private (unit) calls. -Every system named in `rules.py` must exist and be enabled in `hblink.cfg`. +Every system named in `rules.py` must exist and be enabled in `hblink.cfg`. **Upgrading from an older HBlink3?** OpenBridge members used to live inline in `BRIDGES`; convert your file with `python tools/migrate_obp_rules.py` (see [CONFIGURING.md](CONFIGURING.md#obp_bridges)). ## 3. Run @@ -65,7 +66,7 @@ python bridge.py -c hblink.cfg -r rules.py # conference-bridge router (main # or python bridge_all.py -c hblink.cfg # forward-everything proxy # or -python hblink.py -c hblink.cfg # protocol core, standalone master/peer +python hblink.py -c hblink.cfg # protocol core, standalone server/outbound ``` `-c` / `-r` default to `hblink.cfg` and `rules.py` in the program directory. `-l LEVEL` overrides the configured log level. diff --git a/README.md b/README.md index 52b9aec..b142e2b 100755 --- a/README.md +++ b/README.md @@ -4,17 +4,23 @@ HBlink3 is an open-source implementation of the DMR **HomeBrew Repeater Protocol > **Which HBlink?** HBlink3 is "system-oriented" and built for **transit routing and conference bridging** between networks. Its companion, **HBlink4** (by the same author), is a repeater-oriented **endpoint server** for running a single end-point network with no granudlar transit routing. Pick the one that matches your role. +> ⚠️ **Upgrading? Two breaking config changes to know about.** +> 1. **OpenBridge routing moved** — `MODE: OPENBRIDGE` systems are no longer inline `BRIDGES` members in `rules.py`; they now live in a dedicated **`OBP_BRIDGES`** table. Old configs stop with a clear startup error. Run **`python tools/migrate_obp_rules.py`** to convert your `rules.py` automatically, or see [CONFIGURING.md](CONFIGURING.md#obp_bridges). +> 2. **System modes renamed** — `MASTER`→**`SERVER`** and `PEER`/`CLIENT`→**`OUTBOUND`** (`OPENBRIDGE` unchanged). Update the `MODE:` line of each stanza in `hblink.cfg`. +> +> Your `hblink.cfg`/`rules.py` are git-ignored, so a `git pull` won't touch them — but the new code won't accept the old values. Non-OpenBridge configs need only change (2). + ## Applications | Program | Purpose | |---|---| | `bridge.py` | The main application: a configurable conference-bridge call router with dynamic on/off triggering, timeouts, and private (unit) call routing. | | `bridge_all.py` | A simple proxy that forwards all traffic between every configured system — makes several repeaters appear as one. | -| `hblink.py` | The protocol core. Runs standalone as a master/peer for testing, and is the module the applications are built on. | +| `hblink.py` | The protocol core. Runs standalone as a server/outbound system for testing, and is the module the applications are built on. | ## Features -- HBP **server** (master) and **client** (peer) modes for MMDVM repeaters and hotspots +- HBP **server** and **outbound** (client) modes for MMDVM repeaters and hotspots - **OpenBridge** connectivity to other transit call routers, including Brandmeister / DMR+ (IPSC2) - Rules-based **conference-bridge routing** with dynamic ON/OFF/RESET talkgroup triggers and timeouts, including the ability to use TGIDs as in-band signalling to trigger dynamic connections -- sometimes referred to as "dial-a-talkgroup". - **Private (unit) call** routing using a learned subscriber-to-system map that only floods systems configured for unit calls, and only until the target unit's system is located. It then prunes re-transmission to the system with the target unit. @@ -38,7 +44,7 @@ See **[INSTALL.md](INSTALL.md)** for full setup, configuration, and running as a ## Configuration -- **`hblink.cfg`** — global settings, reporting, logging, alias downloads, and one stanza per system (`MASTER`, `PEER`, or `OPENBRIDGE`). Start from `hblink-SAMPLE.cfg`. +- **`hblink.cfg`** — global settings, reporting, logging, alias downloads, and one stanza per system (`SERVER`, `OUTBOUND`, or `OPENBRIDGE`). Start from `hblink-SAMPLE.cfg`. - **`rules.py`** — (`bridge.py` only) defines the conference bridges, the systems/talkgroups/timeslots that belong to each, and the systems permitted to pass unit calls. Start from `rules_SAMPLE.py`. Your `hblink.cfg` and `rules.py` are git-ignored, so `git pull` won't overwrite them. @@ -79,7 +85,7 @@ used. Talkgroup IDs not present in the file are displayed as their raw numeric v ## Dashboard A real-time web dashboard lives in [`dashboard/`](dashboard/) — a separate program -with a modern dark UI showing master/peer/OpenBridge systems, conference-bridge +with a modern dark UI showing server/outbound/OpenBridge systems, conference-bridge state, and a live call log. Enable HBlink3's reporting feed (`[REPORTS]` in `hblink.cfg`) and see [dashboard/README.md](dashboard/README.md) to run it. the dashboard is kept separate to keep performance of the forwarding hot path free from unecessary diff --git a/dashboard/FIELDS.md b/dashboard/FIELDS.md index 8e52a7c..7cec33b 100644 --- a/dashboard/FIELDS.md +++ b/dashboard/FIELDS.md @@ -39,16 +39,16 @@ and its state is remembered in your browser. --- -## Master Systems +## Server Systems -One block per HBlink3 **MASTER** instance (repeaters/hotspots connect *to* it). +One block per HBlink3 **SERVER** instance (repeaters/hotspots connect *to* it). **Block heading** | Element | Meaning | |---|---| -| System name | The master's name from `hblink.cfg` (e.g. `MASTER-1`). | -| `repeat` / `isolate` pill | `repeat` if the master forwards traffic between its own peers (`REPEAT: True`), `isolate` if it does not. | +| System name | The server's name from `hblink.cfg` (e.g. `SERVER-1`). | +| `repeat` / `isolate` pill | `repeat` if the server forwards traffic between its own repeaters (`REPEAT: True`), `isolate` if it does not. | | *N* peer(s) | How many repeaters/hotspots are currently logged in. | | Show/Hide Connections | Collapses this system's connection table. When collapsed, system-level TS1/TS2 activity pills replace it so live calls stay visible. Default from `SERVER_REPEATERS`; per-system state is remembered in your browser. | @@ -64,10 +64,10 @@ One block per HBlink3 **MASTER** instance (repeaters/hotspots connect *to* it). --- -## Peer Systems +## Outbound Systems -One row per HBlink3 **PEER** instance (HBlink3 connecting *out* to an upstream -master, e.g. another HBlink or a BrandMeister/IPSC2 server). +One row per HBlink3 **OUTBOUND** instance (HBlink3 connecting *out* to an upstream +server, e.g. another HBlink or a BrandMeister/IPSC2 server). | Column | Meaning | |---|---| diff --git a/dashboard/README.md b/dashboard/README.md index 09463c4..501ba80 100644 --- a/dashboard/README.md +++ b/dashboard/README.md @@ -14,13 +14,13 @@ full-table refreshes, and no pickle on the wire. - **Last Heard** — the most-recent unique callers (user, call type, source repeater, timeslot, talkgroup, and when), derived live from the call log. The table is collapsible; its open/closed state is remembered in your browser. -- **Master Systems** — each master and its connected repeaters (ID, callsign, +- **Server Systems** — each server and its connected repeaters (ID, callsign, location, IP, uptime, frequency, colorcode, slots) with the live call on each timeslot (source → destination, color-coded by direction). Each server's connection list can be collapsed with the **Show/Hide Connections** button; when collapsed, system-level TS1/TS2 activity pills replace the table so you still see what's talking. State is remembered per-browser. -- **Peer Systems** — outbound peer connections with health (connection state, +- **Outbound Systems** — outbound connections with health (connection state, pings sent/ackd/lost) and live per-timeslot calls. - **OpenBridge Systems** — network ID, target, and active streams. - **Conference Bridges** — each bridge's members: timeslot, talkgroup, @@ -45,7 +45,11 @@ Open `http://:8080`. ## Configuration (`config.py`) - `HBLINK_IP` / `HBLINK_PORT` — HBlink3's reporting feed (the `[REPORTS]` section - of `hblink.cfg`: `REPORT_PORT`, default 4321). + of `hblink.cfg`: `REPORT_PORT`, default 4321). Used when `HBLINK_TRANSPORT` is `tcp`. +- `HBLINK_TRANSPORT` / `HBLINK_SOCKET` — optional. Set `HBLINK_TRANSPORT = 'unix'` + and `HBLINK_SOCKET` to the daemon's `REPORT_SOCKET` path to connect over a local + Unix socket instead of TCP (same-box dashboard; immune to NIC flaps / conntrack + eviction). Defaults to `tcp`. Must match the daemon's `REPORT_TRANSPORT`. - `WEB_HOST` / `WEB_PORT` — where the dashboard listens. - `PATH` / `*_FILE` — alias files mapping DMR IDs to callsigns/talkgroup names (the same files HBlink3 uses; point `PATH` at HBlink3's directory to share them). @@ -64,9 +68,11 @@ In `hblink.cfg`, under `[REPORTS]`: - `REPORT: True` - `REPORT_PORT` must match the dashboard's `HBLINK_PORT` - `REPORT_CLIENTS` must include the dashboard host's IP (or `*`) -- `REPORT_INTERVAL` sets how often the systems/bridge snapshot is refreshed. - **Live calls are pushed instantly**; connection/peer-state changes appear on the - next snapshot, so `10` is a good value for dashboard use. +- `REPORT_INTERVAL` sets how often the full systems/bridge snapshot is resent as a + slow resync. **Live calls and repeater connect/disconnect are pushed as they + happen** (granular events); the periodic snapshot is just a safety-net resync and + also carries the per-repeater ping-quality figures, so a larger value like `60` + is fine. ## Notes