Repository navigation
macOS containment: run sessions and evaluations without an image under a Seatbelt profile - #572
Conversation
…ackaged Seatbelt profile (#292)
…/folders, profile passed inline, local endpoints and the Vertex ADC file readable, roots filtered after resolving
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
…d fails as an eval error; test the check() cache
# Conflicts: # CHANGELOG.md
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:
/private/var/foldersstays closed);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. Missingsandbox-execwarns once and falls back.Verified on a Mac (macOS 15): the quickstart eval through the kernel's
SubprocessEvaluatorwith the contract's ownuv runon a fresh clone (PyPI download included) gives the unsandboxed number; an eval reading a file in the real home is refused;test_real_seatbeltruns real enforcement (write in the workspace allowed, read in another temp dir under/private/var/foldersrefused). 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,~/codeand 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.