Security visual — the capability floor: allow the useful, deny the dangerous
One picture of the trust boundary. Every tool call a model proposes is adjudicated before it runs. The floor allows the everyday tool set so the agent is useful, and refuses destructive calls by argument value —
Bashis allowed, butBash{command:"rm -rf /"}is denied before the shell ever sees it. This is the deployable, file-authored form of fak’s “permissions as the floor” thesis, shown on the real Claude Code dogfood (fak/examples/dogfood-claude-policy.json).
Companion to fak/POLICY.md (the
manifest schema + author/validate workflow); the why this layer, not a content guardrail
argument is in COMPARE-security-model-vs-guardrails, a private companion — not published
in this repo (it lives in the paired fak-private research tree). Generated 2026-06-18.
The visual — where the deny happens
model proposes a tool call the kernel adjudicates effect
(untrusted, ring-3) (BEFORE dispatch) (ALLOW and DENY)
┌──────────────────────────┐ tool_use ┌──────────────────────────────┐
│ "run: rm -rf /tmp/x" │ ─────────────────▶ │ Deny[tool]? ──┐ │
│ "run: ls -la" │ │ self-modify glob? │ │
│ "Edit .git/config" │ │ arg-value predicate? ├──▶ │ ALLOW ─▶ run the tool
└──────────────────────────┘ │ allow-list / prefix? │ │ DENY ─▶ refused, with a
▲ │ else DEFAULT_DENY ──┘ │ closed-vocab reason
│ refusal + reason (POLICY_BLOCK / SELF_MODIFY / DEFAULT_DENY) │ (never reaches shell)
└─────────────────────────────────────────────────────────────────┘
“ring-3” in the visual. ring-3 is the least-privileged CPU protection ring (ordinary userspace, as opposed to ring-0 kernel mode). The visual casts the model as that unprivileged userspace and the harness as the kernel adjudicating every syscall (tool call) from evidence the model did not author. The ring-3 inversion in fuller form — model as untrusted ring-3 userspace, harness as kernel — is in
docs/notes/EXPLAINER-trust-floor-two-lenses-2026-06-17.md.
The deny is structural, not a model judgement: an injection that talks the model
into proposing rm -rf changes nothing — the call is refused at the boundary. The
refused call never reaches the shell, the filesystem, or the network.
The chart — what the default dogfood floor allows vs denies
| Proposed call | Verdict | Reason | Why |
|---|---|---|---|
Bash ls -la, cat, grep, git commit |
✅ ALLOW | — | everyday dev work |
Read / Edit / Write a normal file |
✅ ALLOW | — | the agent has to do its job |
Bash rm -rf … / rm -f … |
⛔ DENY | POLICY_BLOCK |
destructive removal — denied by argument value |
Bash sudo … |
⛔ DENY | POLICY_BLOCK |
privilege escalation |
Bash git push … |
⛔ DENY | POLICY_BLOCK |
the agent can commit, but not publish |
Bash curl … \| sh |
⛔ DENY | POLICY_BLOCK |
remote-code-execution pipe |
Bash :(){ :\|:& };: |
⛔ DENY | POLICY_BLOCK |
fork bomb |
Bash dd if=… of=/dev/sd… / mkfs … |
⛔ DENY | POLICY_BLOCK |
disk wipe |
Edit/Write into .git/, .ssh/, internal/kernel/, VERSION |
⛔ DENY | SELF_MODIFY |
can’t rewrite the kernel or secrets |
| any tool the floor never named | ⛔ DENY | DEFAULT_DENY |
fail-closed — nothing is allowed by default |
The load-bearing idea: the deny is on the argument, not just the tool name. A
coarse tool like Bash is one allow-listed capability, but its command value is
gated by arg_rules (RE2 deny-patterns), so the floor distinguishes ls from
rm -rf without forking the kernel.
Prove any verdict without launching a session
# one call, adjudicated against the shipped floor:
fak preflight --tool Bash --args '{"command":"rm -rf /tmp/x"}' \
--policy fak/examples/dogfood-claude-policy.json
# => verdict=DENY reason=POLICY_BLOCK by=monitor
fak preflight --tool Bash --args '{"command":"ls -la"}' \
--policy fak/examples/dogfood-claude-policy.json
# => verdict=ALLOW reason=NONE by=monitor
# eyeball the entire floor (allow-list, arg rules, self-modify globs, redactions):
fak policy --check fak/examples/dogfood-claude-policy.json
The same matrix is locked as a regression test
(fak/internal/adjudicator/dogfood_manifest_test.go::TestDogfoodManifestVerdictMatrix),
so a manifest edit that silently widens the floor fails CI.
How to use / change it
- The Claude Code dogfood (
fak/scripts/dogfood-claude.sh, seefak/DOGFOOD-CLAUDE.md) loads this floor by default so interactive sessions work; override withFAK_DOGFOOD_POLICY=<path>orFAK_DOGFOOD_POLICY=nonefor the raw fail-closed kernel. - The floor is a JSON manifest, not a code change:
fak policy --dumpto start from the built-in default, edit,--check, then--policy FILE. Full schema + closed refusal vocabulary infak/POLICY.md. - Honest scope: the floor bounds which tools run with which argument values. It is
the call-side capability deny — paired with the result-side containment
(poisoned tool results held out of context) described in the top-level
READMEandCOMPARE-security-model-vs-guardrails(private companion — not published).