Stated here so you do not have to discover it in review.
Part of the plimsoll README.
- It does not build the walls itself. gVisor (a layer that answers a container's requests to the kernel itself) and Firecracker (AWS's open-source virtual machine monitor) do that.
wasmruns snippets only. That provider (a provider is the backend that runs the code) runs JavaScript on QuickJS, a small engine compiled to WebAssembly (a portable bytecode), inside the daemon. It has no multi-file projects, so a project grant, permission for a run's code to call listed routes of your API, is rejected on it outright.- No package installation during a run. A run has no network, so
npm installcannot happen inside it, from a public registry or a private one. Dependencies are built into the project image ahead of time, which is also where the registry credential lives and the only place it ever exists; docs/guest-dependencies.md is the recipe. An agent that must install arbitrary packages mid-run is the case general-purpose sandbox virtual machines cover and plimsoll does not. - E2B grants need
E2B_GUARD_URL. E2B is a hosted service that runs each request in a microVM, a small virtual machine made for one run. With a grant, the VM's one permitted connection goes to the guard, an address on the plimsoll server that checks each call, so the credential and the route checks stay outside the hostile VM. Without a grant, an E2B run has no egress: nothing leaves the VM. - The E2B guard only works in the daemon process that started the run, so it cannot sit behind an ordinary load balancer. A run's guard credential lives in the memory of the process that created the run, so a guard request sent to a second copy of the daemon is rejected as an unknown credential, even though it is valid. Whatever serves the public guard URL must be the same process that launches the runs. Running more than one copy needs a separate guard address for each daemon (a distinct hostname or path), not requests spread across the copies in turn.
- The guard needs an address of its own,
PLIMSOLL_GUARD_ADDR. Code in a granted E2B or Docker Cloud run may connect to the guard and nowhere else, so plimsolld serves the guard on a listener that answers the guard path (/v1/e2b/guard,/v1/dockercloud/guard) and nothing else, never on the address callers use for the RPC procedures,/healthzand/readyz. Setting a guard URL withoutPLIMSOLL_GUARD_ADDRfails startup, and the public guard URL must reach that address, so a deployment with grants publishes two addresses: the guard to the provider's network, and the RPC address to its callers. On E2B the guard's network rule names a host, never an address (E2B refuses an address, soE2B_GUARD_URLmust use a name): E2B sends port 443 connections from code in a granted run to that name's own host, whatever address the code asked for, and lets it reach port 80 of the same host. Serve nothing else on ports 80 and 443 of the guard's host name. /readyzreports configuration and reachable dependencies, not a working run. Fordockerit checks the docker daemon and runtime it is configured to use. Fore2bit checks settings only, and does not prove the API is reachable, the key is valid, or the guard can be reached. The proof of behaviour is the startupSmokeTest, which runs once and creates a real microVM that costs money, so it deliberately does not run on/readyz, which anyone can poll without logging in. A green/readyzone2bmeans "configured", not "working". A red one says onlynot ready; why is in the daemon's log, since a poller without a login has no business reading host or image names.- The isolation tiers are evidence, not attestation. A tier
is how strong the wall around a run is; attestation is cryptographic proof from the
hardware of what software is running.
kernelandvmrest on which provider it is, the daemon's and the runtime's configuration, and the startup tests, as isolation-tiers.md spells out. Nothing here measures a hypervisor (the program that runs virtual machines) or checks a kernel wall cryptographically. If your threat model (the attacks you must hold out against) needs attestation, no tier here supplies it. - There is no single control point across several daemons. Each daemon is configured and controlled on its own.
- Nothing updates itself. Updates are manual. The QuickJS build is
pinned, fixed to one exact version, by its SHA-256, and gVisor by a SHA-512 of its release recorded for
each architecture the installer supports (x86_64 and aarch64); the installer refuses
any other architecture. Code generators are pinned by
go.modtooldirectives and the gate's programs bygate-tools.versions. Every base image the recipes build on is pinned by its digest (a hash of its content), but the packages they install are not: apk takes version ranges (python3~3.14) and apt the distribution's current release, so rebuilding an image can change its installed software. Launch-time image digest pinning is opt-in throughSANDBOX_REQUIRE_PINNED_IMAGES=1. - A restarted daemon forgets the sandboxes it was still removing. When a sandbox's removal is not confirmed, the daemon keeps counting it against its capacity until it is gone. That count lives in the daemon's memory: after a restart, such a leftover is removed once the lifetime label it was started with has passed, but until then it does not count against the new process's limits. Several daemons may share one docker host, and a leftover names no daemon, so a restarted daemon cannot tell its own leftovers from another daemon's live sandboxes.