skip to content

Runtime, Reloading & Systemd

Dynamic credential, CA bundle & peers reloading

Section titled “Dynamic credential, CA bundle & peers reloading”

When federation is configured (--peers-file, --fed-cert, --fed-key), xmsg serve reloads certificates, CA bundles, and peers.json without dropping active connections:

  • Periodic SHA-256 polling & SIGHUP: Every --fed-reload-interval-secs (default 30 seconds, XMSG_FED_RELOAD_INTERVAL_SECS) — or immediately upon receiving SIGHUP — xmsg checks the SHA-256 content hashes of --fed-cert, --fed-key, --peers-file, and each peer’s ca bundle. Content hashing detects atomic symlink swaps (such as Kubernetes ..data Secret rotations) even when symlink mtime does not change.
  • Atomic swap & fail-closed validation: xmsg builds and validates the complete new TLS acceptor, client credentials, private key match, peer map, and CA trust anchors before swapping. If any file is invalid or mismatched, the reload is aborted, an error is logged, and the previous active configuration stays in service uninterrupted.
  • Per-request policy re-evaluation: Existing HTTP/1.1 keep-alive connections on /fed/v2/* re-verify peer membership (current_peers()) and source-IP CIDR policy (from) on every request. In-flight long-polls complete on their pre-reload snapshot (bounded by the 30–60 s wait timeout) before re-evaluating on the next poll.

xmsg serve supports Type=notify-reload (ReloadSignal=SIGHUP) and socket activation under systemd (src/systemd.rs):

  • Socket activation (LISTEN_FDS, LISTEN_PID, LISTEN_FDNAMES): When LISTEN_PID matches xmsg serve’s PID and LISTEN_FDS >= 1, xmsg adopts inherited descriptors starting at FD 3 (SD_LISTEN_FDS_START) and marks each FD_CLOEXEC. Descriptors are matched by canonical LISTEN_FDNAMES slot names:
    • fed (--fed-listen)
    • listen (--listen)
    • agent (--agent-sock)
    • register (--register-sock / --leaf-register-sock)
    • http (--http-sock) When LISTEN_FDS=1 and LISTEN_FDNAMES is unset, the single descriptor is assigned to the sole configured TCP listener (--fed-listen or --listen). Any unknown, duplicate, or unconfigured slot, non-stream/non-listening socket, or bound address/path mismatch fails closed at startup.
  • Readiness & reload notifications (NOTIFY_SOCKET):
    • Sends READY=1 only after all configured listeners are bound (or adopted) and accepting connections.
    • Brackets every post-startup reload with RELOADING=1\nMONOTONIC_USEC=<usec> followed by READY=1 (or READY=1\nSTATUS=reload failed: <reason> when a reload fails and retains the previous configuration, or READY=1\nSTATUS=nothing to reload when SIGHUP arrives without federation configured).
    • Sends STOPPING=1 when SIGTERM or SIGINT initiates graceful shutdown.

The SQLite database (--db-path, default $XDG_STATE_HOME/xmsg/xmsg.db or ~/.local/state/xmsg/xmsg.db) records its schema version in PRAGMA user_version (currently version 3):

  • Version 3: Opened in place, preserving all queued messages, stored replies, instance_claims, idempotency keys, peer_mailbox, and forwarded_messages across restarts.
  • Below version 3: Message state tables are dropped and recreated at version 3 in a single transaction while preserving instance_claims.
  • Above version 3: Refused at startup naming both versions so a downgrade never corrupts newer state. To roll back across a schema bump, back up xmsg.db before upgrading.
Terminal window
# Run Rust test suite
cargo test --all-targets
# Run Clippy lints and rustfmt check
cargo clippy --all-targets -- -D warnings
cargo fmt -- --check
# Run shared service client checks & mutation suite
cargo test --locked --no-default-features --features client --test shared_client
python3 scripts/client-graph.py
python3 scripts/client-mutants.py
# Build Nix default package and OCI image
nix build .#default --no-link
nix build .#image --no-link
nix build -L .#checks.x86_64-linux.image --no-link
# Run full Nix CI suite (module eval, agy/codex hooks, pi/opencode/hermes plugins, shared-client-compat)
nix flake check ./ci
palette
dark
light
↑↓ select↵ apply esc close

Palettes adapted from Catppuccin (Macchiato) (MIT), Tokyo Night (Apache-2.0), gruvbox (MIT), Catppuccin (Latte) (MIT), Rosé Pine (Dawn) (MIT).