This page explains how to check a change before you commit it: what each command runs, what it proves, and what it cannot prove.
- Node.js 24.11 or later. Run
npm installin the repository root. - Docker with BuildKit. The shim builds for
linux/amd64, so on an Arm machine Docker needs amd64 emulation. - If your network inspects TLS, set
SANDBOX_EXTRA_CAto a CA bundle that trusts it. Image builds pass the bundle as a BuildKit secret, so it never becomes part of an image. Without the variable, builds work as before.
Done when npm install finishes and docker info succeeds.
Run both commands before each commit:
npm run check
npm testnpm run check runs:
vp pack, which builds the package intopackages/sandbox/dist.vp check, which formats, lints, and type-checks the TypeScript. The lint rules include the anti-slop rules intools/oxlint/anti-slop. Fix what they report instead of adding a suppression.knip, which finds unused files, exports, and dependencies.shim:check, described below.
npm test runs the package's unit tests with vp test, then shim:check again. Integration tests skip themselves unless you turn them on.
Done when both commands exit with code 0.
To run one test file while you work:
npx vp test run packages/sandbox/tests/read-file.test.tsThe unit tests in packages/sandbox/tests run in Node.js, not in workerd. They give the package test doubles instead of a container: helpers.ts builds shim frames and fake processes, worker-test-doubles.ts provides Worker runtime objects such as ExecutionContext, and cloudflare-workers.ts stands in for the cloudflare:workers module. The lint rules forbid module mocking, so pass doubles in through constructors and parameters.
npm run shim:check builds the verify target of images/sandbox-tools/Dockerfile for linux/amd64. Each stage fails the build when its check fails:
cargo fmt --check,cargo clippywith warnings as errors, andcargo test. The tests includecrates/sandbox-tools/tests/shim.rs, which runs the built binary as a subprocess.- A static release build of
sandbox-shim. - The contract test,
packages/sandbox/tests/shim-contract.test.mjs. It runs the real package code against the compiled shim through a stand-in forexec()that spawns local processes, so the Rust and TypeScript sides of the protocol are tested together. - Checks that the binary is a 64-bit x86-64 ELF with no interpreter and no shared library dependencies.
The Rust tests run inside the Linux build, so Linux-specific behavior is tested even on a Mac. Run cargo test on the host only for quick feedback. Tests that mount a tmpfs skip themselves in the build, which cannot mount; npm run test:shim-mounts reruns them in a privileged container.
npm run test:release runs everything below and npm test. Each part can also run on its own.
| Command | What it proves | Needs |
|---|---|---|
npm run test:package |
The built package exports the expected runtime API. It also prints the files that npm pack would publish. |
Nothing extra |
npm run test:shim-mounts |
The shim's tests pass with real mounts, including that a restore refuses a directory with a mount inside it and never deletes files inside a mount | Docker, privileged containers |
npm run test:s3-e2e |
S3Gateway authorizes, signs, and forwards the requests that a real s3fs sends to MinIO for reads, writes, renames, directories, multipart uploads, and deletes |
Docker, privileged containers |
npm run test:s3-lifecycle |
The shim mounts, reuses, inspects, denies, retries, and unmounts a real s3fs mount |
Docker, privileged containers |
The S3 tests start MinIO and an s3fs container, and reach services on the host through host.docker.internal. They remove the containers, networks, and images they create when they finish.
npm run test:s3-native-local runs the full mount lifecycle under wrangler dev. It is not part of test:release, because it needs Wrangler and workerd builds that are not released yet. Set SANDBOX_WRANGLER_PATH and MINIFLARE_WORKERD_PATH to local checkouts that contain the commits listed in packages/sandbox/tests/s3-native-local.integration.test.ts.
Local tests cannot show everything a deployed sandbox does. Under wrangler dev, the container has Docker's short hostname instead of the 64-character hostname of a deployed container, non-root users have no Linux capabilities, and reloading the Worker destroys the container instead of leaving it running. Deploy an example when a change depends on behavior like this.
-
Log in with
npx wrangler login, and setCLOUDFLARE_ACCOUNT_IDif your login has more than one account. -
Deploy the example:
npm run example:workspace:deploy
The deploy scripts build
sandbox-tools:localfirst when the example uses the shim, and use the Wrangler version pinned inpackage.json. -
Follow the
curlsteps in the example's README. If your account putsworkers.devbehind Cloudflare Access, add the headercf-access-token: $(cloudflared access token -app=<WORKER_URL>)to each request. -
Delete what you deployed.
npx wrangler delete --name <WORKER_NAME>deletes the Worker. At Wrangler 4.137.0, Wrangler cannot delete the container application of a Durable Object, so delete it with the Containers API: requestGET /accounts/<ACCOUNT_ID>/containers/applications/<ID>and check that the name is yours, then sendDELETEto the same path. Delete by exact name or ID, never by a pattern.
Done when the README's steps give the expected responses and the Worker and container application are gone.
Workers Observability records each request, log, and exception, which helps when a step fails. A Durable Object invocation marked canceled is often a client that disconnected, not a failure in the container.