Files
boxmaker/docs/plans/M3b/10-brokerd-podman-args.md
T
kyleandClaude Opus 5.5 b426ca1958 Specify and plan M3b: the runner and the tools
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>
2026-09-22 22:29:27 -07:00

4.4 KiB

M3b task 10: the podman argument lists

Branch: m3b (run git switch m3b; git status --short must be empty, otherwise stop) Commit subject: brokerd: the podman argument lists

Goal

Build, without running anything, the exact podman arguments for one call's container, and for http_fetch's egress proxy. Every argument is its own OsString: nothing is ever passed through a shell, and the tool's arguments go on standard input, never on the command line. The lists are tested as golden files, one argument per line. Spec section 6.

The container's name says whose call it is, so RunSpec gains the session and the call.

Files

  • Copy: crates/brokerd/tests/podman_args.rs and the six files crates/brokerd/tests/fixtures/podman/*.args
  • Create: crates/brokerd/src/podman.rs
  • Modify: crates/brokerd/src/runner.rs, crates/brokerd/src/lib.rs (pub mod podman;), docs/implementer-log.md

RunSpec gains two fields

In runner.rs, RunSpec gets session: SessionId and call: CallId as its first two fields (private, like the others), with getters:

pub fn session(&self) -> &SessionId;
pub fn call(&self) -> CallId;

run fills them from decision.request().session.clone() and decision.request().call. The compile_fail doctest at the top of runner.rs builds a RunSpec with a struct literal: add the two fields to it, first, so it still fails only because the fields are private:

//!     session: proto::SessionId::new("s1").unwrap(),
//!     call: proto::CallId(1),

podman.rs

pub const EGRESS_MOUNT: &str = "/run/egress";
pub const EGRESS_SOCKET: &str = "/run/egress/egress.sock";
pub const TOOLKIT: &str = "/bin/toolkit";

/// "boxmaker-<session>-<call>-<n>"
pub fn container_name(session: &SessionId, call: CallId, n: u64) -> String;

/// The tool's container. `egress` is the call's egress directory, for `http_fetch` only.
pub fn tool_args(spec: &RunSpec, runner: &Runner, name: &str, egress: Option<&Path>) -> Vec<OsString>;

/// The egress proxy's container.
pub fn egress_args(runner: &Runner, name: &str, dir: &Path, hosts: &[String]) -> Vec<OsString>;

tool_args, in this order:

run  --rm  -i  --name=<name>  --label=boxmaker=tool  --network=none
--read-only  --cap-drop=all  --security-opt=no-new-privileges  --userns=keep-id
--pids-limit=<runner.pids>  --memory=<runner.memory>
--tmpfs=/tmp:rw,size=64m,mode=1777
--volume=<path>:<path>:ro   (or :rw when writable)   one per spec.mounts(), in order
--volume=<egress dir>:/run/egress:rw                  only when `egress` is Some
<runner.image>  /bin/toolkit  <spec.tool().as_str()>

egress_args, in this order:

run  -d  --rm  --name=<name>-egress  --label=boxmaker=egress  --network=<runner.egress_network>
--read-only  --cap-drop=all  --security-opt=no-new-privileges  --userns=keep-id
--pids-limit=64  --memory=128m
--volume=<dir>:/run/egress:rw
<runner.image>  /bin/toolkit  egress-proxy  --socket  /run/egress/egress.sock  --allow  <hosts joined with ",">

The six hardening flags (--read-only to --memory=…) are the same in both, so write them once in a private function. Build the --volume=<dir>:… argument as an OsString with push, so a directory need not be UTF-8 (dir.as_os_str()), not with format! on display().

Steps

  • 1. Copy. git switch m3b, then cp docs/plans/M3b/files/crates/brokerd/tests/podman_args.rs crates/brokerd/tests/, mkdir -p crates/brokerd/tests/fixtures/podman, cp docs/plans/M3b/files/crates/brokerd/tests/fixtures/podman/*.args crates/brokerd/tests/fixtures/podman/
  • 2. See it fail. cargo test -p brokerd --test podman_args. Expected: it does not compile.
  • 3. Write the code. Run cargo fmt --all.
  • 4. See it pass. cargo test -p brokerd --test podman_args --test runner. Expected: 7 and 8 passed. cargo test -p brokerd --doc: every doctest 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 and cargo test -p brokerd --doc pass; make gate prints gate: ok.

Stop and report if

  • A golden file disagrees with the lists above: report both, do not edit the golden file.
  • Anything would put the tool's arguments (the command, the path, the URL) on podman's command line.