# rrcd `rrcd` is a standalone RRC hub daemon (server) built on Reticulum (RNS). - License: MIT (see LICENSE) - Changelog: CHANGELOG.md - Versioning policy: VERSIONING.md ## Install (dev) From the `rrcd/` directory: To install from source (non-editable): - `python -m pip install .` - `python -m pip install -e .` For contributors (lint + tests): - `python -m pip install -e ".[dev]"` - `ruff check .` - `pytest -q` ## Run To run a basic RRC hub with default settings: - `rrcd` You can also run it as a module: - `python -m rrcd` First run will create a default config file at `~/.rrcd/rrcd.toml` and a default identity at `~/.rrcd/hub_identity`, plus a room registry at `~/.rrcd/rooms.toml`. You should read and edit the config before starting again. To override the default state directory (`~/.rrcd/`), set `RRCD_HOME`, e.g. `RRCD_HOME=/tmp/rrcd rrcd`. To specify a custom identity and destination name: - `rrcd --identity ~/.rrcd/hub_identity --dest-name rrc.hub` Optional: - `rrcd --config rrcd.toml` You need a working Reticulum configuration (see Reticulum docs). ## Logging By default, `rrcd` logs to stderr (good for systemd/journald). You can configure logging in `~/.rrcd/rrcd.toml`: ```toml [logging] level = "INFO" # set to DEBUG for connection/packet tracing rns_level = "WARNING" # python-logging level for the "RNS" logger (if used) console = true file = "" # e.g. "~/.rrcd/rrcd.log" (empty disables) format = "%(asctime)s %(levelname)s %(name)s[%(threadName)s]: %(message)s" datefmt = "" ``` CLI overrides are also available: - `rrcd --log-level DEBUG` - `rrcd --log-file ~/.rrcd/rrcd.log` If you use `/reload`, logging settings are applied immediately. ### Troubleshooting: connect times out after HELLO If a client connects, sends `HELLO`, and then times out waiting for `WELCOME`, check the hub logs for an error like: - `Packet size of ... exceeds MTU of ... bytes` This usually means the hub tried to send a `WELCOME` (or other message) that is too large for the current Reticulum link MTU. Mitigations: - `WELCOME` sent by `rrcd` is intentionally minimal (hub name/version/caps). - The hub `greeting` is delivered after `WELCOME` via one or more `NOTICE` messages chunked to fit the link MTU. ## Compatibility `rrcd` implements the core RRC protocol as described in the RRC docs. Protocol alignment notes (for implementers): - `HELLO` and `WELCOME` bodies are CBOR maps with unsigned integer keys. - Capabilities are carried in body key `2` as a CBOR map (not a bitmask). Keys inside the capabilities map are unsigned integers; values are advisory. - `WELCOME` is intentionally minimal (hub name/version/caps only). Any hub greeting text is delivered after `WELCOME` via one or more `NOTICE` messages. Extensions beyond core RRC will be documented in the Extensions section of this README as they are added. In addition to the core protocol, `rrcd` includes operator-facing and policy-level features that are allowed by the spec: - **First-run bootstrap**: if the default config and identity are missing, `rrcd` will create them and exit with a note asking you to edit the config before starting again. - **Rate limiting**: per-link message rate limiting may reject messages with `ERROR`. - **Room and input limits**: limits such as maximum rooms per session and maximum room name length. - **Optional `JOINED` member list**: can include a best-effort list of members. - **Optional hub-initiated `PING`**: can periodically ping clients and optionally close links that do not respond in time. ## Extensions `rrcd` intentionally avoids adding new on-wire message types. Operator features use a hub-local convention: if a client sends a `MSG`/`NOTICE` whose body is a string beginning with `/`, and the command is recognized, the hub treats it as a command and does not forward it. Wire-level extensions (backwards-compatible): - **Optional envelope nickname**: the hub may include an additional envelope key `K_NICK = 7` (string) when forwarding `MSG`/`NOTICE`. This is an optional hint associated with `K_SRC` so clients can display a human-friendly nickname instead of only the sender identity hash. The hub learns this value from the client's optional envelope nickname field `K_NICK = 7` on inbound messages, and treats it as the authoritative nickname for that link. Clients should treat `K_NICK` as optional and fall back to `K_SRC` when it is missing. Nicknames are advisory only; clients should treat them as display hints. The hub may ignore, sanitize, replace, or omit them. Current hub sanitation policy: store/emit only trimmed nicknames that are UTF-8 encodable, contain no newlines/NUL, and are at most `nick_max_chars` characters (default: 32). Configure trusted operators and banned identities in the TOML config: - `trusted_identities`: list of Reticulum Identity hashes (hex) allowed to run commands - `banned_identities`: list of Identity hashes (hex) that are disconnected on identify Implemented commands (best-effort): Server operator commands (require identity in `trusted_identities`): - `/stats` — show hub stats (uptime, clients, rooms, counters) - `/reload` — reload `rrcd.toml` and `rooms.toml` from disk - `/who [room]` — list members (nick and/or hash prefix) - `/kline add ` — add a server-global ban (persists to `banned_identities`) - `/kline del ` — remove a server-global ban (persists to `banned_identities`) - `/kline list` — list global bans Room moderation commands (room founder/ops; some actions may also work for server operators): - `/kick ` — remove a client from a room - `/register ` — persist room settings to `rooms.toml` (founder only; must be in the room) - `/unregister ` — remove room settings from `rooms.toml` (founder only; must be in the room) - `/topic [topic]` — show or set a room topic - `/mode (+m|-m)` — set moderated mode - `/mode (+i|-i)` — set invite-only mode - `/mode (+k|-k) [key]` — set/clear room key (password) - `/mode (+t|-t)` — set topic-ops-only (only ops can change topic) - `/mode (+n|-n)` — set no-outside-messages - `/mode (+r|-r)` — read-only; use /register or /unregister - `/mode (+o|-o|+v|-v) ` — IRC-style user modes (alias for op/voice) - `/op ` / `/deop ...` — grant/revoke room operator - `/voice ` / `/devoice ...` — grant/revoke voice (for moderated rooms) - `/ban add ` — add a room-local ban - `/ban del ` — remove a room-local ban - `/ban list` — list room-local bans - `/invite add ` — send a room invite (as a `NOTICE` to the target) - `/invite del ` — remove a room-local invite - `/invite list` — list room-local invites Notes: - On successful JOIN, the hub sends a follow-up `NOTICE` to the joining client with room info (registered/unregistered, mode flags, and topic). - When a room is registered, default mode flags are `+nrt`. - `/invite` always sends the target a `NOTICE` (and fails if the target is not currently connected). - If the room has join restrictions, the hub also records an expiring invite so the target can actually use it to join: - `+i` (invite-only): only an invite allows a user to JOIN. - `+k` (keyed): an invite allows a user to JOIN without knowing the key. The key can then be disseminated in-band (in room) if desired. These stored invites are consumed on successful JOIN or discarded when they expire. Configure the expiry with `room_invite_timeout_s` in `rrcd.toml`. - Registered-but-empty rooms may be pruned after a period of inactivity. Configure `room_registry_prune_after_s` and `room_registry_prune_interval_s` in `rrcd.toml`. ## rooms.toml format The room registry file (`~/.rrcd/rooms.toml` by default) is a TOML document with a top-level `[rooms]` table. Each registered room is stored under a per-room table. Example: - `[rooms."lobby"]` Supported keys per room: - `founder`: hex Reticulum Identity hash (string) - `topic`: room topic (string, optional) - `moderated`: whether the room is in +m (bool) - `invite_only`: whether the room is in +i (bool) - `topic_ops_only`: whether the room is in +t (bool) - `no_outside_msgs`: whether the room is in +n (bool) - `key`: room key/password for +k (string, optional) - `operators`: list of identity hashes (strings) - `voiced`: list of identity hashes (strings) - `bans`: list of identity hashes (strings) - `invited`: table mapping identity hash (hex string) -> expiry unix timestamp seconds (float) - `last_used_ts`: unix timestamp seconds (float; used for pruning) Note: room names are TOML keys. Quote room names that contain spaces or other non-identifier characters, e.g. `[rooms."my room"]`. ## Security and threat model This section describes what `rrcd` is designed to protect against, what it is *not* designed to protect against, and the assumptions you should keep in mind when deploying it. Assumptions: - Reticulum link establishment and remote identity are authoritative for who a peer “is” (the hub uses the Link’s remote identity hash as the peer identity). - The host running `rrcd` is trusted by the operator (if the host is compromised, the hub and its policy controls are compromised). What `rrcd` aims to protect against: - **Unauthenticated pre-handshake traffic**: inbound packets are ignored until the Link’s remote identity is available. - **Protocol misuse**: clients must `HELLO` before they can perform other actions (WELCOME gating). - **Accidental resource exhaustion**: basic per-link rate limiting and input limits (rooms per session, room name length). - **Basic abuse controls**: operator identities, global bans (`/kline`), and per-room bans/modes. What `rrcd` does *not* protect against (non-goals): - **Denial of service by a determined attacker**: rate limiting is best-effort and does not prevent all forms of DoS. - **A malicious or compromised operator**: identities in `trusted_identities` can enforce policy; they can also abuse that power. - **Metadata/privacy leakage outside the hub’s control**: your threat model depends on Reticulum’s transport and your network topology. - **Confidentiality against the hub itself**: the hub can observe and forward traffic; do not treat it as a “zero trust” component. Operational guidance: - Keep the hub identity file and config directory private (the default storage directory is `~/.rrcd/`). - Treat `trusted_identities` like admin keys. - Prefer running `rrcd` under a dedicated OS user with locked-down permissions, aka “least privilege” principle. TL;DR: don’t run it as root.