Gates and sandboxing
Seatbelt, bwrap and Windows attestation; network denied; fail-closed rules
A gate is a shell command whose exit code is the verdict, with no model involved (lib/gates.mjs). plan.gate.build and plan.gate.test run in every ticket worktree after the build, and again in the integration worktree before a merge.
Gate commands are untrusted. They run a script that lives in the builder's worktree (for example the test script in package.json), and a builder can rewrite that script. Running it unsandboxed on the host would be a sandbox escape, so agb contains two things separately: the builder process and the gate commands (lib/gates.mjs).
Gate sandbox
C22 Worktree and post-merge gates run sandboxed: Seatbelt (sandbox-exec) on macOS and bubblewrap (bwrap) on Linux. If no usable sandbox exists, the gate fails closed and returns ok: false with "gate refused: sandboxing requested but unavailable", unless you set AGB_SANDBOX_GATES=0.
The generated SBPL profile (lib/gates.mjs):
- C22 network denied (
(deny network*)); - writes denied everywhere, then allowed only under the worktree,
/tmp,/private/tmp, the OS temp dir,/private/var/foldersand the standard/devstreams; - writes to the worktree's
.gitandnode_modulesdenied again last, so they stay read-only even though they sit inside the worktree.
Paths are canonicalized first, since Seatbelt matches /private/var and not the /var symlink.
AGB_SANDBOX_GATES=0
This runs gate scripts unsandboxed with your full privileges. Use it only where something else is the isolation boundary, such as a disposable CI container, or when a gate truly needs the network or a global cache write.
Builder containment
Builders are launched with agy --sandbox and on top of a kernel containment floor. On macOS and Linux both are mandatory and cannot be bypassed: runAgy refuses with containment_unavailable (lib/agy.mjs, lib/agy.mjs).
| Platform | Containment floor | When it is missing |
|---|---|---|
| macOS | Seatbelt (sandbox-exec) | Builder refused |
| Linux | bubblewrap, verified by a probe run with --unshare-pid (lib/doctor.mjs) | Builder refused; systemd-run and unshare are not accepted as fallbacks |
| Windows | A Job Object via powershell.exe, which bounds process lifetime but not filesystem or network; agb also requires the active Windows sandbox check to pass (lib/agy.mjs) | Builder refused unless a valid bypass attestation exists |
Credential directories (agy 1.4.6 and later)
Builders run under agy --sandbox. From agy 1.4.6 that sandbox no longer lets commands read or modify ~/.docker, ~/.kube or ~/_netrc unless you grant access explicitly; earlier versions left ~/.docker and ~/.kube writable. A builder ticket that pushes to a container registry or talks to a Kubernetes cluster can therefore fail where it used to succeed, and the failure shows up as a failed command in the builder transcript, not as an agb error.
Gate commands are unaffected. They run in their own sandbox (above) with the network denied, and never had these credentials.
Keep registry and cluster work out of builder tickets. Deploy from outside agb, after the merge decision, which stays with a human. If a builder genuinely needs one of these paths, grant it explicitly in agy's sandbox configuration (see the agy 1.4.6 release notes) and scope the credential as narrowly as you can.
Windows bypass attestation
C22 On Windows only, a builder may run without the full sandbox if .adlc/config.json holds a valid sandboxBypassAttestation. It must:
- contain
installationId,repositoryRootCommit,repositoryOrigin,repositoryPath,nonce,runId,acknowledgedPlatform,authorizedBy,timestamp,expiresAtandsignature; - name
win32, expire, and live no longer than one hour (lib/doctor.mjs); - match this host's
~/.adlc/installation_id; - carry an HMAC-SHA256 signature over those fields made with
ADLC_ADMIN_KEY(lib/doctor.mjs); - use a single-use nonce, recorded on consumption so it cannot be replayed.
On any other platform the attestation is rejected: "Sandbox bypass attestation is only supported on Windows" (lib/agy.mjs).
Secrets never reach workers
Workers get an allow-listed environment. Known secrets (ADLC_ADMIN_KEY, ADLC_MANIFEST_KEY, GITHUB_TOKEN, cloud and model API keys), anything matching KEY|TOKEN|SECRET|PASSWORD|AUTH|CREDENTIAL|PRIVATE|CERT, and loader variables such as NODE_OPTIONS and LD_PRELOAD are stripped (lib/agy.mjs).
Summary of fail-closed rules
| Situation | Result |
|---|---|
Gate sandbox unavailable, AGB_SANDBOX_GATES unset | Gate fails |
| Builder containment unavailable on macOS or Linux | Builder refused |
| Builder containment incomplete on Windows, no valid attestation | Builder refused |
Rails declared but adlc not authenticated, or ticket store unreadable | Run refuses to dispatch (see Rail enforcement) |
For the threat model, see Security. agb doctor reports these checks before a run: see Verify.