Skip to content

Latest commit

 

History

History
80 lines (77 loc) · 5.97 KB

limitations.md

File metadata and controls

80 lines (77 loc) · 5.97 KB

What plimsoll does not do

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.
  • wasm runs 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 install cannot 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, /healthz and /readyz. Setting a guard URL without PLIMSOLL_GUARD_ADDR fails 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, so E2B_GUARD_URL must 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.
  • /readyz reports configuration and reachable dependencies, not a working run. For docker it checks the docker daemon and runtime it is configured to use. For e2b it 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 startup SmokeTest, 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 /readyz on e2b means "configured", not "working". A red one says only not 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. kernel and vm rest 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.mod tool directives and the gate's programs by gate-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 through SANDBOX_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.