Skip to content

macOS containment: run sessions and evaluations without an image under a Seatbelt profile - #572

Merged
renmengye merged 6 commits into
mainfrom
feat/macos-seatbelt
Oct 24, 2026
Merged

renmengye merged 6 commits into
mainfrom
feat/macos-seatbelt

Conversation

@renmengye

@renmengye renmengye commented Oct 24, 2026 •

Copy link
Copy Markdown
Member

Closes #292.

On macOS without an image (the laptop quickstart path), sessions, evaluations and panel lenses now run under a packaged Seatbelt profile, src/outerloop/containment/macos.sb, applied with /usr/bin/sandbox-exec. Before this, an uncontained session could read any file the account can.

What the profile allows:

  • write: the workspace, the run's scratch directory (HOME/TMPDIR), and the per-user cache dir (the CLI's keychain access writes there; the rest of /private/var/folders stays closed);
  • read: system paths, the Homebrew prefix, and the install dirs of the executables involved (resolved through symlinks; never home itself or a parent of it), plus the Vertex ADC file when one is configured;
  • network: TCP 443, DNS, and local ports (a local model endpoint). Seatbelt filters by address and port, not host name.

The profile is passed inline (sandbox-exec -p), so there is no profile file a sandboxed process could edit. Linux and Apptainer paths are unchanged. Missing sandbox-exec warns once and falls back.

Verified on a Mac (macOS 15): the quickstart eval through the kernel's SubprocessEvaluator with the contract's own uv run on a fresh clone (PyPI download included) gives the unsandboxed number; an eval reading a file in the real home is refused; test_real_seatbelt runs real enforcement (write in the workspace allowed, read in another temp dir under /private/var/folders refused). Before the build, a probe ran one Claude session under the draft profile: it ran the eval and wrote in the workspace; reads of ~/.ssh/config, ~/.zshrc, ~/code and writes to ~ were refused (details on #292).

Not verified: a Claude session with an API key through the integrated wrapper (the probe used a subscription login, which needs keychain reads this profile does not grant; the docs say to use an API key), the Metal/GPU path, Codex and Hermes sessions (they need the image in every mode anyway).

Deferred: limiting egress to the configured model host through a local proxy.

Built by codex; review fixes by Claude; reviewed by codex and Claude.

…/folders, profile passed inline, local endpoints and the Vertex ADC file readable, roots filtered after resolving
@renmengye renmengye added the outerloop:review re-request the advisory review label Oct 24, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Round 1 — reviewed head 72ad0d49 — reviewer summarizer:hermes/gpt-5.6-terra over coverage+credentials+deployment+general+lifecycle+prose.

terra
Advisory findings from outerloop — the code owner decides. Reply to disagree; the outerloop:no-review label opts this PR out.

Verdict: 2 blocking, 1 advisory.

2 findings attached to the lines below.

Advisory (non-blocking):

  • Suggestion: The macOS setup hint still says runs are uncontained. [general+prose] Rewrite it to say that Apptainer is unavailable but Seatbelt contains local no-image runs when sandbox-exec is present. (src/outerloop/image.py:83; high confidence)

Two blocking containment issues: unrestricted HTTPS permits provider-key exfiltration, and executable-derived readable roots can expose home subdirectories such as .ssh. Also update the stale macOS setup hint. Rejected: none; the general and prose setup-hint findings were merged because they identify the same contradictory user-facing message.

Comment thread src/outerloop/containment/macos.sb Resolved
Comment thread src/outerloop/containment/__init__.py Resolved

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Round 2 (re-run on the same head) — reviewed head 72ad0d49 — reviewer hermes/gpt-5.6-terra.

terra
Advisory findings from outerloop — the code owner decides. Reply to disagree; the outerloop:no-review label opts this PR out.

Verdict: nothing blocking — 1 advisory note.

Advisory (non-blocking):

  • macOS init still says that no containment exists. On macOS, ensure_image() prints install_hint(), whose text says runs stay uncontained and macOS containment is only planned, contradicting this new support and misleading users about their session safety; rewrite it to say no-image local runs use Seatbelt. (docs/install.md:141; high confidence)

macOS setup still reports that runs are uncontained and that containment is only planned.

@renmengye renmengye added outerloop:review re-request the advisory review and removed outerloop:review re-request the advisory review labels Oct 24, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Round 3 — reviewed head f1cd7893 — reviewer hermes/gpt-5.6-terra.

terra
Advisory findings from outerloop — the code owner decides. Reply to disagree; the outerloop:no-review label opts this PR out.

Verdict: 1 blocking, 0 advisory.

Seatbelt denies the validation cache directory. check() creates cache_dir in the system temp directory, but _run() grants Seatbelt write access only to eval_home, so macOS no-image validation commands that write UV_CACHE_DIR or UV_PROJECT_ENVIRONMENT fail with a permission error. (src/outerloop/orchestrator.py:434; high confidence)

The macOS validation path writes its eval cache outside the Seatbelt scratch directory.

@renmengye renmengye added outerloop:review re-request the advisory review and removed outerloop:review re-request the advisory review labels Oct 24, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Round 4 — reviewed head b9a05225 — reviewer hermes/gpt-5.6-terra.

terra
Advisory findings from outerloop — the code owner decides. Reply to disagree; the outerloop:no-review label opts this PR out.

Verdict: nothing blocking — 1 advisory note.

1 finding attached to the lines below.

Malformed shell commands bypass the evaluator's normal error handling on macOS.

Comment thread src/outerloop/orchestrator.py Resolved
@renmengye
renmengye merged commit 8962790 into main Oct 24, 2026
1 check passed
@renmengye
renmengye deleted the feat/macos-seatbelt branch October 24, 2026 12:12
@renmengye renmengye mentioned this pull request Oct 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

outerloop:review re-request the advisory review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

macOS containment: a kernel-owned Seatbelt profile for sessions and evaluations

1 participant