Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/sandbox.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
name: Sandbox

# The runner sandbox's boundary test (`just runner-sandbox`) runs nightly on a
# The runner sandbox's boundary tests (`just runner-sandbox`) run nightly on a
# GitHub-hosted runner, outside pull requests and the merge queue, so landing
# never waits on it. GitHub reports a failed scheduled run only to the account
# never waits on them. GitHub reports a failed scheduled run only to the account
# that last changed the cron schedule (with Actions notifications on), not to
# the repository's watchers; that is the maintainer, whose account lands the
# workflow, so they see a boundary failure.
Expand Down
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1333,10 +1333,11 @@ commands start from a minimal environment, so its login, the opencode server's
password, and the kix tools' token stay out of their reach. This is an OS
sandbox, not a VM, and it narrows rather than removes the risk of running
agent commands on your machine. On Linux, `just runner-sandbox` in this
repository runs a scripted model through the pinned opencode and
sandbox-runtime in a checkout that tries to read credentials, write outside its
sandbox, plant opencode config, reach the network, and connect to the Nix
daemon's or another Unix socket, and fails if any attempt gets through. CI runs it nightly, outside the merge queue.
repository runs a scripted model through the pinned Claude Code, and through
the pinned opencode and sandbox-runtime, in a checkout that tries to read
credentials, write outside its sandbox, plant its own agent config and hooks,
reach the network, and connect to the Nix daemon's or another Unix socket, and
fails if any attempt gets through. CI runs it nightly, outside the merge queue.

On Linux the sandbox needs `bwrap` and `socat` (the flake's `runner` package
pins both) and unprivileged user namespaces, including the nested one the
Expand Down
7 changes: 4 additions & 3 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,11 @@
# `flock` (the land daemon's repository lock) and `script` (the tests' terminal), which
# self-hosted NixOS runners don't otherwise provide.
++ pkgs.lib.optionals pkgs.stdenv.hostPlatform.isLinux [ pkgs.util-linux ];
# The runner package's opencode and sandbox-runtime, for the sandbox boundary test
# (test/live/runner-sandbox.test.ts), which runs where srt does. Named as the runner
# finds them rather than put on PATH, where they would shadow the user's own opencode.
# The runner package's Claude Code, opencode, and sandbox-runtime, for the sandbox
# boundary tests (test/live/runner-sandbox*.test.ts), which run on Linux. Named as the
# runner finds them rather than put on PATH, where they would shadow the user's own.
env = pkgs.lib.optionalAttrs pkgs.stdenv.hostPlatform.isLinux {
QUEST_RUNNER_CLAUDE = pkgs.lib.getExe claudeCode;
QUEST_RUNNER_OPENCODE = pkgs.lib.getExe opencode;
QUEST_RUNNER_SRT = pkgs.lib.getExe sandboxRuntime;
};
Expand Down
4 changes: 2 additions & 2 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ review-sandbox *args:
QUEST_LIVE=1 bun run vitest run test/live/review-sandbox {{args}}

# Check that a runner job's commands cannot escape its sandbox, with the pinned
# opencode and sandbox-runtime and a scripted model, on Linux. Needs no login;
# CI runs it nightly (.github/workflows/sandbox.yml). Run it after bumping either.
# Claude Code, opencode, and sandbox-runtime and a scripted model, on Linux. Needs
# no login; CI runs it nightly (.github/workflows/sandbox.yml). Run it after bumping any.
runner-sandbox *args:
QUEST_LIVE=1 bun run vitest run test/live/runner-sandbox {{args}}

Expand Down
5 changes: 3 additions & 2 deletions quest/m0/kix/sandbox/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,9 @@ Shipped in [moq-dev/quest#212](https://github.com/moq-dev/quest/pull/212)
`/etc/opencode` loads above the runner's and could replace the sandboxed
`shell`.
`test/live/runner-sandbox.test.ts` checks that boundary with the pinned
opencode and srt and a scripted model. It runs in `just runner-sandbox`
opencode and srt and a scripted model, and
`test/live/runner-sandbox-claude.test.ts` checks Claude Code's the same way,
its scripted model set as the account's endpoint in its settings. It runs in `just runner-sandbox`
and a nightly GitHub-hosted workflow (`.github/workflows/sandbox.yml`),
not in `just test` or the merge queue, whose container can't mount
`/proc` for bwrap, so a bump that weakens the boundary fails the next
Expand All @@ -93,4 +95,3 @@ Shipped in [moq-dev/quest#212](https://github.com/moq-dev/quest/pull/212)
## Required

- [Per-job opencode saved output](/quest/m0/kix/sandbox/opencode-data.md) - each opencode job saves its tool output and shell captures beside its checkout, so no job reads another's, while sessions and the credential stay in the account's database
- [Claude Code boundary test](/quest/m0/kix/sandbox/claude-boundary.md) - the same hostile-repository test for Claude Code jobs, with a fake model set in the account's settings
62 changes: 0 additions & 62 deletions quest/m0/kix/sandbox/claude-boundary.md

This file was deleted.

Loading