Skip to content

feat(broker): nodkey broker command, start and stop order, systemd unit - #60

Merged
groupsky merged 1 commit into
mainfrom
feat/broker-command
Oct 15, 2026
Merged

groupsky merged 1 commit into
mainfrom
feat/broker-command

Conversation

@groupsky

Copy link
Copy Markdown
Contributor

What

Part B of #11: the nodkey broker command, which wires the merged parts into one daemon.

  • Start order: load and check the config → core dumps off (soft and hard RLIMIT_CORE 1, PR_SET_DUMPABLE 0, read back) → the broker's TLS key → SSH keys (with each key's hosts; the same public key in two sections refuses to start) → open the audit log → stores (a store that fails to start denies its own requests and sends one alert) → the broker service → the Telegram approver → the approval core → listen → broker_started → serve. Nothing listens before the audit log is open, and nothing is served before the approval core runs.
  • Stop order on SIGTERM/SIGINT or when the approver stops: stop polling, close the service, the server, the stores and the audit log, and write broker_stopped; bounded by 80 s.
  • Exit codes: 0 clean stop, 1 other end, 64 usage, 76 Telegram polling conflict (the token may be stolen), 77 Telegram refused the token. The unit does not restart on 64, 76 or 77.
  • Alerts: a torn audit line found at start, and a failed store, alert the person; start alerts retry with a growing wait until they are sent. A log_recovered entry without a later broker_started alerts again at the next start.
  • Telegram: its connections are reset on stop, so a late getUpdates cannot reach Telegram after a restart. The CA and proxy settings in use are logged at start.
  • contrib/systemd/nodkey.service: encrypted credentials, StateDirectory=, UMask=0077, LimitCORE=1, MemoryMax=, MemorySwapMax=0, MemoryZSwapMax=0, TimeoutStopSec=90s, RestartPreventExitStatus=64 76 77, LogRateLimitIntervalSec=0, and a strict sandbox (systemd-analyze security 1.4) that still lets bws and bw run.
  • store.PrepareDir makes or checks the store directory; audit.Logger.LastRecovery; telegram.ErrTokenRefused.

Tests

The real command in a child process with throwaway keys, fake store programs and a fake Telegram server (reached through HTTPS_PROXY and a test CA, so nothing goes to the real Telegram): the start and stop order, every start failure, the same key twice, a failed store, a polling conflict (76), a refused token (77), core dumps off, the torn-line alert after a failed start, alert retries, and a canary secret end to end through the real Telegram approver. Stress runs with -count=10 -cpu 1,2,4 pass on Go 1.27 and 1.26.

Docs

DESIGN.md sections 7, 9, 11, 14, 15, 16 and 17; THREAT-MODEL.md T6, T16, T17 and T18; AGENTS.md layout.

Review

Three rounds with the security-reviewer agent; the last said "ready to commit".

Fixed on the way:

  • the torn-line alert could be lost when the first start failed;
  • secrets could reach the disk through swap (MemorySwapMax=0);
  • a revoked bot token caused a restart loop (now exit 77, no restart);
  • only the soft core limit was set;
  • start alerts had only one try.

Left for later issues: #59 (send start alerts at the same time, so a stop during a long outage stays clean).

Closes #11

Wire the broker service, the approval core, the Telegram approver, the stores and the audit log into one daemon, with core dumps off and an example hardened unit.

Closes #11
@groupsky groupsky added this to the v1 milestone Oct 15, 2026
@groupsky
groupsky merged commit 0129939 into main Oct 15, 2026
6 checks passed
@groupsky
groupsky deleted the feat/broker-command branch October 15, 2026 03:50
@groupsky groupsky mentioned this pull request Oct 15, 2026
7 tasks done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Broker daemon: nodkey broker

1 participant