Repository navigation
Releases: crocodile-labs/openmoat
Release list
0.1.1 - 2026-10-09
Alpha: the hook's decisions are not enforced by the operating system (ADR-013). Only commands inside the host sandboxes
moat initconfigures, or undermoat run, are confined by the OS. Elsewhere an allowed call runs with your permissions, so a classifier mistake is a security bug.
Release Notes
Added
moat benchreproduces how a hook decides (#364). With no arguments it sends the bundled MoatBench scenarios (attacks, benign work and developer workflows) to thismoatas Claude Code, Codex and Cursor hook payloads in a throwaway home and prints the scorecard.moat bench --hook <command> --host claude-code|codex|cursorsends the same payloads on stdin to any hook command, reads each reply by that host's hook protocol (allow, ask, deny, passthrough, or error when the hook crashes, times out after 10 s or answers outside the protocol) and lists each step whose answer differs from the scenario, followed by what that host does when its hook is missing, crashes or times out, as documented in docs/THREAT_MODEL.md §5. The scenarios' commands never run; the hook runs in the throwaway home with an environment of onlyPATH,HOMEandUSERPROFILE.--verboseprints that environment and every payload;--format jsonis for scripts; it exits 0 whatever the scores. The scenarios moved fromtests/moatbench/tocrates/openmoat-cli/moatbench/, and the MoatBench test now runsmoat bench.moat run --isolate -- <agent>: the Isolated tier on Linux (ADR-018, #174). bubblewrap starts the agent in new user, mount, PID, IPC, UTS, cgroup and network namespaces, rootless, with no daemon or image. Its root holds only the project,sandbox.read_roots, the agent's executable,--writepaths and an empty in-memory temp directory; the rest of the home does not exist there. Inside those trees every path the policy denies that exists at start is covered: denied reads (.envfiles,~/.cargo/credentials.toml) by an empty placeholder no one may open, denied writes (.git,.claude/settings.json) by a read-only mount..env,.envrcand.moatdirectly in the project get an empty placeholder for the session even when missing, so they cannot be created. The only network is loopback, where OpenMoat serves the proxy's port and relays it over a Unix socket to the proxy outside. Inside, the Lightweight tier's Landlock rules and seccomp filter apply as well. Wherebwrapis missing or cannot create its namespaces, it refuses (exit 64) and never falls back to the Lightweight tier; macOS refuses until #175. docs/EVIDENCE.md gains a column for it, run by theubuntu-latestCI jobs: a hostilenpm testgetsEACCESreading the project's.env, which plainmoat runon Linux still lets it read.
Security
moat runnow refuses terminal input injection. The agent shares the terminal it was started in, andioctl(TIOCSTI)pushes characters into that terminal's input: once the agent exits, the user's shell reads them and runs them outside every sandbox (the CVE-2017-5226 class). On Linux the seccomp filter, in the Lightweight tier and inside--isolate, refusesioctlwithTIOCSTIorTIOCLINUX(EPERM, x32 included), whateverdev.tty.legacy_tiocstisays. On macOS the Seatbelt profile deniesTIOCSTIon every file. docs/EVIDENCE.md gains a row run on a terminal the test creates:EPERMunder everymoat runcolumn and under Codex's macOS profile; Claude Code starts its commands without a terminal; under Codex's Linux profile only the CI runner's kernel refuses it (EIO,dev.tty.legacy_tiocstiat 0), so on a kernel that allowsTIOCSTIa Codex-sandboxed script can type into the terminal.
Install openmoat 0.1.1
Install prebuilt binaries via shell script
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/crocodile-labs/openmoat/releases/download/v0.1.1/openmoat-installer.sh | shInstall prebuilt binaries via powershell script
powershell -ExecutionPolicy Bypass -c "irm https://github.com/crocodile-labs/openmoat/releases/download/v0.1.1/openmoat-installer.ps1 | iex"Install prebuilt binaries via Homebrew
brew install crocodile-labs/tap/moatDownload openmoat 0.1.1
| File | Platform | Checksum |
|---|---|---|
| openmoat-aarch64-apple-darwin.tar.xz | Apple Silicon macOS | checksum |
| openmoat-x86_64-apple-darwin.tar.xz | Intel macOS | checksum |
| openmoat-x86_64-pc-windows-msvc.zip | x64 Windows | checksum |
| openmoat-aarch64-unknown-linux-gnu.tar.xz | ARM64 Linux | checksum |
| openmoat-x86_64-unknown-linux-gnu.tar.xz | x64 Linux | checksum |
| openmoat-aarch64-unknown-linux-musl.tar.xz | ARM64 MUSL Linux | checksum |
| openmoat-x86_64-unknown-linux-musl.tar.xz | x64 MUSL Linux | checksum |
Verifying GitHub Artifact Attestations
The artifacts in this release have attestations generated with GitHub Artifact Attestations. These can be verified by using the GitHub CLI:
gh attestation verify <file-path of downloaded artifact> --repo crocodile-labs/openmoatYou can also download the attestation from GitHub and verify against that directly:
gh attestation verify <file-path of downloaded artifact> --bundle <file-path of downloaded attestation>0.1.0 - 2026-10-08
Alpha: the hook's decisions are not enforced by the operating system (ADR-013). Only commands inside the host sandboxes
moat initconfigures, or undermoat run, are confined by the OS. Elsewhere an allowed call runs with your permissions, so a classifier mistake is a security bug.
Release Notes
First beta and first release that is not a GitHub pre-release.
Changed
- docs/SANDBOX.md notes a macOS 14 behaviour under
moat run(#361). On some machines, Seatbelt sometimes refuses the allowed loopback connection to the proxy for a few milliseconds about every 15 seconds. A network request then fails at once, and a retry succeeds. A one-rulesandbox-execprofile shows the same without OpenMoat (#280, #351). CI's macOS 15 runners did not show it.
Fixed
moat --helpno longer says the alpha has no OS enforcement; it says where the OS bounds commands and points tomoat status.
Security
- On Linux, Codex's sandbox now keeps project scripts from creating
.envand.envrcin the workspace roots (#377). Codex hides only the files a deny glob matches when a command starts, so a script could create a missing.envrc, which direnv runs in the user's shell.moat initandmoat sandbox syncnow also write.envand.envrcas deny entries under:workspace_rootson Linux; bubblewrap mounts an empty read-only file over a missing one while the command runs, and the script getsEROFS..env.local, a.envrcin a subdirectory and a project'sbin/moatstay creatable for sandboxed commands on Linux;moat sandbox showlists them (codex.linux-write-globs). macOS profiles are unchanged.
Install openmoat 0.1.0
Install prebuilt binaries via shell script
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/crocodile-labs/openmoat/releases/download/v0.1.0/openmoat-installer.sh | shInstall prebuilt binaries via powershell script
powershell -ExecutionPolicy Bypass -c "irm https://github.com/crocodile-labs/openmoat/releases/download/v0.1.0/openmoat-installer.ps1 | iex"Install prebuilt binaries via Homebrew
brew install crocodile-labs/tap/moatDownload openmoat 0.1.0
| File | Platform | Checksum |
|---|---|---|
| openmoat-aarch64-apple-darwin.tar.xz | Apple Silicon macOS | checksum |
| openmoat-x86_64-apple-darwin.tar.xz | Intel macOS | checksum |
| openmoat-x86_64-pc-windows-msvc.zip | x64 Windows | checksum |
| openmoat-aarch64-unknown-linux-gnu.tar.xz | ARM64 Linux | checksum |
| openmoat-x86_64-unknown-linux-gnu.tar.xz | x64 Linux | checksum |
| openmoat-aarch64-unknown-linux-musl.tar.xz | ARM64 MUSL Linux | checksum |
| openmoat-x86_64-unknown-linux-musl.tar.xz | x64 MUSL Linux | checksum |
Verifying GitHub Artifact Attestations
The artifacts in this release have attestations generated with GitHub Artifact Attestations. These can be verified by using the GitHub CLI:
gh attestation verify <file-path of downloaded artifact> --repo crocodile-labs/openmoatYou can also download the attestation from GitHub and verify against that directly:
gh attestation verify <file-path of downloaded artifact> --bundle <file-path of downloaded attestation>0.1.0-alpha.7 - 2026-10-08
Alpha: the hook's decisions are not enforced by the operating system (ADR-013). Only commands inside the host sandboxes
moat initconfigures, or undermoat run, are confined by the OS. Elsewhere an allowed call runs with your permissions, so a classifier mistake is a security bug.
Release Notes
Added
- docs/EVIDENCE.md now covers the Standard tier (#346): CI's new
standard tierjob (macOS and Linux) runs the same hostile project scripts asnpm testunder Claude Code's sandbox and Codex'smoatprofile, each configured with the settingsmoat initgenerates, and asserts what the operating system did (EPERM,EACCES, the agent's proxy's 403) and that nothing was read, written or reached. Claude Code 2.1.290 runs headless (claude -p --bare) against a local fake Anthropic API, and codex-cli 0.160.1 runscodex sandbox -P moat; no account, API key or internet is used, andscripts/ci/host-binaries.shfetches both binaries pinned by checksum. On Linux it found two gaps, now listed there: Claude Code's sandbox skips the generated.envdenies, so a script reads the project.env(#359), and Codex runs no command at all under the generated profile, failing closed (#358). Windows is listed as not run, with the reason. moat initandmoat sandbox syncconfigure Cursor's own sandbox from the policy (#324), as they do for Claude Code and Codex:sandbox.jsonnext to Cursor'shooks.json(~/.cursor, or$CURSOR_CONFIG_DIR) getstype: "workspace_readwrite",readBoundary: "workspace", the read roots asadditionalReadPaths, write allow rules outside the project asadditionalReadwritePaths, and the policy's hosts asnetworkPolicywithdefault: "deny". Other keys are kept, the file is backed up first and pinned whole by the lock,moat doctornames a weakened setting, andmoat uninstallremoves exactly these keys.moat statusreports Cursor ashook + OS sandboxwhen the file matches. Cursor's schema has no key that denies a path: a read root with a denied path below it (~/.cargo) is left out, and what the policy denies inside the workspace (.env,.git,.moat) stays open to sandboxed commands;moat sandbox showlists both, and that Cursor runs a command outside its sandbox when its Auto-review classifier approves it, in Run Everything mode and in the CLI without--sandbox enabled. Not configured on native Windows, where Cursor documents no sandbox. The keys follow Cursor 3.23's documentation and were not yet run under a Cursor build.
Changed
- README leads with what sets OpenMoat apart: one policy that configures every enforcing layer, with published evidence; the comparison adds failing closed and published evidence.
Fixed
- Codex on Linux runs commands again under the profile
moat initgenerates (#358). Before it starts a command, Codex on Linux lists every file below each deny glob withrg --filesso bubblewrap can hide the matches, and it refuses to start the command when ripgrep reports any error; the profile repeated the**/.env,**/.env.*and**/.envrcdenies below every read root, and root-only directories such as/etc/ssl/privateand/tmp/systemd-private-*stopped every command. The profile now repeats those denies only in the project and below the read roots inside the home;moat sandbox showlists the roots outside it (codex.outside-home), where the hook still denies the agent's own reads. docs/EVIDENCE.md's Codex Linux column now shows what the operating system does to each hostile script instead of a sandbox error. - Codex on Linux starts commands under the generated profile wherever it is installed (#371). It runs its own executable again inside bubblewrap to apply seccomp but grants itself no read access to it, so it started commands only when installed under a read root. On Linux,
moat initandmoat sandbox syncnow let commands read the executablecodexonPATHstarts (through npm'scodex.jslauncher, the platform binary), listed bymoat sandbox showascodex.own-binary; runmoat sandbox syncagain after moving Codex. Whencodexis not onPATH, or its executable is inside a denied path such as the Codex home (Codex's standalone installer), nothing is granted andmoat sandbox showsays so. macOS profiles are unchanged.
Security
- On Linux, Claude Code's sandbox now keeps project scripts away from the project's
.envfiles (#359). Bubblewrap needs concrete paths, so the sandbox expands eachdenyReadglob from its first literal directory when a command starts, and it skipped the generated/**/.env,/**/.env.*and/**/.envrc:npm testcould print the.env.moat initandmoat sandbox syncnow also writeRead(./**/.env),Read(./**/.env.*)andRead(./**/.envrc)topermissions.denyon Linux. Claude Code expands those under the session's working directory, and a script reading a match getsEACCES(CI'sstandard tier (ubuntu-latest)job, Claude Code 2.1.290). A file created after a command starts, or a.envin another readable directory, stays readable, and Claude Code's file tools now refuse.env.examplethere too;moat sandbox showlists both. macOS settings are unchanged, andmoat uninstallremoves the rules. - The default policy denies agent writes to
~/.cursor/sandbox.jsonand a project's.cursor/sandbox.json(kernel-self, #324): a project file replaces Cursor's read boundary and adds hosts to its sandbox. - Glob characters in a path operand no longer get past deny rules (#355). The shell expands an unquoted
*,?or[before the command runs, but the operand was checked only as the literal word, socat .en?,cat .e*,cat .[e]nvandhead -c 99 .en[v]were allowed andcat ~/.ss?/id_rsaonly asked whilecat .envwas denied. Such an operand, and a redirection target, is now checked as written and as every path it names in the real directories, resolved through symlinks, withbashmatching rules (*skips dotfiles unless the pattern starts with.; an unmatched pattern is only its literal word). Quoted and escaped patterns stay literal, and directories are listed only for glob operands. A call whose globs name more than 256 paths, list more than 1024 directories or descend more than 8 levels of**asks (unparseable), and a deny among the named paths still wins. On hook-only setups (the Cursor editor, Claude Code on native Windows) this read secrets; the Standard tier's sandbox already blocked it. - Brace expansion no longer gets past deny rules (#362). Bash makes several words of one with unquoted braces before anything else, so
cat .{env,x}runscat .env .x, but the word was checked as written and allowed;cat .e{n,m}v,cat ~/.ssh/{id_rsa,x}and{cat,.env}got through the same way. Words are now expanded asbashdoes it (comma lists, nested braces,{1..3},{a..e..2},{01..10}, several groups in one word, the command name included) and every word made is checked, and globbed when it holds* ? [. Quoted and escaped braces,{},{x}and${…}stay literal. A word whose braces make more than 256 words asks (unparseable), and a deny elsewhere in the call still wins.moat allow --alwaysof such a command writes the words it makes ({cat,.env}→cat .env), which is what the rule is matched against.
Install openmoat 0.1.0-alpha.7
Install prebuilt binaries via shell script
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/crocodile-labs/openmoat/releases/download/v0.1.0-alpha.7/openmoat-installer.sh | shInstall prebuilt binaries via powershell script
powershell -ExecutionPolicy Bypass -c "irm https://github.com/crocodile-labs/openmoat/releases/download/v0.1.0-alpha.7/openmoat-installer.ps1 | iex"Install prebuilt binaries via Homebrew
brew install crocodile-labs/tap/moatDownload openmoat 0.1.0-alpha.7
| File | Platform | Checksum |
|---|---|---|
| openmoat-aarch64-apple-darwin.tar.xz | Apple Silicon macOS | checksum |
| openmoat-x86_64-apple-darwin.tar.xz | Intel macOS | checksum |
| openmoat-x86_64-pc-windows-msvc.zip | x64 Windows | checksum |
| openmoat-aarch64-unknown-linux-gnu.tar.xz | ARM64 Linux | checksum |
| openmoat-x86_64-unknown-linux-gnu.tar.xz | x64 Linux | checksum |
| openmoat-aarch64-unknown-linux-musl.tar.xz | ARM64 MUSL Linux | checksum |
| [openmoat-x86_64-unknown-linux-musl.tar.xz](https://github.com/crocodile-labs/openmoat/releases/download/v0.1.0-alpha.7/openmo... |
0.1.0-alpha.6 - 2026-10-08
Alpha: the hook's decisions are not enforced by the operating system (ADR-013). Only commands inside the host sandboxes
moat initconfigures, or undermoat run, are confined by the OS. Elsewhere an allowed call runs with your permissions, so a classifier mistake is a security bug.
Release Notes
Added
- MoatBench measures prompts on real development work (#336):
tests/moatbench/workflows.yamlruns step-by-step Rust, Node, Python, Go, git, Docker and Make workflows, and project-file edits, through Claude Code, Codex and Cursor payloads. Each step expectsallow, oraskwhere the default policy means to (new dependencies,git push,docker build), and the scorecard prints the asks per workflow (workflows: 152 steps, 26 asks (expected 17), 0 unexpected, 9 expected failures). A policy change that adds an ask or deny to one of these steps fails CI. Four known false positives of the default policy are recorded as expected failures:python3 -m venv .venv,source .venv/bin/activate,git rebase mainand a baremakeask. Steps can now be file edits (edit:, sent as Claude CodeEdit, a Codexapply_patchUpdate File, and CursorWrite) and can carry their owngapmarker. - docs/EVIDENCE.md: what the OS layer does to hostile project scripts that the hook allows (#335). The differential suite runs each payload as
npm testundermoat run, in a throwaway home with fake secrets, and asserts the outcome on every pull request: on macOS (Seatbelt) and Linux (Landlock and seccomp), reading~/.ssh/id_rsaor~/.aws/credentials, writing outside the project, a direct TCP connection, a DNS query over UDP, a symlink from the project into~/.ssh, and editing the policy or Claude Code's settings fail withEPERMorEACCES, and an unlisted host gets the proxy's 403. On Linux a project.envstays readable, shown as a known gap. The Codex and Claude Code sandboxes are not run in CI and are listed as not verified. Regenerate withMOAT_UPDATE_EVIDENCE=1 cargo test -p openmoat --test e2e differential. moat statusandmoat doctorprint one protection level per agent, derived only from the hook and sandbox checks they already make (#334):hook + OS sandbox(hook installed and current, generated sandbox in place and matching the policy),hook only(the Cursor editor, Claude Code on native Windows, or a sandbox that is missing, weakened or out of date, with the reason), ornot protected(hook missing, out of date or unreadable), followed by one line of what that agent's hook and sandbox do not cover.moat status --format jsonprints the same per agent. Themoathome screen names each protected agent's level (Protecting Claude Code (hook + OS sandbox)). The level never changes whetherstatusordoctorreports a problem.
Changed
- THREAT_MODEL no longer lists Claude Code
SendFileas ungoverned: the hook checks its files as reads. - Default policy: four steps of everyday development work that asked only through the catch-all default are now allowed by
dev-shell(#350), and the MoatBench workflow suite has no known false positives left (workflows: 152 steps, 19 asks (expected 19), 0 unexpected, 0 expected failures).python -m venv DIRandpython3 -m venv DIR: each directory is now anfs.write, a plain name included, so a venv outside the project, in.gitor over~/.moatasks or is denied;--clearand--upgrade/--upgrade-depsask.source .venv/bin/activateand. .venv/bin/activate(alsovenv/), spelled exactly and with no arguments; any other file, a path with.., or the same spelling after acdout of the project asks.git rebase <ref>, likegit pull --rebase;--exec/-x,--interactive/-i,--edit-todo,--continue,--skipand--strategy/-sask, in any prefix git accepts and inside short-option clusters. A baremake, likemake test;make -f,make -Cand the existing recipe-shell overrides still ask. Stricter:npm execandnpm xnow ask (installs) likenpx, since both download a package they cannot find. - README, the CLI crate README and docs/INSTALL.md no longer say every action is checked: they say OpenMoat checks the commands, file access, web requests and MCP calls the agents report through their hooks, which is what README Limits and THREAT_MODEL describe (#331).
- A permanent approval (
moat allow --always,moat allow --last --always, orainmoat) prints what the rule will match before writing it (#333). For a shell command:will allow: npm test (and the same command with extra arguments); deny rules still win, since a command rule matches the approved command as a prefix. For files it says the rule names exactly those paths. The undo line is unchanged, and so is what a rule matches. - The native Windows note for Claude Code now says
the hook still applies the policyinstead ofthe hook still checks every call, since some tools are not hooked (#331). moat doctor's Cursor note is now its protection line,hook only, with the same reason: no OS sandbox from OpenMoat, andmoat runcovers the Cursor CLI.
Security
- A part of a call the engine cannot classify no longer hides the decisions of the other parts (#345). It is one more
ask(ruleunparseable) merged with them, strictest wins, so adenystands: an MCP call that reads~/.ssh/id_rsaand adds aurlwith no host,cd "$X" && cat notes ~/.ssh/id_rsa,bash --frobnicate -c x; cat ~/.ssh/id_rsaandecho $(bash --frobnicate -c x) $(cat ~/.ssh/id_rsa)wereaskand are now denied bysecrets-paths, andbash -c "eval eval eval eval eval eval true"bypipe-to-shell. The shell classifier keeps classifying the other simple commands after one it cannot, and the other paths of a command after one in an unknown directory. A call whose unclassifiable part is all it does still asks. Under a policy whose default isdeny, a classified part that no rule allows is now denied even when another part cannot be classified. Session taint also counts the classified parts of such a call. moat guarddenies (exit 2) when it has not decided within 10 seconds, instead of waiting for the host's hook timeout, after which Claude Code, Codex and the Continue CLI run the call (#337). The deny is not recorded, since what hangs may be the audit log. docs/THREAT_MODEL.md §5 has a table, with sources, of what each host does when the hook binary is missing, crashes, times out or prints bad output, and whatguardanswers for a malformed payload, an unknown event and a tool OpenMoat does not know; end-to-end tests cover OpenMoat's side for each host's payload shape, and thatmoat statusandmoat doctorreport a hook whose binary is gone asnot protected. Theknown gapslines now say Claude Code and Codex also run the call when the hook crashes, and that Cursor blocks it.- The audit log masks the exact values of the policy's brokered secrets (
secrets:), whatever their format, before an event is stored, so they reach neitheraudit.dbnormoat audit export(#338). Previously only known token formats were redacted, and a value sent as part of a host name was recorded bymoat proxy. Matching ignores letter case and skips values shorter than 8 bytes, which occur in ordinary text.moat guardmasks the values with afileorenvsource it can read in every call the policy decides; the proxy masks all of them. The working directory is now redacted too. Canary tests plant fake secrets in shell commands, paths, URLs, proxy headers and MCP arguments and check that none is stored and the hash chain still verifies. - An MCP call whose arguments nest a path or URL deeper than 8 levels is now
ask(ruleunparseable, reasonmcp arguments not fully checked: nested deeper than 8 levels) instead of being decided on the tool name alone (#332). The paths and URLs found above that depth are still checked, so adenyamong them still wins. More than 1024 paths and URLs still denies the call.
Install openmoat 0.1.0-alpha.6
Install prebuilt binaries via shell script
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/crocodile-labs/openmoat/releases/download/v0.1.0-alpha.6/openmoat-installer.sh | shInstall prebuilt binaries via powershell script
powershell -ExecutionPolicy Bypass -c "irm https://github.com/crocodile-labs/openmoat/releases/download/v0.1.0-alpha.6/openmoat-installer.ps1 | iex"Install prebuilt binaries via Homebrew
brew install crocodile-labs/tap/moatDownload openmoat 0.1.0-alpha.6
| File | Platform | Checksum |
|---|---|---|
| openmoat-aarch64-apple-darwin.tar.xz | Apple Silicon macOS | checksum |
| openmoat-x86_64-apple-darwin.tar.xz | Intel macOS | checksum |
| openmoat-x86_64-pc-windows-msvc.zip | x64 Windows | checksum |
| openmoat-aarch64-unknown-linux-gnu.tar.xz | ARM64 Linux | checksum |
| [openmoat-x86_64-unknown-linux-gnu.tar.xz](https://github.com/crocodile-labs/openmoat/... |
0.1.0-alpha.5 - 2026-10-07
Alpha: the hook's decisions are not enforced by the operating system (ADR-013). Only commands inside the host sandboxes
moat initconfigures, or undermoat run, are confined by the OS. Elsewhere an allowed call runs with your permissions, so a classifier mistake is a security bug.
Release Notes
Added
- docs/ADDING_AN_AGENT.md: how to add support for another AI coding agent, from mapping its hook payload to an action, through installing the hook and capturing fixtures, to what the pull request must include (#319). Linked from CONTRIBUTING.md and the README.
- docs/SANDBOX.md: how to run the Cursor CLI (
agent) undermoat runon macOS: its installation directory insandbox.read_roots, an allow rule forapi2.cursor.sh,--write ~/.cursor,CURSOR_API_KEYinstead of the keychain, and--sandbox disabledfor that run (#324). moat doctornotes, when the Cursor hook is installed, that OpenMoat gives Cursor no OS sandbox, so only the hook applies the policy, and thatmoat runcovers the Cursor CLI (#324).
Changed
- README, docs/INSTALL.md and docs/THREAT_MODEL.md no longer say that Cursor has no sandbox of its own. Cursor has one;
moat initdoes not configure it, and Cursor can rerun a command outside it, so in the Cursor editor only the hook applies the policy (#324).
Fixed
- On native Windows,
moat initandmoat sandbox syncno longer write Claude Code'ssandboxblock orpermissions.blockReadsOutsideWorkingDirectories(#327). Claude Code's sandbox runs on macOS, Linux and WSL2 only, and withfailIfUnavailable: trueClaude Code exits at startup where it cannot run.init,sandbox sync,sandbox show,doctorandstatusnow saysandbox not available on native Windows; the hook still checks every call (use WSL2 for OS confinement)for Claude Code instead of reporting the sandbox missing or weakened. Runmoat initormoat sandbox syncagain to remove those settings from an earlier version (they keep your other settings and re-pin the file). Codex is unchanged.
Install openmoat 0.1.0-alpha.5
Install prebuilt binaries via shell script
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/crocodile-labs/openmoat/releases/download/v0.1.0-alpha.5/openmoat-installer.sh | shInstall prebuilt binaries via powershell script
powershell -ExecutionPolicy Bypass -c "irm https://github.com/crocodile-labs/openmoat/releases/download/v0.1.0-alpha.5/openmoat-installer.ps1 | iex"Install prebuilt binaries via Homebrew
brew install crocodile-labs/tap/moatDownload openmoat 0.1.0-alpha.5
| File | Platform | Checksum |
|---|---|---|
| openmoat-aarch64-apple-darwin.tar.xz | Apple Silicon macOS | checksum |
| openmoat-x86_64-apple-darwin.tar.xz | Intel macOS | checksum |
| openmoat-x86_64-pc-windows-msvc.zip | x64 Windows | checksum |
| openmoat-aarch64-unknown-linux-gnu.tar.xz | ARM64 Linux | checksum |
| openmoat-x86_64-unknown-linux-gnu.tar.xz | x64 Linux | checksum |
| openmoat-aarch64-unknown-linux-musl.tar.xz | ARM64 MUSL Linux | checksum |
| openmoat-x86_64-unknown-linux-musl.tar.xz | x64 MUSL Linux | checksum |
Verifying GitHub Artifact Attestations
The artifacts in this release have attestations generated with GitHub Artifact Attestations. These can be verified by using the GitHub CLI:
gh attestation verify <file-path of downloaded artifact> --repo crocodile-labs/openmoatYou can also download the attestation from GitHub and verify against that directly:
gh attestation verify <file-path of downloaded artifact> --bundle <file-path of downloaded attestation>0.1.0-alpha.4 - 2026-10-07
Alpha: the hook's decisions are not enforced by the operating system (ADR-013). Only commands inside the host sandboxes
moat initconfigures, or undermoat run, are confined by the OS. Elsewhere an allowed call runs with your permissions, so a classifier mistake is a security bug.
Release Notes
Changed
- README opens with a 40-second recording of the launch demo (
docs/assets/demo.gif, from realmoat guardverdicts). The demo script shortens the throwaway paths and the binary path in its output, and docs/DEMO.md no longer says the operating system does not enforce decisions. - docs/INSTALL.md says how to upgrade:
brew update && brew upgrade moat, sincebrew upgradealone may not see a new release of the tap (#314). - After a session grant (
oinmoat, ormoat allow --lastwithout--always),moatsays that the grant covers only this agent session and that a new session asks again; for Claude Code,claude --continueresumes the session (#312).
Fixed
moat initno longer leaves out an agent silently whenCLAUDE_CONFIG_DIR,CODEX_HOMEorCURSOR_CONFIG_DIRnames a directory that does not exist yet (#313). It prints one line per such agent with the variable and the directory, and says to start the agent once to create it and then runmoat initagain. It does not create the directory.
Install openmoat 0.1.0-alpha.4
Install prebuilt binaries via shell script
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/crocodile-labs/openmoat/releases/download/v0.1.0-alpha.4/openmoat-installer.sh | shInstall prebuilt binaries via powershell script
powershell -ExecutionPolicy Bypass -c "irm https://github.com/crocodile-labs/openmoat/releases/download/v0.1.0-alpha.4/openmoat-installer.ps1 | iex"Install prebuilt binaries via Homebrew
brew install crocodile-labs/tap/moatDownload openmoat 0.1.0-alpha.4
| File | Platform | Checksum |
|---|---|---|
| openmoat-aarch64-apple-darwin.tar.xz | Apple Silicon macOS | checksum |
| openmoat-x86_64-apple-darwin.tar.xz | Intel macOS | checksum |
| openmoat-x86_64-pc-windows-msvc.zip | x64 Windows | checksum |
| openmoat-aarch64-unknown-linux-gnu.tar.xz | ARM64 Linux | checksum |
| openmoat-x86_64-unknown-linux-gnu.tar.xz | x64 Linux | checksum |
| openmoat-aarch64-unknown-linux-musl.tar.xz | ARM64 MUSL Linux | checksum |
| openmoat-x86_64-unknown-linux-musl.tar.xz | x64 MUSL Linux | checksum |
Verifying GitHub Artifact Attestations
The artifacts in this release have attestations generated with GitHub Artifact Attestations. These can be verified by using the GitHub CLI:
gh attestation verify <file-path of downloaded artifact> --repo crocodile-labs/openmoatYou can also download the attestation from GitHub and verify against that directly:
gh attestation verify <file-path of downloaded artifact> --bundle <file-path of downloaded attestation>0.1.0-alpha.3 - 2026-10-07
Alpha: the hook's decisions are not enforced by the operating system (ADR-013). Only commands inside the host sandboxes
moat initconfigures, or undermoat run, are confined by the OS. Elsewhere an allowed call runs with your permissions, so a classifier mistake is a security bug.
Release Notes
Added
- Policy key
taint.protected_writes(#215): path globs that a write to asks once the session read untrusted content, in addition to the built-in list (CI, git hooks, build scripts, agent instructions). It extends the list and cannot shorten it;moat policy lintrejects!exclusions and globs that do not compile. Policies without the key behave as before. moat allow --lastandmoat allow --last --alwaysapprove file actions too, not only shell commands (#278). The last ask can be a file read, write or delete from a Claude Code file tool, a Codexapply_patchor a Cursor file tool. A session grant covers exactly those files for that action;--alwaysadds anfs.readorfs.writerule for exactly those paths (as asked and with symlinks resolved) and prints it with its undo command. Deny rules still win.moat(the home screen) shows and approves the newest file ask the same way.~/.moat/approvals.jsonmoves to version 2, which records the approved action; version 1 files are still read and are rewritten as version 2 on the next grant.
Changed
- When Codex or a Cursor file tool cannot prompt, the deny message now says to run
moatormoat allow --last(Cursor's said to add a policy rule and runmoat doctor --accept). The Continue CLI message namesmoattoo.
Fixed
moatsays1 decisioninstead of1 decisions(#278).
Install openmoat 0.1.0-alpha.3
Install prebuilt binaries via shell script
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/crocodile-labs/openmoat/releases/download/v0.1.0-alpha.3/openmoat-installer.sh | shInstall prebuilt binaries via powershell script
powershell -ExecutionPolicy Bypass -c "irm https://github.com/crocodile-labs/openmoat/releases/download/v0.1.0-alpha.3/openmoat-installer.ps1 | iex"Install prebuilt binaries via Homebrew
brew install crocodile-labs/tap/moatDownload openmoat 0.1.0-alpha.3
| File | Platform | Checksum |
|---|---|---|
| openmoat-aarch64-apple-darwin.tar.xz | Apple Silicon macOS | checksum |
| openmoat-x86_64-apple-darwin.tar.xz | Intel macOS | checksum |
| openmoat-x86_64-pc-windows-msvc.zip | x64 Windows | checksum |
| openmoat-aarch64-unknown-linux-gnu.tar.xz | ARM64 Linux | checksum |
| openmoat-x86_64-unknown-linux-gnu.tar.xz | x64 Linux | checksum |
| openmoat-aarch64-unknown-linux-musl.tar.xz | ARM64 MUSL Linux | checksum |
| openmoat-x86_64-unknown-linux-musl.tar.xz | x64 MUSL Linux | checksum |
Verifying GitHub Artifact Attestations
The artifacts in this release have attestations generated with GitHub Artifact Attestations. These can be verified by using the GitHub CLI:
gh attestation verify <file-path of downloaded artifact> --repo crocodile-labs/openmoatYou can also download the attestation from GitHub and verify against that directly:
gh attestation verify <file-path of downloaded artifact> --bundle <file-path of downloaded attestation>0.1.0-alpha.2 - 2026-10-07
Alpha: the hook's decisions are not enforced by the operating system (ADR-013). Only commands inside the host sandboxes
moat initconfigures, or undermoat run, are confined by the OS. Elsewhere an allowed call runs with your permissions, so a classifier mistake is a security bug.
Release Notes
Added
moat allow --site <host>,moat allow --dir <path>andmoat allow --remove <id>(#300).--siteadds anetallow for one host name; it also covers web fetches.--diradds anfs.readandfs.writeallow for an existing directory and everything below it, stored as written and with its symlinks resolved. Both append to~/.moat/policy.d/approved.yaml, check that the merged policy lints, re-pin, and print the rule as written plus themoat allow --removecommand that undoes it. Deny rules still win. Wildcards, URLs, the home directory, its ancestors and filesystem roots are refused.--removetakes out anyapproved-Nrule,--alwaysones included, and--alwaysnow prints its undo command too. New attack fixtures show that an agent runningmoat allow --siteor--diris denied (kernel-self).moat edit(#300) opens~/.moat/policy.yamlin$VISUAL,$EDITOR,vior (Windows)notepad, on a copy inside~/.moat. An unchanged copy changes nothing. A copy that does not lint is reported and never written, and you can reopen it. Otherwise it prints the lint warnings and a unified diff, and writes and re-pins only afterApply? [y/N]is answeredy. The previous policy is kept in~/.moat/policy.yaml.bak. It needs a terminal and an intact lock, likemoat allow.moat uninstall [--hosts …] [--purge]removes OpenMoat's hooks and sandbox settings from the agents and prints what it did per file (#282). A file unchanged sincemoat initgets its backup back byte for byte, a fileinitcreated is deleted, and a file changed since keeps those changes. The lock stops pinning the undone files;~/.moatstays unless--purge. It runs only from a terminal.moat initrecords the config directory of each agent it sets up in~/.moat/hosts.json, which the lock pins (#298).status,doctor,allow,sandbox syncanduninstalluse the recorded directories, so a customCLAUDE_CONFIG_DIR,CODEX_HOMEorCURSOR_CONFIG_DIRno longer has to be exported for every command.moat doctorreports a variable that names another directory than the record instead of following it;moat initwith the variable set moves the record.kernel-selfprotects both the recorded directory and the one the variable names. Nothing changes for agents in their default directories.moatwith no subcommand shows one health line (the agents whose hook is installed, today's decisions with the number denied and asked) and then what needs a person (#299). Lock drift is listed andAccept these changes? [y/N]runsmoat doctor --acceptony. The newest shellaskfrom today that no grant or permanent rule covers is shown with its rule and asksAllow? [o]nce for this session / [a]lways / [n]o, which runsmoat allowfor exactly that command. Otherwise it printsNothing needs you.Without a terminal it prints the command to run and changes nothing.
Changed
moat initasks before changing an agent (#282). At a terminal it lists the agents it found with the files it would change and asksProtect Claude Code (…)? [Y/n]for each, then names the backups andUndo anytime: moat uninstall.--hostsand the new--yesskip the questions. Without a terminal and without either flag,initsets up~/.moatbut changes no agent's files, and says how to go on: scripts that ranmoat initneed--yes.
Fixed
moat statusandmoat doctorshow an agent thatmoat initleft out (the person said no, or named other--hosts) as· not set upinstead of a missing hook and five sandbox problems. Lock drift on a Claude Code settings file no longer namestheme, which is not pinned (#287).
Security
kernel-selfdenies anymoatcommand run under a pseudo-terminal wrapper (script,unbuffer,tmux,screen, quoted forexpect, Pythonpty.spawn,osascript), so an agent cannot answer the questions of a baremoat(#299). An agent runningmoatormoat statuswithout a wrapper still getsask, and without a terminalmoatchanges nothing.kernel-selfdeniesmoat editto agents, by name, by absolute path and under every pseudo-terminal wrapper already listed formoat allow(#300).kernel-selfdenies agent runs ofmoat uninstall, including through pseudo-terminal wrappers, likemoat allow(#282).
Install openmoat 0.1.0-alpha.2
Install prebuilt binaries via shell script
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/crocodile-labs/openmoat/releases/download/v0.1.0-alpha.2/openmoat-installer.sh | shInstall prebuilt binaries via powershell script
powershell -ExecutionPolicy Bypass -c "irm https://github.com/crocodile-labs/openmoat/releases/download/v0.1.0-alpha.2/openmoat-installer.ps1 | iex"Install prebuilt binaries via Homebrew
brew install crocodile-labs/tap/moatDownload openmoat 0.1.0-alpha.2
| File | Platform | Checksum |
|---|---|---|
| openmoat-aarch64-apple-darwin.tar.xz | Apple Silicon macOS | checksum |
| openmoat-x86_64-apple-darwin.tar.xz | Intel macOS | checksum |
| openmoat-x86_64-pc-windows-msvc.zip | x64 Windows | checksum |
| openmoat-aarch64-unknown-linux-gnu.tar.xz | ARM64 Linux | checksum |
| openmoat-x86_64-unknown-linux-gnu.tar.xz | x64 Linux | checksum |
| openmoat-aarch64-unknown-linux-musl.tar.xz | ARM64 MUSL Linux | checksum |
| openmoat-x86_64-unknown-linux-musl.tar.xz | x64 MUSL Linux | checksum |
Verifying GitHub Artifact Attestations
The artifacts in this release have attestations generated with GitHub Artifact Attestations. These can be verified by using the GitHub CLI:
gh attestation verify <file-path of downloaded artifact> --repo crocodile-labs/openmoatYou can also download the attestation from GitHub and verify against that directly:
gh attestation verify <file-path of downloaded artifact> --bundle <file-path of downloaded attestation>0.1.0-alpha.1 - 2026-10-07
Alpha: the hook's decisions are not enforced by the operating system (ADR-013). Only commands inside the host sandboxes
moat initconfigures, or undermoat run, are confined by the OS. Elsewhere an allowed call runs with your permissions, so a classifier mistake is a security bug.
Release Notes
Added
- Lock drift on a JSON or TOML file names the top-level keys that changed since the pin, in
moat doctor, inmoat doctor --acceptbefore it re-pins, and inguard'skernel-integrityreasons:settings.json was modified: changed hooks; added theme(#288). The lock records a digest per top-level key for those files; a lock written earlier keeps the plain message until its next re-pin.
Changed
moat doctorandmoat runprint one line with the number of places a sandbox is stricter or wider than the policy instead of the whole list;--verboseprints the list (#289). Problems (a weakened setting, drift) still print in full, andmoat sandbox showstill prints everything.moat showlabels its time columntime (UTC)andmoat replayaddsUTCto each session's start time; both were already UTC but unlabelled (#290).
Fixed
- The policy lock leaves the top-level
themekey out of Claude Code'ssettings.jsondigest, so Claude Code writing"theme"on first run no longer denies every call withkernel-integrity(#287). Every other key stays pinned, a file that does not parse is drift, and a lock pinned before this change keeps verifying until the next re-pin.
Security
kernel-selfcovers the state and host directories whereMOAT_HOME,CLAUDE_CONFIG_DIR,CODEX_HOMEorCURSOR_CONFIG_DIRmoved them (#286). A write to$MOAT_HOME/policy.yamlor$CLAUDE_CONFIG_DIR/settings.jsonwas asked (default) instead of denied; the lock caught the change only afterwards. Patterns naming~/.moat,~/.claude,~/.codexor~/.cursornow also match the moved directory, in the hook,moat policy check,moat runand the Standard-tier sandbox settings.
Install openmoat 0.1.0-alpha.1
Install prebuilt binaries via shell script
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/crocodile-labs/openmoat/releases/download/v0.1.0-alpha.1/openmoat-installer.sh | shInstall prebuilt binaries via powershell script
powershell -ExecutionPolicy Bypass -c "irm https://github.com/crocodile-labs/openmoat/releases/download/v0.1.0-alpha.1/openmoat-installer.ps1 | iex"Install prebuilt binaries via Homebrew
brew install crocodile-labs/tap/moatDownload openmoat 0.1.0-alpha.1
| File | Platform | Checksum |
|---|---|---|
| openmoat-aarch64-apple-darwin.tar.xz | Apple Silicon macOS | checksum |
| openmoat-x86_64-apple-darwin.tar.xz | Intel macOS | checksum |
| openmoat-x86_64-pc-windows-msvc.zip | x64 Windows | checksum |
| openmoat-aarch64-unknown-linux-gnu.tar.xz | ARM64 Linux | checksum |
| openmoat-x86_64-unknown-linux-gnu.tar.xz | x64 Linux | checksum |
| openmoat-aarch64-unknown-linux-musl.tar.xz | ARM64 MUSL Linux | checksum |
| openmoat-x86_64-unknown-linux-musl.tar.xz | x64 MUSL Linux | checksum |
Verifying GitHub Artifact Attestations
The artifacts in this release have attestations generated with GitHub Artifact Attestations. These can be verified by using the GitHub CLI:
gh attestation verify <file-path of downloaded artifact> --repo crocodile-labs/openmoatYou can also download the attestation from GitHub and verify against that directly:
gh attestation verify <file-path of downloaded artifact> --bundle <file-path of downloaded attestation>0.1.0-alpha.0 - 2026-10-07
Alpha: the hook's decisions are not enforced by the operating system (ADR-013). Only commands inside the host sandboxes
moat initconfigures, or undermoat run, are confined by the OS. Elsewhere an allowed call runs with your permissions, so a classifier mistake is a security bug.
Release Notes
Added
- Standard tier (ADR-018, #168, #169):
moat initconfigures Claude Code's and Codex's own sandboxes from the policy through the enforcement IR (ADR-019). Claude Code's user settings get thesandboxblock (enabled,failIfUnavailable: true,allowUnsandboxedCommands: false,excludedCommands: [], absolute deny and allow lists,network.allowedDomainswithstrictAllowlist) andpermissions.blockReadsOutsideWorkingDirectories: true; Codex'sconfig.tomlgets a[permissions.moat]profile,default_permissions = "moat"andfeatures.network_proxy = true. Other keys and comments are kept; each file is backed up to<file>.moat-sandbox-backupfirst.moat sandbox showprints what the policy compiles to with every loss (stricter) and allowance (wider);moat sandbox syncrewrites and re-pins (a person at a terminal, refused over a drifted lock).moat doctorandmoat statuscheck both hosts and print the losses. - Policy: the additive
sandbox: { read_roots: [...] }section lists paths the host sandboxes may read although no allow rule covers them (toolchains, system and temp directories). Only OS layers use it; the hook still asks for those reads and deny rules win inside them. The default policy lists a cross-platform set; a policy without the section gets that list. - Differential suite (ADR-019, #170):
tests/differential/scenarios.yamlruns each attack, benign flow and public sandbox-escape replay against every enforcement point on the machine (the hook decision, and each host sandbox whose binary is present) and fails on a layer that disagrees with the recorded verdict. A missing host binary skips that layer visibly.scripts/ci/differential.shruns the full suite. - Differential suite: the Codex host-sandbox layer (
codex sandbox -P moat) runs every scenario, withnpm/cargoshims so the hook sees an allowednpm test/cargo buildwhile the payload inside the script is what the sandbox must stop. It confirms Codex blocks every filesystem attack (secret reads, writes outside the project, shell-rc,.githooks, settings-escape, symlink read-escape) and the documented narrower results (a.env.exampleread and.gitwrites, sogit status/git commit, are denied). It surfaced a gap (#229):codex sandboxleaves direct network open, so the generated egress allowlist is enforced only by the proxy (ADR-020); the two egress rows are marked as a known gap pointing at #229, visible in the matrix. - Differential suite: the Claude Code host-sandbox layer runs the real
claude -p --bareagainst a fake Anthropic API (tests/differential/fake_api.py) with the settingsmoat sandbox showgenerates (no hook), confirming Claude Code's sandbox blocks every attack, direct egress to link-local metadata included. Benign project work cannot be verified headlessly (Claude Code establishes working directories interactively), so those rows are skipped with a visible notice pointing at #238. Action::McpToolcarries the paths and hosts an adapter derived from the call's arguments (reads,writes,hosts); the engine turns them intofs.read,fs.writeandnetatoms, so an allowed MCP tool name can no longer read~/.aws/credentialsor fetch an unlisted host. Conformance fixtures acceptmcp_tool: { name, reads, writes, hosts }.- Claude Code and Cursor adapters derive those paths and hosts from MCP
tool_inputby argument name (path,paths,file_path,source,destination,url, …); write-shaped tool names (write_*,edit_*,move_*,delete_*) anddestination/targetarguments producefs.write. - Policy
secrets:(ADR-020,docs/POLICY.md§2.1): brokered secrets, each with one exacthost, aheaderand asource(file,envorkeychain). Ids, hosts, header names and sources are validated, framing headers (Host,Content-Length, …) are refused, and a secret opens no host by itself.moat policy lintwarns when no allow rule names the host, or when no deny rule keeps afileorenvsource from the agent. The optionalplain_http: trueallows injection into clear-text HTTP; it is off by default, and lint warns when it is set for a host that is not loopback. The agent's placeholder ismoat-secret:<id>:placeholder. openmoat-proxyleak blocking for brokered secrets (ADR-020). It refuses a request whose head, or plain-HTTP body, carries a secret's placeholder or value to a host other than that secret's own, and records it asproxy-secret. Inside a body the check spans read boundaries, and the chunk that completes the secret is not forwarded. Values are zeroed on drop (zeroize) and never formatted, so reasons andDebugname only the id and host. The check matches exact bytes;docs/THREAT_MODEL.md§5 lists what it does not stop.openmoat-proxyinjection for brokered secrets over plain HTTP (ADR-020), only for secrets that setplain_http: true. Without it, the request is forwarded with the placeholder, and the audit row records that the secret was not injected. In a request to the secret's host, the placeholder in the secret's header becomes the value, and the header is added when the request has none. The rewritten head is zeroed after it is written. HTTPS is not decrypted, so it gets no injection yet.moat proxybrokers the policy'ssecrets:. It reads eachfile,envorkeychainsource at start-up (macOS/usr/bin/security, Linux/usr/bin/secret-tool; Windows keychains are not supported yet) and exits 64 when one cannot be read. It prints each secret's placeholder for the agent and never the value.- MoatBench mini (
docs/MOATBENCH.md, #132): end-to-end scenarios intests/moatbench/<category>.yamlsent through the realmoat guardas Claude Code, Codex and Cursor payloads. Each step's verdict is read back from the audit log and checked against the host's answer; a scorecard lists verdicts per category, false positives and known gaps (scenarios marked with their issue). Runs in the quality gate. - Repository policy (ADR-022, #128): a project's
<project>/.moat/policy.yamlmakes the user policy stricter for every tool call in the project. Itsdenygroups join the user's deny rules. Itsaskgroups are tried before the user's allow rules, so an action the user allows asks, but a user deny is never softened. Itsallowgroups are ignored unless trusted (below). Onlyversion,deny,askandalloware accepted. Repository rule ids carry the prefixrepo:. A repository policy that cannot be read or parsed, or that setsdefaults,executablesorsandbox, denies every call in the project withkernel-error; it is never ignored.moat policy checkuses it unless--policyis given. The rules apply in the hook only: host sandboxes andmoat proxyare generated from the user policy. moat trust [<repo>] [--revoke](ADR-022, #128) lets a repository policy'sallowgroups apply. It prints each allow rule it lets in, then records the SHA-256 of the file against the canonical project root in~/.moat/trust.json. That file is pinned by the lock and never kept in the repository. Trusted allow groups come after the user's allow rules, so user deny rules still win. Any change to the file, or the same file in another checkout, applies in its tightening-only form again untilmoat trustruns again. Atrust.jsonthe lock does not pin is ignored. Likemoat allow, it needs a terminal and an intact lock (refusing to change trust …, exit 64).moat statusandmoat doctor, run inside a project, name its repository policy and digest and say whether it is trusted, not trusted or changed since it was trusted; one that does not parse is a problem (exit 64).moat runon Linux applies the Landlock rules (new dependencylandlock, the rust-landlock project's crate, Linux only) to a thread of its own that starts the agent, so the proxy thread stays unrestricted. File access and TCP connections are hard requirements (Landlock ABI 4, Linux 6.7): an older kernel is refused rather than run with the network open. Scoping of abstract Unix sockets and signals is added where the kernel has it (Linux 6.12). Windows still refuses (exit 64).moat run [--write PATH]… -- <agent> [args](Lightweight tier, ADR-018), on macOS: starts the agent under/usr/bin/sandbox-execwith the Seatbelt profile generated for the project in the current directory. The profile also lets the agent read its own executable and, with--write, read and write the given paths (its state directory); every grant is printed as an allowance before the agent starts, and deny rules still win. Network goes only to amoat proxythatmoat runserves on a loopback port (HTTP_PROXY,HTTPS_PROXY,ALL_PROXYset,NO_PROXYremoved); the proxy refuses loopback destinations, so the sandbox's one open port reaches no other local service. Refused over a drifted policy lock, outside a project, and on Windows (exit 64). It tells the user to turn the agent's own sandbox off: Seatbelt does not nest. Ctrl-C belongs to the agent;moat runkeeps serving. New exit code 1: the agent exited non-zero.- Lightweight tier on Linux, first part: the Landlock rules generated from the IR for the project in the current directory (
crates/openmoat-cli/src/sandbox/landlock.rs), printed bymoat sandbox show(lightweight.landlock). Reads and executes go only below the project,sandbox.read_roots, the agent's executable and a few devices; writes go below the project, the temp directory and--writepaths; TCP connections go only to the proxy's port. Landlock only grants, so the report lists where it is wider than the IR: a deny rule or!...