Providers

W&B Sandboxes Provider

W&B Sandboxes Provider

Read when:

  • choosing provider: wandb (alias weights-and-biases);
  • pointing Crabbox at a Weights & Biases Sandbox;
  • changing internal/providers/wandb.

W&B Sandboxes are managed, isolated Linux containers backed by CoreWeave Sandboxes. The provider talks to the CoreWeave gateway over gRPC (coreweave.sandbox.v1beta2) using the W&B API key as the x-wandb-api-key metadata header, so a caller who has already run wandb login does not need a new provider-specific credential.

This is a delegated-run provider: Crabbox does not provision an SSH box and does not go through the broker coordinator. The CLI starts a sandbox, runs a single command in it via the unary Exec RPC, and stops it again. There is no workspace sync, so crabbox run requires --no-sync.

> Experimental. The upstream API is coreweave.sandbox.v1beta2 and W&B > Sandboxes are in preview. Expect field renames and breaking changes until > upstream cuts a stable v1; the protos are pinned via buf.lock in > internal/providers/wandb/proto/.

#When to use

Use W&B Sandboxes when you already have W&B credentials and want a managed Linux sandbox without provisioning your own runner or creating a new account. Pick a provider with SSH (AWS, Hetzner, Static SSH, Daytona) instead when you need direct shell access or a persistent dev box — this provider is exec-only by design.

W&B Sandboxes are Linux-only. Desktop, browser, code, SSH, Actions hydration, and the broker coordinator are not available.

#Commands

crabbox run    --provider wandb --no-sync -- python eval.py
crabbox run    --provider wandb --no-sync --id swift-crab -- pnpm test
crabbox run    --provider wandb --no-sync --keep --lease-output session.json -- true
crabbox status --provider wandb --id swift-crab
crabbox stop   --provider wandb swift-crab
crabbox list   --provider wandb
crabbox doctor --provider wandb

Without --id, run acquires a new sandbox, runs the command, and stops the sandbox afterwards. With --id <sandbox-id> it execs into the existing sandbox and leaves it running. warmup is rejected — sandboxes are acquired per run, with no separate provisioning phase.

--lease-output <path> writes a reusable session handle containing the stable sandbox ID and an exact cleanup command. Pair it with --keep when another process should reuse the newly acquired sandbox.

#Auth

Resolve the W&B API key in this precedence (first match wins):

  1. CRABBOX_WANDB_API_KEY — explicit override, CI-friendly.
  2. wandb.apiKey — config-file value.
  3. WANDB_API_KEY — canonical env var written by wandb login.
  4. ~/.netrc — the machine api.wandb.ai (or .com) entry written by wandb login.

WANDB_ENTITY_NAME is also required; it is sent as the x-entity-id gRPC header. WANDB_PROJECT is optional and, when set, is sent as x-project-name.

export CRABBOX_WANDB_API_KEY=wandb_v1_...
export WANDB_ENTITY_NAME=my-team

The API key is never exposed as a CLI flag; do not pass secrets on the command line.

#Config

provider: wandb
target: linux
wandb:
  defaultImage: ubuntu:24.04
  maxLifetimeSeconds: 1800   # 30 min; W&B reclaims the sandbox at this limit

Runtime defaults applied when unset: defaultImage is ubuntu:24.04 and maxLifetimeSeconds is 1800. The raw configuration remains empty/zero until the provider applies these defaults; flag registration does not eagerly fill them.

Provider flags (each overrides the matching wandb.* config key):

--wandb-image           # container image used when acquiring a new sandbox
--wandb-max-lifetime    # maximum sandbox lifetime in seconds

Environment overrides:

  • defaultImage also reads CRABBOX_WANDB_DEFAULT_IMAGE, then WANDB_DEFAULT_IMAGE.
  • maxLifetimeSeconds also reads CRABBOX_WANDB_MAX_LIFETIME_SECONDS, then WANDB_MAX_LIFETIME_SECONDS.
  • CWSANDBOX_BASE_URL overrides the gateway endpoint (default api.cwsandbox.com:443); the https:// / http:// scheme is stripped if present.

The three configuration settings use shared typed bindings. Empty file/environment strings preserve prior values. File maxLifetimeSeconds assigns only when positive; zero or negative file values leave the prior setting intact. Environment parsing instead accepts signed integers: a parsed primary value, including zero or a negative value, wins. An empty or malformed primary falls back to the vendor alias, then to the prior setting. Runtime defaulting and the lease TTL still determine the effective lifetime. The client-owned API-key precedence above is unchanged.

#Lifecycle

crabbox run:

  1. With --id <sandbox-id>, require the exact local claim created when Crabbox acquired the sandbox, verify that the claim still matches the W&B endpoint, entity, project, and sandbox ID, then require the provider-side crabbox inventory tag before Exec.
  2. Otherwise, Start a sandbox with an idle keep-alive command, poll until it reaches RUNNING (200 ms backoff, ×1.5, capped at 2 s), persist the exact ownership claim, Exec the command, then Stop it unless --keep is set. If claim persistence fails, Crabbox rolls back the acquired sandbox before returning.

Run outcomes and timing are finalized after automatic Stop. A Stop failure makes an otherwise successful run fail with exit 1 and retains its recovery session and unchanged claim. Command failures and mapped gRPC exit codes stay primary when Stop or timing output also fails; secondary diagnostics remain visible. gRPC/API failures are classified as provider-error, not as observed command exits, while keeping their mapped numeric exit codes. --keep-on-failure is decided before timing output, so a reporting failure cannot discard an already-failed run's sandbox. A timing-output failure after successful Stop does not claim the sandbox is retained. Closing the local gRPC connection remains warning-only. Command timing measures Exec separately; total timing includes acquisition and automatic cleanup.

status and stop enforce the same exact claim and tagged-inventory checks before issuing Get or Stop. A tagged sandbox without the matching local claim, or a claim from another endpoint, entity, or project, fails closed. Successful automatic or explicit stop removes the unchanged claim; failed cleanup keeps it so a later retry can still route to the owned resource. If tagged inventory no longer contains a claimed sandbox, Crabbox probes that exact ID: a confirmed provider NotFound lets stop remove only the unchanged stale claim. An untagged but existing sandbox or any lookup error preserves the claim and fails closed. status renders the sandbox state (for example running; a COMPLETED sandbox is reported as stopped so status --wait treats it as terminal). list paginates sandboxes tagged crabbox. stop issues a graceful stop (15 s timeout). Acquire and exec apply a startup timeout that is the lesser of five minutes and the sandbox lifetime.

#Capabilities

  • SSH: no — gRPC Exec only.
  • Crabbox sync: no — --no-sync is required.
  • Desktop / browser / code: no.
  • Actions hydration: no.
  • Coordinator (broker): no — always direct from the CLI.

#Gotchas

  • maxLifetimeSeconds is the main billing-safety limit. Crabbox defaults to 30 minutes; raise it with --wandb-max-lifetime only when needed. A lease TTL shorter than the configured lifetime narrows it further.
  • Exec is the unary RPC, so command output is buffered server-side and returned at completion rather than streamed; there is no interactive PTY.
  • Environment variables are applied at Start time only. When you target an existing sandbox with --id, selected user env vars cannot be forwarded onto it (the Exec RPC has no env field), so explicit --allow-env selections containing such values are rejected, including CI and NODE_OPTIONS. Crabbox's local run metadata (CRABBOX_LEASE_ID, CRABBOX_RUN_ID, and CRABBOX_SLUG) may be omitted for reuse, including when an env summary is requested. The built-in implicit CI/NODE_OPTIONS defaults retain their existing omission exception. Neither exception forwards values through Exec or refreshes the sandbox's original Start-time environment; similarly named custom variables are not treated as reserved metadata.
  • --reclaim, --shell, --sync-only, --checksum, --force-sync-large, --full-resync, --download, --artifact-glob, and --require-artifact are rejected: W&B owns the sandbox lifecycle and there is no Crabbox SSH/rsync target.
  • --reclaim remains unsupported because the pinned W&B API has no safe way to adopt a tagged external sandbox into a conflict-safe exact local claim.
  • --keep retains a newly acquired sandbox after the run; --keep-on-failure retains it only when the command fails, so you can debug. Both paths retain the exact local claim needed by later run --id, status, and stop calls.
  • gRPC failures map to sysexits-aligned exit codes: 77 (unauthenticated / permission denied), 4 (not found), 124 (deadline exceeded), 69 (unavailable / resource exhausted).

#Live smoke

Run a live smoke when changing W&B lifecycle, exec, status, or auth code. Keep the API key in the environment; never pass it as an argument.

export CRABBOX_WANDB_API_KEY=wandb_v1_...
export WANDB_ENTITY_NAME=my-team
go build -trimpath -o bin/crabbox ./cmd/crabbox
CRABBOX_LIVE=1 \
CRABBOX_LIVE_PROVIDERS=wandb \
CRABBOX_LIVE_COORDINATOR=0 \
CRABBOX_LIVE_REPO=/path/to/my-app \
scripts/live-smoke.sh

The shared harness exits before any W&B provider command when no API key is exported. With the API key and entity configured, it runs doctor, creates a kept sandbox with a 60-second lifetime, proves status and reuse through the exact claim, proves that the provider tag alone cannot authorize stop, then stops the sandbox and confirms no active remote inventory or local claim residue.

For manual debugging, run the same lifecycle directly:

export CRABBOX_WANDB_API_KEY=wandb_v1_...
export WANDB_ENTITY_NAME=my-team
go build -trimpath -o bin/crabbox ./cmd/crabbox

bin/crabbox doctor --provider wandb
bin/crabbox run --provider wandb --no-sync --wandb-max-lifetime 60 -- echo crabbox-wandb-ok

Or the Go smoke test (acquire, exec, stop):

go test -tags smoke -run TestSmokeVersionAndExec -v ./internal/providers/wandb/

#Regenerating the proto stubs

cd internal/providers/wandb/proto
buf dep update          # refresh buf.lock
buf generate            # regenerate ../gen/

Bump the BSR commit pin in buf.lock only via a dedicated, auditable PR.

Related docs: