the ask
No firewall, proxy, or temp credentials; bash and generated code cannot access the secret; streaming input and output. Asked publicly, near verbatim, by the founder of an eval platform — and nobody had a clean answer.
If the same process has model credentials and can directly run bash, sandboxing becomes a game of subtracting authority after the fact. Flip the model: agents request effects; handlers own authority; results stream back into resumable continuations.
agent
emits EffectRequest
↓
EZAF runtime
policy + capabilities + user intent
↓
handler
model.complete | shell.run | fs.* | net.fetch
↓
EffectResult / stream
injected back into the continuationNo firewall, proxy, or temp credentials; bash and generated code cannot access the secret; streaming input and output. Asked publicly, near verbatim, by the founder of an eval platform — and nobody had a clean answer.
They preserve the Unix ambient-authority model while demanding capability-security guarantees. The agent must hold enough authority to talk to the model, arbitrary code must never reach that authority, and the usual isolation tools are off the table.
Untrusted generated code must not be able to exfiltrate secrets, while the user keeps interactive streaming agent behavior. Nothing in that sentence requires the agent to possess anything.
The sandboxing problem is a symptom of the wrong abstraction. APIs grant ambient authority; effects make authority explicit, interceptable, streamable, resumable, auditable — and evaluable.
Claude/Codex process has model credentials can call the model API can run bash can read/write files can use the network → sandbox tries to subtract power from an overpowered process, after the fact
Agent may request effects. Handlers possess authority. Untrusted code receives only effect results. → the model API key is an implementation detail of the model.complete handler
The agent stops being a program that does things and becomes a state machine that wants things: State + Event → State + Effects. The handlers own the dirty world. Effects an agent requests instead of powers it holds: model.complete · shell.run · fs.read · fs.write · net.fetch · artifact.release · user.ask
Bash and generated code cannot access the model secret because the secret only exists inside the model.complete handler. There is nothing to leak: it was never an in-process value in the untrusted world.
Model tokens, shell stdout/stderr, and artifact updates are all event streams already. The effect loop is a streaming loop by construction, not by accommodation.
The tool runner receives no credentials at all — not short-lived ones, none. Authority is a handler you route through, not a token you hold.
Network mediation stops being the primary security mechanism because authority is structural. There is still an authority-bearing runtime — that is unavoidable — but it is an effect handler, not a secret smeared across a process tree.
The protocol-handler flow on the front page is one instantiation — a website proposing local work. The primitive underneath is wider: any surface can propose work; any runtime can request effects; any handler can satisfy them; the user controls authority, policy, continuity, and release. It turns ambient actions into explicit requests that can be reviewed, delegated, sandboxed, resumed, audited, and released.
Claude Code / Codex → EZAF → sandbox / CI / local handler
a GitHub issue → EZAF → agent / worktree / PR handler
a Slack message → EZAF → calendar / email / doc handler
a browser layer → EZAF → app / session / action handler
an MCP client → EZAF → capability-aware tool router
a local app → EZAF → user-approved effect
opaque CLI wrapper — Run Claude Code / Codex under a constrained env; capture pty streams; watch diffs. EZAF is a continuation manager, audit layer, and release gate.
structured effect log — Normalize observed tool use into typed effect events. The ledger exists before the enforcement does.
hooks as guards — PreToolUse maps Bash / Edit / Read / WebFetch / MCP calls into policy decisions: allow, deny, rewrite.
hooks as routers — Selected tool calls stop executing in the agent's default path — they are delegated to EZAF handlers: local sandbox, CI, WASI, Firecracker, human approval.
split model from tools — The model adapter holds the credential and speaks the vendor API; the tool runner holds nothing and executes effects. The CLI survives as a UI over two authority domains.
native effect mode — Agents emit typed effect requests directly; EZAF resolves and resumes. Hooks disappear or become optional middleware.
Hooks are the wedge. They are not the final security boundary — they are the compatibility shim that lets existing agents federate side effects into a user-owned runtime: Bash → shell.run, Read/Edit → fs.*, WebFetch → net.fetch, MCP → mcp.call, subagent → agent.delegate.
The same architecture that makes sandboxing tractable makes agent behavior testable. Stop grading final answers and raw scrollback; grade the typed effect trace:
Did the agent request the right effects?
Did it ask for excessive authority?
Did it route through the correct handler?
Did it recover correctly from denied effects?
Did it leak secrets into stdout, stderr, or artifacts?
Did the final artifact depend only on allowed capabilities?
Rock-solid agent sandboxing will not come from better flags around monolithic CLIs. It comes from moving authority into effect handlers.