sand-boxer/docs/runbooks/profile-bwrap-local.md
tegwick b6655d8859
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s
Add bounded stdin to owner execution
Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a06def-6490-7033-8448-2eab2d12ed44
2026-09-04 22:30:11 +02:00

3 KiB

Runbook: profile.bwrap-local owner execution

profile.bwrap-local copies the requested repository into an owner-managed workspace and starts a bubblewrap namespace with network default-deny. Commands must cross the sand-boxer owner boundary; direct namespace entry is unsupported.

Create with governed identity

sandboxer create \
  --profile profile.bwrap-local \
  --input repo=/path/to/repository \
  --actor agt \
  --project glas-harness \
  --session-id session-123 \
  --run-id run-456

Keep the returned sandbox id. The source path is copied; it is not mounted into the namespace.

Execute as the local owner

sandboxer exec SANDBOX_ID \
  --actor agt \
  --project glas-harness \
  --session-id session-123 \
  --run-id run-456 \
  -- python3 -c 'from pathlib import Path; print(Path.cwd())'

The result records the bound identity, profile and extension, command name, exit/timeout outcome, workspace, and declared network posture. Captured stdout and stderr default to 256 KiB per stream and the timeout defaults to 15 minutes.

Credential values are never command options or environment assignments. A rein that owns credential acquisition may receive only a catalog identifier, for example --credential-route-ref rein-openweights-openrouter-approle; inside the sandbox that appears as the value-free JSON list SANDBOXER_CREDENTIAL_ROUTE_REFS. The selected rein resolves and cleans up the credential through its approved owner-fronted route.

Resident owner API

The high-risk HTTP route is disabled unless the service process receives SANDBOXER_EXEC_TOKEN through its approved service credential delivery path. Before provisioning that credential, locate its custody route with warden route find "sand-boxer owner execution service credential" --json; do not paste it into a shell history, workplan, State Hub, or sandbox mount.

Call POST /v1/sandboxes/SANDBOX_ID/exec with a bearer token and this body:

{
  "command": ["python3", "-V"],
  "consumer": {
    "actor": "agt",
    "project": "glas-harness",
    "session_id": "session-123",
    "run_id": "run-456"
  },
  "credential_route_refs": [],
  "stdin_text": null,
  "timeout_seconds": 30,
  "max_output_bytes": 65536
}

The bearer authenticates access to the owner service; exact consumer identity matching additionally binds the command to the existing sandbox grant. stdin_text is capped at 1,000,000 UTF-8 bytes and is not returned or logged.

Destroy

sandboxer destroy SANDBOX_ID

Destroy kills the namespace process group and removes the copied workspace. The namespace is not tied to the process that handled create, because the resident API handles create and exec as separate requests. TTL expiry and the stale-resource reaper provide crash recovery for an owner service that exits before explicit destroy.

For a non-secret create/execute/source-absence/destroy proof, run:

make smoke-bwrap-exec
make smoke-bwrap-owner-api

The second target exercises the bearer-protected HTTP route with the real bwrap backend, not a mocked manager.