A draft spec for the owner's review and 13 offline tasks with their given tests: shared tool arguments and host rules in proto, the sealed fetch target (M3a finding 14), the toolkit tools and SOCKS5 egress proxy, and brokerd's [runner], podman argument lists, runtime and proxy lifecycle. Each task's tests were run against a reference at that task's end state (560 to 638 tests, clippy clean); the reference is not in the repository. Adds the runner-unavailable runbook entry and tip T23 (ETXTBSY in script tests). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
4.6 KiB
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 filescrates/brokerd/tests/fixtures/config/runner_*.toml, andcrates/brokerd/tests/support/rig.rs(replaces the old one: it builds aConfigwith a struct literal, which now needsrunner: None) - Modify:
crates/brokerd/src/config.rs,docs/implementer-log.md
Interfaces
In config.rs:
#[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<Runner>,
}
impl Config {
// … as now, plus:
/// `<home>/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:
imageis not<name>@sha256:<hex>where<name>is not empty and<hex>is exactly 64 of0-9a-f(lowercase). Split withrsplit_once("@sha256:"). Text:[runner] image is {image:?}; it must be named by digest: <name>@sha256:<64 hex digits>.memoryis not digits followed by one ofb,k,m,g(at least one digit). Text:[runner] memory is {memory:?}; it must be a number and one of b, k, m, g.egress_networkorpodmanis empty. Text:[runner] podman and egress_network must not be empty.- Any of
output_cap,pids,read_file_ms,write_file_ms,shell_ms,http_fetch_msis 0, checked in that order. Text:[runner] <name> 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, thencp 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. Thencargo 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 brokerdpasses;make gateprintsgate: ok.
Stop and report if
- A test wants an image named by tag (
:latest) to be accepted.