An annotated session showing the full lifecycle of a gated write action on the
f0-pa-actions server (run_test — running a ProjectAchilles validation test
on one host), including the two paths that fail closed. Illustrative
recreation: values are fictional; the refusal messages, intent shape, watcher
flow, and audit line match the implementation in core/gating/ exactly.
The rule being demonstrated (security model): execution requires BOTH the platform write flag AND a per-action human confirmation delivered out-of-band — the model alone can never act.
Operator: run the phishing-simulation test on web-01.
Tool call: run_test(test_id="3f9d…", hostname="web-01")
Finding (posture):
Action 'projectachilles.run_test' is disabled.
Set the platform write flag to enable it.
With
PROJECTACHILLES_ALLOW_WRITEunset, the write tools are inert. This is the state every server ships in. Enabling writes is a deliberate deployment decision, made in a file the model cannot edit.
The operator has set PROJECTACHILLES_ALLOW_WRITE=true in
.env.projectachilles and restarted the server.
Tool call: run_test(test_id="3f9d…", hostname="web-01") — no
confirmation_token.
Finding (intent, finding_type: "action", abridged — full shape in
examples/findings/projectachilles-actions.json):
{
"finding_type": "action", "severity": "info",
"title": "INTENT (not executed): run_test T1566-phish-sim on host web-01",
"evidence": [
{ "key": "action", "value": "run_test" },
{ "key": "matched_hosts", "value": "1" },
{ "key": "status", "value": "awaiting operator confirmation (watcher or token)" }
]
}First call = preview, always. The intent states exactly what would run and where. For a tag/fleet run the intent lists the matched host count, and the confirmation is bound to that count — if membership changes, the model must re-preview and the operator must re-approve.
Model: I've prepared the run. A pending approval is registered — please approve it in your watcher terminal, then ask me to proceed.
$ python scripts/confirm_action.py --watch
Watching for gated-action requests (~/.f0sectools/gating/requests) — Ctrl-C to stop.
projectachilles.run_test -> 3f9d…|web-01 — approve? [y/N] y
APPROVED projectachilles.run_test -> 3f9d…|web-01 (15 min, single use)This is the forge-resistant part. The approval is written to
$F0_GATING_DIR/approvals/by the operator's CLI. No token, no approval text, nothing confirmable ever appears in model context — there is nothing the model could echo, replay, or invent. (Headless alternative:confirm_action.py <action> "<target>"prints a single-use token the operator passes along — same guarantees, hash-only at rest.)
Operator: approved — go ahead.
Tool call: run_test(test_id="3f9d…", hostname="web-01") — the identical
call.
Finding (result, abridged):
{
"finding_type": "action", "severity": "info",
"title": "Executed: run_test T1566-phish-sim on web-01 (task queued)",
"evidence": [ { "key": "task_id", "value": "9c21…" } ]
}The gate consumed the stored approval — single-use, unlink-before-validate, so a concurrent duplicate call cannot also win. A second identical call now would return a fresh intent, not an execution.
The audit line ($F0_GATING_DIR/audit.log, local only):
{"action": "projectachilles.run_test", "target": "3f9d…|web-01",
"actor": "f0-pa-actions", "method": "approval", "token_ref": "a1b2c3d4…"}(token_ref is the first 16 hex chars of a SHA-256 — truncated here so the
fictional example doesn't trip secret scanners.)
| Property | Where it showed up |
|---|---|
| Fail closed, twice | flag off → refusal; flag on but unconfirmed → intent only |
| Out-of-band confirmation | approval happened in the watcher terminal, invisible to the model |
| Single-use, target-bound, TTL'd | the approval died on consumption; it named one action on one target; 15-minute expiry |
| Audited | one JSONL line linking execution to its confirmation by hash reference |
The convenience mode (chat-confirm, opt-in per platform) trades away the
out-of-band property for supervised reversible actions — its honest caveat is
in the security model.
It is never wired to destructive actions.