Files
boxmaker/docs/plans/M4a/03-gatewayd-config.md
T
kyleandClaude Opus 5.5 0339dc13b2 Plan M4a: gatewayd in 15 tasks, with skeletons and given tests
Each task's tests were run against a reference at its end state; the end states were replayed
from master in order with the gate at each step (650 to 762 tests); each skeleton compiles
against its tests and fails them. The reference is kept off this machine. Lessons T27 (every
wait in a test has a limit) and T28 (mutate the reference before hand-over) come from this work.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-23 19:05:44 -07:00

4.5 KiB

M4a task 03: gatewayd.toml

Branch: m4a (run git switch m4a; git status --short must be empty, otherwise stop) Commit subject: gatewayd: config, gatewayd.toml into a checked Config

Goal

gatewayd.toml says where Mattermost is, where the token comes from, who may talk to Boxmaker and in which channels, and a few limits. It is our format: an unknown key anywhere is an error, and a value that is wrong is refused at start with a message that names it. Spec section 3.

[mattermost]
url = "https://straylight.scylla-hammerhead.ts.net"   # https or http; no path
ca_file = "/etc/boxmaker/extra-ca.pem"                  # optional; added to the system's roots

[secrets.mattermost_token]
credential = "mattermost-token"      # exactly one of: credential, env, file

[allow]
users = ["abcdefghijklmnopqrstuvwxyz"]   # Mattermost user ids (26 characters)
channels = []                            # channels and group messages allowed, by id; default none

[loop]
socket = ""                          # empty: <home>/run/loop/loop.sock

[paths]
home = "/var/lib/boxmaker"           # default: BOXMAKER_HOME, then /var/lib/boxmaker

[limits]
queue = 20
typing_every_ms = 3000
ping_every_ms = 30000
dead_after_ms = 60000

Files

  • Copy: crates/gatewayd/tests/config.rs, crates/gatewayd/tests/support/tmp.rs, and the skeleton crates/gatewayd/src/config.rs
  • Modify: crates/gatewayd/src/lib.rs (pub mod config;), docs/implementer-log.md

The skeleton

Every type is written, with its serde attributes: Config, MattermostConfig, SecretSpec, SecretSource, AllowConfig, LoopConfig, Paths, Limits (with their defaults), ServerUrl, ConfigError, and the constant MATTERMOST_TOKEN = "mattermost_token". Do not change them. The functions are todo!(), each with its steps above it.

The messages, exactly

Config::problem returns the first problem, in this order:

Problem Message
url fails parse_url parse_url's message: [mattermost] url "<url>" must be http:// or https://, a host, an optional port, and nothing else (the url with {:?})
ca_file not absolute [mattermost] ca_file "<path>" must be an absolute path ({:?})
no [secrets.mattermost_token] [secrets.mattermost_token] is missing
a secret whose source() fails [secrets.<name>] <source's message>
allow.users empty [allow] users is empty: a gateway that answers nobody is a mistake
an id in allow.users or allow.channels that is not valid_id [allow] "<id>" is not a Mattermost id (26 characters of a-z and 0-9) ({:?})
a limit that is 0 (checked in the order queue, typing_every_ms, ping_every_ms, dead_after_ms) [limits] <name> must be at least 1

SecretSpec::source's messages:

Case Message
not exactly one of the three needs exactly one of credential, env and file
credential empty or with a byte that is not an ASCII letter, digit, _, . or - credential "<name>" is not a credential name (letters, digits, _ . -)
env empty or with a byte that is not A-Z, 0-9 or _ env "<var>" is not a variable name (A-Z, 0-9, _)
file not absolute file "<path>" must be an absolute path

Config::token_source gives source() of mattermost_token, or [secrets.mattermost_token] is missing. ConfigError's Display is <path>: <error or message>.

Steps

  • 1. Copy. git switch m4a, then mkdir -p crates/gatewayd/tests/support && cp docs/plans/M4a/files/crates/gatewayd/tests/config.rs crates/gatewayd/tests/ && cp docs/plans/M4a/files/crates/gatewayd/tests/support/tmp.rs crates/gatewayd/tests/support/ && cp docs/plans/M4a/files/crates/gatewayd/src/config.rs crates/gatewayd/src/. Add pub mod config; to crates/gatewayd/src/lib.rs.
  • 2. See it fail. cargo test -p gatewayd --test config. Expected: it compiles and 7 tests fail with not yet implemented.
  • 3. Fill the functions one at a time: valid_id, parse_url, SecretSpec::source, ConfigError's fmt, then the Config methods. cargo check -p gatewayd after each.
  • 4. See it pass. cargo test -p gatewayd --test config. Expected: 7 passed.
  • 5. Run the gate. cargo fmt --all, then make gate. Expected last line: gate: ok.
  • 6. Log and commit. git add crates/gatewayd docs/implementer-log.md Cargo.lock && git commit

Done when

  • cargo test -p gatewayd --test config passes; make gate prints gate: ok.

Stop and report if

  • A test wants a type changed. The types are the format; report instead.