How Newt runs shell commands with brush
Models write shell.
OCAP / The brush shell
Models write shell. A shell string like cat a | grep b && rm c hides several commands behind one call, so Newt does not hand it to the host shell. It parses it with brush, a bash-compatible shell written in Rust, and checks each program the string would start against the grant. Step through how, and why.
The command is illustrative. Names and paths come from newt-core/src/config/shell.rs and the vendored vendor/agent-bridle-tool-shell. Keys: left/right arrows step, space plays or pauses.
What each stage maps to
| Stage | What it does | Where | Status |
|---|---|---|---|
| Engine choice | safe-subset, host or brush; the confined brush worker needs an active fence on Linux or macOS. | newt-core/src/config/shell.rs |
Implemented |
| Parse and run | Brush in an authenticated worker process; exec is removed from its builtins. | vendor/agent-bridle-tool-shell/src/brush_shell.rs |
Implemented |
| Empty environment | do_not_inherit_env, explicit env seam, seeded PATH. | newt-core/src/agentic/tools/shell.rs |
Implemented |
| Spawn gate | Every external spawn is checked on resolved program and cwd. | vendor/agent-bridle-tool-shell/src/caveat_interceptor.rs |
Implemented |
| Open gate | Files brush opens itself are checked against fs_read and fs_write. | vendor/agent-bridle-tool-shell/src/caveat_interceptor.rs |
Implemented |
| Carried coreutils | Six uutils commands, re-executed through Newt and gated. | vendor/agent-bridle-tool-shell/src/coreutils_dispatch.rs |
Implemented |
| Kernel fence | Inherited by the worker and descendants. | docs/design/captured-shell-cross-platform.md |
Implemented, open deviations |
| Windows ambient runner | Separate newt-ambient-brush.exe gives POSIX syntax when OCAP is off; cmd.exe fallback; kill-on-close job object. | docs/windows-ambient-shell.md |
Implemented |
| Network check in the interpreter | Network is the kernel backend’s job, not brush’s. | newt-core/src/config/shell.rs |
Not done by brush |
What this does not show
- The interpreter gate is mediation and audit with exec precision. It is not an independent security boundary: the kernel fence is the boundary for files and network.
- Only spawns and opens that brush itself makes are gated. An admitted child and its descendants rely on the inherited kernel fence.
- On Windows,
--full-accesscurrently selects the restricted safe-subset engine, not brush, because the authenticated worker transport the brush engine needs exists only on Linux and macOS. A source branch meant to pick brush there is unreachable, and the changelog still describes it. - On Windows, brush runs as a separate ambient runner when OCAP is off. It is not the confined, per-spawn-gated worker, which exists only on Linux and macOS. Confined on Windows, the shell engine is safe-subset, and choosing the confined brush engine there is refused.
- The confined brush worker is the default only when a kernel fence is active. Where no native fence exists, the shell can collapse to advisory, a recorded open deviation.
- Carried coreutils are not in-process. Each is a re-exec, which is also a cost.
- Newt builds on renamed
brush-ocap-*forks published on crates.io, not on upstream brush itself. The upstream interceptor proposal (reubeno/brush#1184) is still open, and the bundled-command dispatch code is marked for upstreaming. - No speed claim: the repo documents N admissions for N spawns but has no measurement of what that costs in time.
Source: newt-agent site/brush.html at 67b24697. newt-agent is Apache-2.0: Gilamonster Foundation / newt-agent.
Revisions
- · claude-sonnet-5-5 · Moved from newt-agent site/brush.html at 67b24697; prose converted to Markdown, animation text moved to copy.