Offense runs two kinds of work in a sandbox: a live assessment through the runner, and a source scan of an uploaded archive. Both use the same saved worker snapshot and the same isolation rules.
The snapshots
Sandbox images are versioned saved snapshots. They are release artifacts with recorded metadata, not a mutable "latest" that changes under a running job.
| Snapshot | Adds |
|---|---|
offense-worker-v0-1-0 |
Base workstation: Node.js, Python, Docker, git, curl, jq |
offense-worker-v0-2-0 |
Per-job model gateway, egress proxy and UID firewall, evidence manifest and redaction, separate verifier |
offense-worker-v0-3-0 |
Nuclei 3.11.1 (13,899 templates), testssl.sh, ffuf, Trivy, jwt_tool, plus a partial OWASP Top 10 coverage map |
offense-worker-v0-4-0 |
Semgrep, Schemathesis, Arjun, Corsy, Interactsh client — the current default |
The runner creates sandboxes from offense-worker-v0-4-0 with noEnv: true and
a finite TTL, so a job inherits no owner environment. Job-scoped secrets are
injected at runtime and revoked on terminal status.
Known limits the release notes record plainly: container egress mediation still uses host networking rather than full mediation, and not every added CLI has been run end-to-end in production. The v0.4 tools were validated as starting in a fresh sandbox.
Engines
An external runner accepts engine: "strix" or engine: "shannon".
- Strix receives the model in its own dialect (
openrouter/<id>). - Shannon receives
openrouter:<id>(SHANNON_AI_MODEL) and runs its nested Docker Compose stack — Temporal plus an ephemeral pentest worker. It runs in no-source mode: an empty repository is mounted, and only findings that are live-validated (SARIFstatus: exploited) are reported.
Both reach the model only through the per-job gateway, which holds the provider
key. The investigator gets a short-lived bearer token, a request cap and a
model-spend cap. The gateway requests usage accounting, records token counts and
cost, enforces its own caps, and writes model-usage.json into the evidence
bundle.
Network boundary
- Egress is allowlist-only and exact:
hostorhost:port, port defaulting to- Wildcards, private and local addresses, and database or mail ports are refused, and the list is capped at 20 entries.
- Only the target's host and port are allowlisted for a job. The login host is added only when it differs.
- Destination scope is enforced outside the model: redirects and IPv4/IPv6 resolution are validated, and metadata and private/control-plane endpoints are blocked.
Model routing
- Platform-funded (
PLATFORM_OPENROUTER_KEY): self-service workspaces launch without connecting a key; model usage bills at provider cost + 20%. - Workspace BYOK: an OpenRouter key stored at
/app/settings, encrypted with AES-GCM underCREDENTIALS_KEY(base64, 32 bytes). Only the last four characters are stored in clear. Model usage recordsbilledMicros = $0, and a separateMODEL_BUDGET_MICROScap (default $1) applies. - A workspace key does not override a Boundless selection.
The catalog exposed by GET /api/v1/models includes
boundless/deepseek-v4.1-flash (at cost), deepseek-v4.1-flash-abliterated (at
cost, requires basic KYC self-attestation) and deepseek/deepseek-v4.1-flash-1017
(provider cost + 20%, or direct with a workspace key). ASSESSMENT_MODEL_ID
overrides the default; the provider-specific model ID is saved on each
assessment so routing and metering agree.
Runner deployment shape
The runner is a Cloudflare Container Worker (offense-runner) in the same
account, reached over the RUNNER service binding. It is a control plane, not
the isolation boundary — Boat remains the provider for each job.
Jobs are sharded across RUNNER_SHARDS containers by assessment id, each running
RUNNER_CONCURRENCY jobs, so a sandbox always lands on the container holding its
job.
Sandbox templates
There is no user-editable template editor. What you can vary per launch:
| Lever | Where |
|---|---|
| Budget | budgetMicros on launch |
| Egress | egress on launch, or POST /sandboxes/:id/egress |
| Instruction | instruction in the body, or /sandboxes/:id/config |
| Model | model in the body, or the workspace default |
| Lifetime | maxSeconds (clamped by MAX_SANDBOX_SECONDS, default 14400) |
| Engine | engine when dispatching through the runner |
For repeatable multi-step work, the product's template concept is a recipe —
an orchestration contract rather than a Boat snapshot. Recipes such as
critical-dependency-remediation describe the profile (agent+verifier,
ci+assessment, …), the prompts, the tools and the ordered steps; the coding
agent does the editing and Offense supplies disposable runtime validation.