Repository navigation
Sandbox Trial sessions in bwrap with per-Harness logins - #1199
Merged
Merged
Conversation
A Trial session runs in bwrap with a process namespace of its own: the prepared repository read-write, the Harness binary, the Factory skills folder, a fresh home and the toolchains read-only. Codex, Muse and agy logins bind read-write in place, Claude takes its token from the Credentials, Grok Trials serialize on a Trials home wiped to its login, and OpenCode takes its provider key from a new opencode.api_key. Trials that cannot start sandboxing, or have no Grok login or OpenCode key, are void before their sessions start. Closes #1012
Review findings, with the reviewers' tests kept: toolchain bin directories join the session PATH (a bound top-level bin, plus the resolved homes of cargo, rustc, node, npm, python3 and php), and an overridden XDG_CONFIG_HOME resolves to the directory holding muse/ rather than one level too deep. The login override takes Option, and the Trial fakes share one directory const. Closes #1012
Without bwrap the Trial runs unsandboxed and the fresh home is never made, so the reader watched trial-output/home/.codex while the agent wrote the scenario home: the usage report was missed and the Trial succeeded instead of voiding. Unsandboxed, the agent and its reader share the scenario home, so no override is needed.
This was referenced Oct 24, 2026
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
A Trial's session now runs in bwrap with a process namespace of its own: the prepared repository read-write, the Harness binary, the Factory skills folder, a fresh home, and the toolchains read-only. Harness flags stay as ADR 0013 has them; the network stays the host's (#1013).
Logins: Codex
auth.jsonbinds read-write in place, so a refresh reaches production. Claude takesCLAUDE_CODE_OAUTH_TOKENfrom the Credentials'claude.setup_token. Museauth.json+ lock and agyantigravity-oauth-tokenbind read-write in place. Grok Trials share~/.thirdshift/trials/grok(wiped to login + lock, rename-safe dir bind); without a login the Trial fails before its session. OpenCode takes its provider key only from a newopencode.api_keyin the Credentials, with the Trial config setting that provider'sapiKeyto{env:…}; without it the Trial fails before its session. A sandbox that cannot start voidssandbox_unavailable; a missing Grok login or OpenCode key voidssession_unavailable.THIRDSHIFT_TRIAL_NO_SANDBOXruns sessions without the sandbox (Experiment suite, emergency escape).Seams under test: the Trial session launch (
Sessions::within_sandboxed), the sandbox builder (sandbox::prepare: binds, home, logins), and the Credentialsopencode.api_keyparse/save.Evidence
a_bound_toolchain_is_reachable_on_the_session_pathfailed withsh: 1: fake-tool: not found, anda_muse_login_under_an_overridden_config_home_is_found_in_placefailed withcat: .../fresh/home/.config/muse/muse/auth.json: No such file or directory.After: both pass;
cargo test --bin thirdshift sandboxgives 17 passed,cargo test --test trialgives 17 passed,cargo test --test experimentsgives 52 passed, plussession(87),setup(34),evals/eval_arms,email_test/setup_lifetime/setup_offergreen,cargo fmt --checkandcargo clippy --testsclean, andnpm run docs:checkconfirms the regenerated site docs. The fullscripts/affected-testssweep was stopped in favour of these targeted suites; CI reruns everything.Merge Danger
Door: two-way
Reverting restores unsandboxed Trial sessions; no data format changes.
Blast Radius: Trial sessions
Runs, Graders, and the Experiment machinery are untouched (the suite runs Trials with
THIRDSHIFT_TRIAL_NO_SANDBOX). New void causes reuse the existing TrialOutcome::Voidcontract.Unaddressed findings
Security
No unaddressed introduced findings. Guidance-mode review of
git diff 226a5a58aea394932ea6c29dda585c860535a92a...HEAD(sandbox launcher, per-Harness logins,opencode.api_keyCredentials, Trial wiring) against the sandbox and data-isolation attack classes; the repo has no SECURITY.md or threat model. All 17sandbox::tests pass on HEAD (unbound-path and host-process isolation, orphan reaping, login refresh and rename propagation, Grok mutual exclusion, key scoping), plus 17trialintegration tests. Residual exposures are documented design, not boundary violations: production login files bind read-write in place per the issue spec, and the network stays the host's per #1013.Standards
src/resend_key.rs:139):Credentials::savetakes three parallel secret params instead of one bundle. Declined: three params remain a small interface, and the reviewer demanded no action.Spec
src/evals/experiment/model_grader.rs:320,Command::new(&grader.harness.path)unchanged). Declined for this pull request: open #1014 ("Trials and Graders run in the sandbox") explicitly owns the Grader half, and the launcher insrc/sandbox.rsis built shared so Trials and Graders run in the sandbox #1014 can reuse it. No new issue: an open issue already covers the finding..thirdshift-issue.md:12-17). Declined as a plan decision: the launcher wraps the Harness spawn (src/sandbox.rs:230,prepare;src/trial.rs:226,for_trial), so the Arm's build is the process doing the wrapping, already outside the sandbox; binding it would be theater. The binds actually taken are documented indocs/trial.md("Sandbox").Security review outcome
No unaddressed introduced findings.
Review coverage
Both review axes read all 12 changed files; no changed file left unread.
site/docs/index.htmlis generated output excluded by check: regenerated withnpm run docsfromdocs/guide.md, hunks mirror 1:1,npm run docs:checkpasses. Review reports:.thirdshift-review-fWAPxE/standards.md,.thirdshift-review-fWAPxE/spec.md.Closes #1012
Built with muse · default model · default effort