Trust
Security
Arcturn’s safety features are controls with edges, and the edges are written down. A safety feature whose limits you can’t see is worse than no feature, because you’ll trust it.
The choke point
Enforcement happens in one place: the runtime’s tool dispatcher checks the permission engine and returns a denial before a tool’s execute is ever reached. There is no second path into a tool, which is the property that makes the rest of this page meaningful — a control with two entrances is not a control.
Read-only tools pass without a prompt. fetch is deliberately excluded from that list: it reads nothing local but sends data to an arbitrary host, so it is gated like a mutating tool. Anything that reaches the ask step with no permission requester configured resolves to deny — and in non-interactive mode, where there is no user to answer, a check that would have prompted is denied automatically with a note on stderr saying which flag would have allowed it.
The controls
Eight layers, each doing one thing. None of them is a boundary you should treat as airtight on its own; together they are the difference between an agent you supervise and an agent you hope about.
Permission engine
A rule-based allow/deny/ask resolver checked by the tool dispatcher before a tool’s execute is reached. Rules are scoped session over project over user, and a more specific deny beats a broader permissive rule even from a nearer scope.
Checkpoints
Before a write or edit touches a file for the first time in a turn, its prior content — or its absence — is stored as a content-addressed blob. /rewind restores those files and forks the conversation instead of deleting it.
Dry-run overlay
--dry-run reroutes every file mutation into a shadow copy of the workspace. You review one aggregate diff with /diff, then /apply writes back through a temp-file-plus-rename, or /discard deletes the shadow tree.
OS sandbox
Opt-in, and separate from dry run: with "sandbox": "workspace-write", a bash command is wrapped by sandbox-exec on macOS or Bubblewrap on Linux. Writes are denied everywhere except the working directory, the OS temp directory and $HOME/.arcturn.
Taint tracking
Distinctive text from fetch, websearch and MCP output is remembered, and a later mutating call whose arguments repeat it is flagged. Extraction is biased toward silence — a tracker that cries wolf gets turned off.
Canary tokens
High-entropy decoy tokens, or real secret values you register yourself, are watched on the way out. Any egress-capable tool call whose arguments contain one verbatim is treated as exfiltration in progress.
Cost ceiling
Cost accounting is always on and updated after every turn. --max-cost aborts the run the moment cumulative spend reaches the ceiling, including spend from sub-agents — an enforcement mechanism, not a warning.
Audit trail
An append-only log of every completed tool call, every interactive permission decision and every hook verdict, recorded as the run happens rather than reconstructed afterwards. Enable it with "audit": true.
Known limits
Every control above has an edge it does not cover. These are disclosures, not failures — they are here so that you calibrate to what the feature actually does rather than to its name.
Permission engine
Two edges, both structural.
alwaysAllowToolsis consulted at step 1 of resolution, before any rule: a tool on that list is allowed without the rule set being reached at all, so a host that widens it past the runtime’s owntodoandplanputs those tools beyond even adeny. And a rule matches the subject a tool reports — an absolute path fromwriteandedit, the command line frombash, tested per shell segment — so a path rule never sees the path a shell command reaches by another spelling:deny **/*.envstops aneditof that file and does not stopcat .env.Checkpoints and /rewind
Only
writeandeditare checkpointed. A shell command that mutates the tree is not, sosed -iandrmare invisible to/rewindand will not come back. The conversation side is genuinely non-destructive; the file side is a real disk mutation.Dry-run overlay
--dry-rundeliberately does not wrapbash,greporglob— they take commands and patterns rather than a single path — so a shell command still reads and mutates the real tree while dry-run is active.Canary tokens
Matching is exact substring containment, so any encoding of the secret defeats it completely: base64 the value and nothing fires. It catches a verbatim leak, not a determined exfiltrator.
Taint tracking
Matching is substring containment over whitespace-normalized text, one direction only and case-sensitive. A genuine echo of an injected instruction is verbatim; an instruction the model paraphrases rather than repeats is not detected.
OS sandbox
It narrows write access only — reads, network access and process spawning are left alone — and it needs a backend:
sandbox-execon macOS orbwrapon Linux. Anything else is unsupported.Speculative approval
Not one of the eight above: speculation is off by default and is introduced on Control. Only
writeandeditcan be speculated, because only file mutations can be undone by throwing a directory away. Everything else is blocked outright while a speculation is open — including abashcall that would only have touched files, because the wrapper cannot know that in advance.Cost ceiling
The guard checks cumulative cost after every turn, so it aborts at the next turn boundary rather than mid-turn. A limit of
0, or leaving it unset, disables the guard entirely.Audit trail and provenance
Both are off by default and record only while enabled — a session you did not turn them on for cannot be reconstructed retroactively. The permission log captures the interactive ask path only: a decision resolved automatically by a rule or by the mode has no
toolNameto attribute and never reaches it, though the tool call itself is still captured.
Adversarial review
The codebase has been through four waves of adversarial review — parallel reviewers whose only job was to break the new seams. The findings are published here rather than quietly patched out, because a security feature that has never been adversarially poked at is a claim, not a control.
The findings were not cosmetic:
/applycould write outside the workspace through an in-workspace symlink.- Served sessions and sub-agents escaped the audit trail entirely.
- The WebSocket upgrade had no
Origincheck, so any web page could drive a loopback server. - Two features were present but unreachable: the canary guard was watching a generated token nobody had ever seen, and speculative approval could never shelter a single byte because tools ran sequentially.
Every fix landed with a regression test verified to fail against the previous behaviour first.
Reporting a vulnerability
Open an issue on GitHub. If you would rather not describe the problem in public, open an issue saying so and I will follow up there.
This is a pre-1.0, single-maintainer project; there is no SLA. I would still rather hear about it than not.
Read the limits, then run it.
Every control on this page has an edge, and every edge is written down. Start a session and watch the first one hold.