Skip to content

Releases: crocodile-labs/openmoat

0.1.1 - 2026-10-09

Choose a tag to compare

@github-actions github-actions released this 09 Oct 04:00
89975af

Alpha: the hook's decisions are not enforced by the operating system (ADR-013). Only commands inside the host sandboxes moat init configures, or under moat 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 bench reproduces how a hook decides (#364). With no arguments it sends the bundled MoatBench scenarios (attacks, benign work and developer workflows) to this moat as Claude Code, Codex and Cursor hook payloads in a throwaway home and prints the scorecard. moat bench --hook <command> --host claude-code|codex|cursor sends 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 only PATH, HOME and USERPROFILE. --verbose prints that environment and every payload; --format json is for scripts; it exits 0 whatever the scores. The scenarios moved from tests/moatbench/ to crates/openmoat-cli/moatbench/, and the MoatBench test now runs moat 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, --write paths 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 (.env files, ~/.cargo/credentials.toml) by an empty placeholder no one may open, denied writes (.git, .claude/settings.json) by a read-only mount. .env, .envrc and .moat directly 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. Where bwrap is 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 the ubuntu-latest CI jobs: a hostile npm test gets EACCES reading the project's .env, which plain moat run on Linux still lets it read.

Security

  • moat run now refuses terminal input injection. The agent shares the terminal it was started in, and ioctl(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, refuses ioctl with TIOCSTI or TIOCLINUX (EPERM, x32 included), whatever dev.tty.legacy_tiocsti says. On macOS the Seatbelt profile denies TIOCSTI on every file. docs/EVIDENCE.md gains a row run on a terminal the test creates: EPERM under every moat run column 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_tiocsti at 0), so on a kernel that allows TIOCSTI a 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 | sh

Install 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/moat

Download 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/openmoat

You 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

Choose a tag to compare

@github-actions github-actions released this 08 Oct 20:16
b219932

Alpha: the hook's decisions are not enforced by the operating system (ADR-013). Only commands inside the host sandboxes moat init configures, or under moat 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-rule sandbox-exec profile shows the same without OpenMoat (#280, #351). CI's macOS 15 runners did not show it.

Fixed

  • moat --help no longer says the alpha has no OS enforcement; it says where the OS bounds commands and points to moat status.

Security

  • On Linux, Codex's sandbox now keeps project scripts from creating .env and .envrc in 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 init and moat sandbox sync now also write .env and .envrc as deny entries under :workspace_roots on Linux; bubblewrap mounts an empty read-only file over a missing one while the command runs, and the script gets EROFS. .env.local, a .envrc in a subdirectory and a project's bin/moat stay creatable for sandboxed commands on Linux; moat sandbox show lists 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 | sh

Install 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/moat

Download 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/openmoat

You 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

Pre-release

Choose a tag to compare

@github-actions github-actions released this 08 Oct 18:44
ad77e25

Alpha: the hook's decisions are not enforced by the operating system (ADR-013). Only commands inside the host sandboxes moat init configures, or under moat 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 tier job (macOS and Linux) runs the same hostile project scripts as npm test under Claude Code's sandbox and Codex's moat profile, each configured with the settings moat init generates, 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 runs codex sandbox -P moat; no account, API key or internet is used, and scripts/ci/host-binaries.sh fetches both binaries pinned by checksum. On Linux it found two gaps, now listed there: Claude Code's sandbox skips the generated .env denies, 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 init and moat sandbox sync configure Cursor's own sandbox from the policy (#324), as they do for Claude Code and Codex: sandbox.json next to Cursor's hooks.json (~/.cursor, or $CURSOR_CONFIG_DIR) gets type: "workspace_readwrite", readBoundary: "workspace", the read roots as additionalReadPaths, write allow rules outside the project as additionalReadwritePaths, and the policy's hosts as networkPolicy with default: "deny". Other keys are kept, the file is backed up first and pinned whole by the lock, moat doctor names a weakened setting, and moat uninstall removes exactly these keys. moat status reports Cursor as hook + OS sandbox when 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 show lists 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 init generates (#358). Before it starts a command, Codex on Linux lists every file below each deny glob with rg --files so bubblewrap can hide the matches, and it refuses to start the command when ripgrep reports any error; the profile repeated the **/.env, **/.env.* and **/.envrc denies below every read root, and root-only directories such as /etc/ssl/private and /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 show lists 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 init and moat sandbox sync now let commands read the executable codex on PATH starts (through npm's codex.js launcher, the platform binary), listed by moat sandbox show as codex.own-binary; run moat sandbox sync again after moving Codex. When codex is not on PATH, or its executable is inside a denied path such as the Codex home (Codex's standalone installer), nothing is granted and moat sandbox show says so. macOS profiles are unchanged.

Security

  • On Linux, Claude Code's sandbox now keeps project scripts away from the project's .env files (#359). Bubblewrap needs concrete paths, so the sandbox expands each denyRead glob from its first literal directory when a command starts, and it skipped the generated /**/.env, /**/.env.* and /**/.envrc: npm test could print the .env. moat init and moat sandbox sync now also write Read(./**/.env), Read(./**/.env.*) and Read(./**/.envrc) to permissions.deny on Linux. Claude Code expands those under the session's working directory, and a script reading a match gets EACCES (CI's standard tier (ubuntu-latest) job, Claude Code 2.1.290). A file created after a command starts, or a .env in another readable directory, stays readable, and Claude Code's file tools now refuse .env.example there too; moat sandbox show lists both. macOS settings are unchanged, and moat uninstall removes the rules.
  • The default policy denies agent writes to ~/.cursor/sandbox.json and 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, so cat .en?, cat .e*, cat .[e]nv and head -c 99 .en[v] were allowed and cat ~/.ss?/id_rsa only asked while cat .env was 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, with bash matching 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} runs cat .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 as bash does 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 --always of 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 | sh

Install 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/moat

Download 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...
Read more

0.1.0-alpha.6 - 2026-10-08

Pre-release

Choose a tag to compare

@github-actions github-actions released this 08 Oct 05:34
6b2acdc

Alpha: the hook's decisions are not enforced by the operating system (ADR-013). Only commands inside the host sandboxes moat init configures, or under moat 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.yaml runs 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 expects allow, or ask where 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 main and a bare make ask. Steps can now be file edits (edit:, sent as Claude Code Edit, a Codex apply_patch Update File, and Cursor Write) and can carry their own gap marker.
  • 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 test under moat 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_rsa or ~/.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 with EPERM or EACCES, and an unlisted host gets the proxy's 403. On Linux a project .env stays readable, shown as a known gap. The Codex and Claude Code sandboxes are not run in CI and are listed as not verified. Regenerate with MOAT_UPDATE_EVIDENCE=1 cargo test -p openmoat --test e2e differential.
  • moat status and moat doctor print 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), or not 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 json prints the same per agent. The moat home screen names each protected agent's level (Protecting Claude Code (hook + OS sandbox)). The level never changes whether status or doctor reports a problem.

Changed

  • THREAT_MODEL no longer lists Claude Code SendFile as 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 DIR and python3 -m venv DIR: each directory is now an fs.write, a plain name included, so a venv outside the project, in .git or over ~/.moat asks or is denied; --clear and --upgrade/--upgrade-deps ask. source .venv/bin/activate and . .venv/bin/activate (also venv/), spelled exactly and with no arguments; any other file, a path with .., or the same spelling after a cd out of the project asks. git rebase <ref>, like git pull --rebase; --exec/-x, --interactive/-i, --edit-todo, --continue, --skip and --strategy/-s ask, in any prefix git accepts and inside short-option clusters. A bare make, like make test; make -f, make -C and the existing recipe-shell overrides still ask. Stricter: npm exec and npm x now ask (installs) like npx, 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, or a in moat) 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 policy instead of the 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, and moat run covers 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 (rule unparseable) merged with them, strictest wins, so a deny stands: an MCP call that reads ~/.ssh/id_rsa and adds a url with no host, cd "$X" && cat notes ~/.ssh/id_rsa, bash --frobnicate -c x; cat ~/.ssh/id_rsa and echo $(bash --frobnicate -c x) $(cat ~/.ssh/id_rsa) were ask and are now denied by secrets-paths, and bash -c "eval eval eval eval eval eval true" by pipe-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 is deny, 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 guard denies (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 what guard answers 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 that moat status and moat doctor report a hook whose binary is gone as not protected. The known gaps lines 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 neither audit.db nor moat audit export (#338). Previously only known token formats were redacted, and a value sent as part of a host name was recorded by moat proxy. Matching ignores letter case and skips values shorter than 8 bytes, which occur in ordinary text. moat guard masks the values with a file or env source 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 (rule unparseable, reason mcp 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 a deny among 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 | sh

Install 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/moat

Download 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/...
Read more

0.1.0-alpha.5 - 2026-10-07

Pre-release

Choose a tag to compare

@github-actions github-actions released this 08 Oct 03:20
5e9571d

Alpha: the hook's decisions are not enforced by the operating system (ADR-013). Only commands inside the host sandboxes moat init configures, or under moat 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) under moat run on macOS: its installation directory in sandbox.read_roots, an allow rule for api2.cursor.sh, --write ~/.cursor, CURSOR_API_KEY instead of the keychain, and --sandbox disabled for that run (#324).
  • moat doctor notes, when the Cursor hook is installed, that OpenMoat gives Cursor no OS sandbox, so only the hook applies the policy, and that moat run covers 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 init does 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 init and moat sandbox sync no longer write Claude Code's sandbox block or permissions.blockReadsOutsideWorkingDirectories (#327). Claude Code's sandbox runs on macOS, Linux and WSL2 only, and with failIfUnavailable: true Claude Code exits at startup where it cannot run. init, sandbox sync, sandbox show, doctor and status now say sandbox 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. Run moat init or moat sandbox sync again 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 | sh

Install 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/moat

Download 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/openmoat

You 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

Pre-release

Choose a tag to compare

@github-actions github-actions released this 07 Oct 21:32
c9c5e2b

Alpha: the hook's decisions are not enforced by the operating system (ADR-013). Only commands inside the host sandboxes moat init configures, or under moat 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 real moat guard verdicts). 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, since brew upgrade alone may not see a new release of the tap (#314).
  • After a session grant (o in moat, or moat allow --last without --always), moat says that the grant covers only this agent session and that a new session asks again; for Claude Code, claude --continue resumes the session (#312).

Fixed

  • moat init no longer leaves out an agent silently when CLAUDE_CONFIG_DIR, CODEX_HOME or CURSOR_CONFIG_DIR names 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 run moat init again. 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 | sh

Install 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/moat

Download 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/openmoat

You 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

Pre-release

Choose a tag to compare

@github-actions github-actions released this 07 Oct 19:44
58f4c4c

Alpha: the hook's decisions are not enforced by the operating system (ADR-013). Only commands inside the host sandboxes moat init configures, or under moat 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 lint rejects ! exclusions and globs that do not compile. Policies without the key behave as before.
  • moat allow --last and moat allow --last --always approve 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 Codex apply_patch or a Cursor file tool. A session grant covers exactly those files for that action; --always adds an fs.read or fs.write rule 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.json moves 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 moat or moat allow --last (Cursor's said to add a policy rule and run moat doctor --accept). The Continue CLI message names moat too.

Fixed

  • moat says 1 decision instead of 1 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 | sh

Install 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/moat

Download 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/openmoat

You 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

Pre-release

Choose a tag to compare

@github-actions github-actions released this 07 Oct 17:57
53e13d1

Alpha: the hook's decisions are not enforced by the operating system (ADR-013). Only commands inside the host sandboxes moat init configures, or under moat 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> and moat allow --remove <id> (#300). --site adds a net allow for one host name; it also covers web fetches. --dir adds an fs.read and fs.write allow 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 the moat allow --remove command that undoes it. Deny rules still win. Wildcards, URLs, the home directory, its ancestors and filesystem roots are refused. --remove takes out any approved-N rule, --always ones included, and --always now prints its undo command too. New attack fixtures show that an agent running moat allow --site or --dir is denied (kernel-self).
  • moat edit (#300) opens ~/.moat/policy.yaml in $VISUAL, $EDITOR, vi or (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 after Apply? [y/N] is answered y. The previous policy is kept in ~/.moat/policy.yaml.bak. It needs a terminal and an intact lock, like moat 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 since moat init gets its backup back byte for byte, a file init created is deleted, and a file changed since keeps those changes. The lock stops pinning the undone files; ~/.moat stays unless --purge. It runs only from a terminal.
  • moat init records the config directory of each agent it sets up in ~/.moat/hosts.json, which the lock pins (#298). status, doctor, allow, sandbox sync and uninstall use the recorded directories, so a custom CLAUDE_CONFIG_DIR, CODEX_HOME or CURSOR_CONFIG_DIR no longer has to be exported for every command. moat doctor reports a variable that names another directory than the record instead of following it; moat init with the variable set moves the record. kernel-self protects both the recorded directory and the one the variable names. Nothing changes for agents in their default directories.
  • moat with 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 and Accept these changes? [y/N] runs moat doctor --accept on y. The newest shell ask from today that no grant or permanent rule covers is shown with its rule and asks Allow? [o]nce for this session / [a]lways / [n]o, which runs moat allow for exactly that command. Otherwise it prints Nothing needs you. Without a terminal it prints the command to run and changes nothing.

Changed

  • moat init asks before changing an agent (#282). At a terminal it lists the agents it found with the files it would change and asks Protect Claude Code (…)? [Y/n] for each, then names the backups and Undo anytime: moat uninstall. --hosts and the new --yes skip the questions. Without a terminal and without either flag, init sets up ~/.moat but changes no agent's files, and says how to go on: scripts that ran moat init need --yes.

Fixed

  • moat status and moat doctor show an agent that moat init left out (the person said no, or named other --hosts) as · not set up instead of a missing hook and five sandbox problems. Lock drift on a Claude Code settings file no longer names theme, which is not pinned (#287).

Security

  • kernel-self denies any moat command run under a pseudo-terminal wrapper (script, unbuffer, tmux, screen, quoted for expect, Python pty.spawn, osascript), so an agent cannot answer the questions of a bare moat (#299). An agent running moat or moat status without a wrapper still gets ask, and without a terminal moat changes nothing.
  • kernel-self denies moat edit to agents, by name, by absolute path and under every pseudo-terminal wrapper already listed for moat allow (#300).
  • kernel-self denies agent runs of moat uninstall, including through pseudo-terminal wrappers, like moat 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 | sh

Install 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/moat

Download 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/openmoat

You 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

Pre-release

Choose a tag to compare

@github-actions github-actions released this 07 Oct 16:09
df15369

Alpha: the hook's decisions are not enforced by the operating system (ADR-013). Only commands inside the host sandboxes moat init configures, or under moat 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, in moat doctor --accept before it re-pins, and in guard's kernel-integrity reasons: 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 doctor and moat run print one line with the number of places a sandbox is stricter or wider than the policy instead of the whole list; --verbose prints the list (#289). Problems (a weakened setting, drift) still print in full, and moat sandbox show still prints everything.
  • moat show labels its time column time (UTC) and moat replay adds UTC to each session's start time; both were already UTC but unlabelled (#290).

Fixed

  • The policy lock leaves the top-level theme key out of Claude Code's settings.json digest, so Claude Code writing "theme" on first run no longer denies every call with kernel-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-self covers the state and host directories where MOAT_HOME, CLAUDE_CONFIG_DIR, CODEX_HOME or CURSOR_CONFIG_DIR moved them (#286). A write to $MOAT_HOME/policy.yaml or $CLAUDE_CONFIG_DIR/settings.json was asked (default) instead of denied; the lock caught the change only afterwards. Patterns naming ~/.moat, ~/.claude, ~/.codex or ~/.cursor now also match the moved directory, in the hook, moat policy check, moat run and 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 | sh

Install 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/moat

Download 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/openmoat

You 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

Pre-release

Choose a tag to compare

@github-actions github-actions released this 07 Oct 03:34
2e031d9

Alpha: the hook's decisions are not enforced by the operating system (ADR-013). Only commands inside the host sandboxes moat init configures, or under moat 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 init configures Claude Code's and Codex's own sandboxes from the policy through the enforcement IR (ADR-019). Claude Code's user settings get the sandbox block (enabled, failIfUnavailable: true, allowUnsandboxedCommands: false, excludedCommands: [], absolute deny and allow lists, network.allowedDomains with strictAllowlist) and permissions.blockReadsOutsideWorkingDirectories: true; Codex's config.toml gets a [permissions.moat] profile, default_permissions = "moat" and features.network_proxy = true. Other keys and comments are kept; each file is backed up to <file>.moat-sandbox-backup first. moat sandbox show prints what the policy compiles to with every loss (stricter) and allowance (wider); moat sandbox sync rewrites and re-pins (a person at a terminal, refused over a drifted lock). moat doctor and moat status check 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.yaml runs 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.sh runs the full suite.
  • Differential suite: the Codex host-sandbox layer (codex sandbox -P moat) runs every scenario, with npm/cargo shims so the hook sees an allowed npm test/cargo build while 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, .git hooks, settings-escape, symlink read-escape) and the documented narrower results (a .env.example read and .git writes, so git status/git commit, are denied). It surfaced a gap (#229): codex sandbox leaves 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 --bare against a fake Anthropic API (tests/differential/fake_api.py) with the settings moat sandbox show generates (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::McpTool carries the paths and hosts an adapter derived from the call's arguments (reads, writes, hosts); the engine turns them into fs.read, fs.write and net atoms, so an allowed MCP tool name can no longer read ~/.aws/credentials or fetch an unlisted host. Conformance fixtures accept mcp_tool: { name, reads, writes, hosts }.
  • Claude Code and Cursor adapters derive those paths and hosts from MCP tool_input by argument name (path, paths, file_path, source, destination, url, …); write-shaped tool names (write_*, edit_*, move_*, delete_*) and destination/target arguments produce fs.write.
  • Policy secrets: (ADR-020, docs/POLICY.md §2.1): brokered secrets, each with one exact host, a header and a source (file, env or keychain). 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 lint warns when no allow rule names the host, or when no deny rule keeps a file or env source from the agent. The optional plain_http: true allows 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 is moat-secret:<id>:placeholder.
  • openmoat-proxy leak 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 as proxy-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 and Debug name only the id and host. The check matches exact bytes; docs/THREAT_MODEL.md §5 lists what it does not stop.
  • openmoat-proxy injection for brokered secrets over plain HTTP (ADR-020), only for secrets that set plain_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 proxy brokers the policy's secrets:. It reads each file, env or keychain source 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 in tests/moatbench/<category>.yaml sent through the real moat guard as 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.yaml makes the user policy stricter for every tool call in the project. Its deny groups join the user's deny rules. Its ask groups are tried before the user's allow rules, so an action the user allows asks, but a user deny is never softened. Its allow groups are ignored unless trusted (below). Only version, deny, ask and allow are accepted. Repository rule ids carry the prefix repo:. A repository policy that cannot be read or parsed, or that sets defaults, executables or sandbox, denies every call in the project with kernel-error; it is never ignored. moat policy check uses it unless --policy is given. The rules apply in the hook only: host sandboxes and moat proxy are generated from the user policy.
  • moat trust [<repo>] [--revoke] (ADR-022, #128) lets a repository policy's allow groups 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 until moat trust runs again. A trust.json the lock does not pin is ignored. Like moat allow, it needs a terminal and an intact lock (refusing to change trust …, exit 64). moat status and moat 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 run on Linux applies the Landlock rules (new dependency landlock, 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-exec with 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 a moat proxy that moat run serves on a loopback port (HTTP_PROXY, HTTPS_PROXY, ALL_PROXY set, NO_PROXY removed); 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 run keeps 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 by moat 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 --write paths; 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 ! ...
Read more