# M3b task 09: `[runner]` in `brokerd.toml` **Branch:** `m3b` (run `git switch m3b`; `git status --short` must be empty, otherwise stop) **Commit subject:** `brokerd: the [runner] section` ## Goal `[runner]` says how tools run: which `podman`, which image, and the limits. Without the section, `brokerd` keeps M3a's runtime, which refuses every call (task 13 does that choice). The image must be named **by digest**, so what runs is exactly what was built. Spec section 6, "Configuration". ## Files - Copy: `crates/brokerd/tests/config_runner.rs`, the five files `crates/brokerd/tests/fixtures/config/runner_*.toml`, and `crates/brokerd/tests/support/rig.rs` (replaces the old one: it builds a `Config` with a struct literal, which now needs `runner: None`) - Modify: `crates/brokerd/src/config.rs`, `docs/implementer-log.md` ## Interfaces In `config.rs`: ```rust #[derive(Debug, Clone, PartialEq, Eq, Deserialize)] #[serde(deny_unknown_fields)] pub struct Runner { #[serde(default = "default_podman")] pub podman: PathBuf, // "podman" pub image: String, // required, no default #[serde(default = "default_egress_network")] pub egress_network: String, // "pasta" #[serde(default = "default_output_cap")] pub output_cap: u64, // 262_144 #[serde(default = "default_memory")] pub memory: String, // "512m" #[serde(default = "default_pids")] pub pids: u32, // 128 #[serde(default = "default_read_file_ms")] pub read_file_ms: u64, // 30_000 #[serde(default = "default_write_file_ms")] pub write_file_ms: u64, // 30_000 #[serde(default = "default_shell_ms")] pub shell_ms: u64, // 100_000 #[serde(default = "default_http_fetch_ms")] pub http_fetch_ms: u64, // 60_000 } impl Runner { /// The time limit for one call of `tool`: the matching `_ms` field. pub fn time_limit(&self, tool: crate::args::ToolName) -> std::time::Duration; } pub struct Config { // paths, sockets, approvals as now, then: #[serde(default)] pub runner: Option, } impl Config { // … as now, plus: /// `/run/egress`: where each `http_fetch` call gets a directory for the proxy's socket. pub fn egress_dir(&self) -> PathBuf; } ``` Write one small private `fn default_…() -> …` per defaulted field (serde needs a function path). Put the struct attributes on separate lines as usual; the table above only saves space. ## Checks in `Config::load` After the `ttl_ms` check, if `runner` is `Some`, check it and return the first problem as `ConfigError::Invalid(path, text)`. In this order: 1. `image` is not `@sha256:` where `` is not empty and `` is exactly 64 of `0-9a-f` (lowercase). Split with `rsplit_once("@sha256:")`. Text: `[runner] image is {image:?}; it must be named by digest: @sha256:<64 hex digits>`. 2. `memory` is not digits followed by one of `b`, `k`, `m`, `g` (at least one digit). Text: `[runner] memory is {memory:?}; it must be a number and one of b, k, m, g`. 3. `egress_network` or `podman` is empty. Text: `[runner] podman and egress_network must not be empty`. 4. Any of `output_cap`, `pids`, `read_file_ms`, `write_file_ms`, `shell_ms`, `http_fetch_ms` is 0, checked in that order. Text: `[runner] must be at least 1`, with the field's name. A missing `image` or an unknown key is already a `ConfigError::Parse` from serde; do not add checks for those. `Config::parse` (the string version the tests use for plain values) does not run these checks; only `load` does, as for `ttl_ms` today. ## Steps - [ ] **1. Copy.** `git switch m3b`, then `cp docs/plans/M3b/files/crates/brokerd/tests/config_runner.rs crates/brokerd/tests/`, `cp docs/plans/M3b/files/crates/brokerd/tests/fixtures/config/runner_*.toml crates/brokerd/tests/fixtures/config/`, `cp docs/plans/M3b/files/crates/brokerd/tests/support/rig.rs crates/brokerd/tests/support/` - [ ] **2. See it fail.** `cargo test -p brokerd --test config_runner`. Expected: it does not compile. - [ ] **3. Write the code.** Run `cargo fmt --all`. - [ ] **4. See it pass.** `cargo test -p brokerd --test config_runner --test config`. Expected: 7 and 7 passed. Then `cargo test -p brokerd`: everything passes. - [ ] **5. Run the gate.** `make gate`. Expected last line: `gate: ok`. - [ ] **6. Log and commit.** `git add crates/brokerd docs/implementer-log.md && git commit` ## Done when - `cargo test -p brokerd` passes; `make gate` prints `gate: ok`. ## Stop and report if - A test wants an image named by tag (`:latest`) to be accepted.