- apt build_plan: add POSIX '--' before user-derived name=version tokens - dnf build_plan: add POSIX '--' before user-derived name-version token - validate_mint_request: enforce canonical UUID v4 on request_id at intake - RAF components/04: document '--' separator in pipeline step 7 - RAF security/05: document hardened argv contract + UUID v4 validation - RAF flows/03: add stale banner (5 deviations from current code) 14 tests pass. Deferred: artifact_path staging root, name/version charset regex (both need design decisions — see task file).
4.3 KiB
Helper Component
A privileged, network-less Rust executor that trusts nothing it didn't verify itself — the last gate before mutation.
Doctrine
The helper (helper/src/main.rs, a single ~2,000-line binary) is the only thing on a gated host allowed to mutate packages, and it earns that privilege by being structurally incapable of being talked into anything. It has no network stack in play, reads its trust inputs from root-owned pinned files, and performs exactly one operation per invocation — the one described by a validly signed capability token. Everything else is a typed denial.
Deny-by-default is the architecture, not a configuration: every failure path returns a Denial with a distinct exit code and a log_security entry. There is no flag that weakens verification. See security/05-supply-chain-gate for the token contract this enforces.
Invocation
The agent invokes it via sudo systemd-run --pipe --property=ProtectSystem=no — a transient unit with full filesystem access, separate from the agent's own locked-down unit. The token arrives on stdin (or a file path for self-update flows). The agent holds zero install sudo; the sudoers file grants discovery commands plus this one invocation line. See components/02-agent.
The Verification Pipeline
run() executes, in order — any failure stops the world:
- Trust-input self-validation (SEC-021) —
validate_trusted_path: every file the helper relies on (keyring dir, agent-id file, state) must be root-owned, not a symlink, and not group/other-writable. The helper defends its own inputs instead of trusting that the installer set permissions correctly. - Keyring load — pinned Ed25519 public keys from
/etc/redflag/trusted-keys. Verify keys, not servers (load-bearing constraint #4). - Closure hash —
closure_hash()recomputes the canonical hash over the token's resolved closure. This must be byte-identical to the Go implementation; cross-language tests pin the contract (testing/01-test-pyramid). - Signature —
verify_signature()checks the token's Ed25519 signature over the signed message (which embeds the closure hash) against the keyring, bykey_id. - Artifact hashes —
verify_artifacts()SHA-256s every artifact the token authorizes. A mismatch anywhere is a denial. - Replay check —
replay_check_and_record(): token IDs are recorded in local state; a token executes once. - Plan + execute —
build_plan()translates the token into the exact package-manager commands;execute_plan()runs them. No interpretation, no substitution. A POSIX--(end-of-options) separator precedes all user-derived values (package names, versions) to prevent option injection (GATE-005). - Receipt —
emit_result()writes aPolicyResultthe agent reports back; the server reconciles it into lifecycle state.
Binary Self-Update Path
Agent, helper, and desktop binaries update through the same gate as packages: stage_and_verify_binary (hash check before anything moves) → atomic_replace_binary (rename, never write-in-place; failed swap leaves <binary>.bak). During agent upgrades, reconcile_agent_unit_dropin() heals fleet systemd units to the current template — this is how pre-AmbientCapabilities units get fixed without manual fleet surgery (deployment/01-docker-stack).
Standalone Mint Mode
The helper also carries a local-authority minting path (MintRequest / MintedToken / load_mint_key) for deployments where the signing authority runs beside the host rather than on a central server. Design of record: security/06-standalone-authority.
Why a Separate Binary, Why Rust
- Privilege separation: the long-running, network-facing agent stays unprivileged; the privileged thing is short-lived, single-purpose, and offline.
- Can't be redirected: no outbound capability means a compromised server can lie in a token — and the signature/hash checks catch that — but nothing can make the helper fetch from somewhere else.
- Small audit surface: one file, explicit pipeline, typed denials. The binary is meant to be read.
Last reviewed: 2026-06-14