Summary
Add OS-native kernel sandboxing (Landlock on Linux, Seatbelt on macOS) as an enforcement layer for Moat runs. Support it in two modes:
- In-container defense-in-depth — apply kernel self-restriction to the agent process inside a Moat container, so a container-boundary breakout still hits an irreversible, deny-by-default capability wall.
- Lightweight local mode — a containerless run mode that uses kernel primitives alone (no Docker/Apple container), for fast, low-friction local runs where full container isolation is more than needed.
Motivation
Moat's current isolation model is the container boundary plus the credential-injecting proxy. That's strong, but:
- Single layer of process isolation. If an agent escapes the container (kernel/container-runtime CVE, misconfig), there's no inner wall. Kernel self-restriction applied to the agent process gives defense-in-depth: even with arbitrary code execution, the agent cannot widen its own restrictions.
- Startup cost / footprint. Spinning up a container (image selection, mounts, daemon registration) is overhead for quick local runs. A kernel-only mode could start in well under a second with no disk usage, lowering the barrier to "just sandbox this one command."
- Defense-in-depth aligns with Moat's deny-by-default posture. A curated default blocklist of sensitive paths (SSH keys,
~/.aws, ~/.config/gcloud, shell history, browser cookies) enforced at the kernel level complements the proxy's credential isolation.
How it would work
Linux — Landlock LSM (kernel 5.13+):
- Agent process calls
landlock_restrict_self() after setup, before exec'ing the agent.
- Restrictions are irreversible (tighten-only) and inherited by all child processes — no userspace escape hatch.
- Allow-list filesystem access (typically workspace CWD writable, everything else denied) and, on kernel 6.7+ (ABI v4), TCP network rules.
macOS — Seatbelt:
- Apply a sandbox profile to the agent process for filesystem/network restriction.
Mode 1 (in-container): the container entrypoint applies kernel restrictions to the agent process before handing off. Container boundary = outer wall; Landlock/Seatbelt = inner wall.
Mode 2 (local): moat run skips container creation entirely; the agent runs as a restricted host process. The credential proxy still sits outside the sandbox (localhost), so the existing per-run token injection model carries over unchanged.
Configuration sketch
# moat.yaml
isolation:
mode: container # container | local
kernel_sandbox: true # apply Landlock/Seatbelt within the chosen mode
sandbox:
allow_write: [./] # paths writable by the agent
deny_paths: # blocked even if otherwise readable
- ~/.ssh
- ~/.aws
- ~/.config/gcloud
CLI equivalents (e.g. --mode local, --kernel-sandbox, --allow ./src) for ad-hoc runs.
Known constraints / threat-model notes
These should be documented honestly so users understand the guarantees:
- Shared kernel in local mode — weaker memory isolation than the container boundary. Local mode trades isolation strength for speed; it is not a replacement for the container in high-risk scenarios.
- Landlock coverage gaps — historically no UDP filtering (TCP-only until ABI v4 / kernel 6.7); some syscall families (chmod/chown/stat) not restrictable on older kernels. Feature-detect ABI version and degrade gracefully with a clear warning.
- Network filtering still relies on the proxy — kernel network rules are coarse (per-port/IP); domain-level policy stays with the proxy, which has its own documented bypass surface (domain fronting, DNS tunneling).
- Windows — Landlock is only available via WSL2; native Windows is out of scope for an initial cut.
- Kernel-version dependency — Landlock requires Linux 5.13+; older hosts fall back to container-only with a warning.
Scope for a first cut
Open questions
- Should local mode be a separate
mode: local or a kernel_sandbox-only flag layered onto a "no container" runtime?
- How does kernel-level network restriction interact with the existing proxy
HTTP_PROXY/NO_PROXY routing — do we need to allow-list the loopback proxy explicitly in the Landlock TCP ruleset?
- Audit logging: should kernel-denied operations be surfaced into the existing audit store?
Summary
Add OS-native kernel sandboxing (Landlock on Linux, Seatbelt on macOS) as an enforcement layer for Moat runs. Support it in two modes:
Motivation
Moat's current isolation model is the container boundary plus the credential-injecting proxy. That's strong, but:
~/.aws,~/.config/gcloud, shell history, browser cookies) enforced at the kernel level complements the proxy's credential isolation.How it would work
Linux — Landlock LSM (kernel 5.13+):
landlock_restrict_self()after setup, before exec'ing the agent.macOS — Seatbelt:
Mode 1 (in-container): the container entrypoint applies kernel restrictions to the agent process before handing off. Container boundary = outer wall; Landlock/Seatbelt = inner wall.
Mode 2 (local):
moat runskips container creation entirely; the agent runs as a restricted host process. The credential proxy still sits outside the sandbox (localhost), so the existing per-run token injection model carries over unchanged.Configuration sketch
CLI equivalents (e.g.
--mode local,--kernel-sandbox,--allow ./src) for ad-hoc runs.Known constraints / threat-model notes
These should be documented honestly so users understand the guarantees:
Scope for a first cut
moat.yamlisolationblock + CLI flagsOpen questions
mode: localor akernel_sandbox-only flag layered onto a "no container" runtime?HTTP_PROXY/NO_PROXYrouting — do we need to allow-list the loopback proxy explicitly in the Landlock TCP ruleset?