Skip to content

Sandbox Trial sessions in bwrap with per-Harness logins - #1199

Merged
JacobStephens2 merged 4 commits into
issue-977from
issue-1012
Oct 24, 2026
Merged

JacobStephens2 merged 4 commits into
issue-977from
issue-1012

Conversation

@JacobStephens2

@JacobStephens2 JacobStephens2 commented Oct 23, 2026 •

Copy link
Copy Markdown
Owner

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).

Trial process (outside)
  sandbox::for_trial
    fresh home under output_dir/home
    Grok Trials-home lock + wipe (one Grok Trial at a time)
  Sessions::within_sandboxed
    bwrap --unshare-pid --die-with-parent --new-session [...]
      --bind repo repo (rw)
      --ro-bind harness /sandbox/harness/<name>
      --bind fresh-home fresh-home (rw) + login binds
      -- <harness> <unchanged session args>

Logins: Codex auth.json binds read-write in place, so a refresh reaches production. Claude takes CLAUDE_CODE_OAUTH_TOKEN from the Credentials' claude.setup_token. Muse auth.json + lock and agy antigravity-oauth-token bind 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 new opencode.api_key in the Credentials, with the Trial config setting that provider's apiKey to {env:…}; without it the Trial fails before its session. A sandbox that cannot start voids sandbox_unavailable; a missing Grok login or OpenCode key voids session_unavailable. THIRDSHIFT_TRIAL_NO_SANDBOX runs 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 Credentials opencode.api_key parse/save.

Evidence

  • Before: the Spec reviewer's a_bound_toolchain_is_reachable_on_the_session_path failed with sh: 1: fake-tool: not found, and a_muse_login_under_an_overridden_config_home_is_found_in_place failed with cat: .../fresh/home/.config/muse/muse/auth.json: No such file or directory.
    After: both pass; cargo test --bin thirdshift sandbox gives 17 passed, cargo test --test trial gives 17 passed, cargo test --test experiments gives 52 passed, plus session (87), setup (34), evals/eval_arms, email_test/setup_lifetime/setup_offer green, cargo fmt --check and cargo clippy --tests clean, and npm run docs:check confirms the regenerated site docs. The full scripts/affected-tests sweep 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 Trial Outcome::Void contract.

Unaddressed findings

Security

No unaddressed introduced findings. Guidance-mode review of git diff 226a5a58aea394932ea6c29dda585c860535a92a...HEAD (sandbox launcher, per-Harness logins, opencode.api_key Credentials, Trial wiring) against the sandbox and data-isolation attack classes; the repo has no SECURITY.md or threat model. All 17 sandbox:: tests pass on HEAD (unbound-path and host-process isolation, orphan reaping, login refresh and rename propagation, Grok mutual exclusion, key scoping), plus 17 trial integration 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

  1. Data Clumps (src/resend_key.rs:139): Credentials::save takes three parallel secret params instead of one bundle. Declined: three params remain a small interface, and the reviewer demanded no action.

Spec

  1. Grader sessions are not sandboxed (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 in src/sandbox.rs is built shared so Trials and Graders run in the sandbox #1014 can reuse it. No new issue: an open issue already covers the finding.
  2. "The Arm's build" is never bound (spec bind list, .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 in docs/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.html is generated output excluded by check: regenerated with npm run docs from docs/guide.md, hunks mirror 1:1, npm run docs:check passes. Review reports: .thirdshift-review-fWAPxE/standards.md, .thirdshift-review-fWAPxE/spec.md.

Closes #1012

Built with muse · default model · default effort

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.
@JacobStephens2
JacobStephens2 merged commit c63c2a4 into issue-977 Oct 24, 2026
18 checks passed
@JacobStephens2
JacobStephens2 deleted the issue-1012 branch October 24, 2026 00:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant