plimsoll


plimsoll runs untrusted code, reminiscent of code an AI agent wrote, inside a sandbox, and
each end result says how sturdy that sandbox was. A request can demand a minimal energy;
if the sandbox is weaker, nothing runs.


gvisor
Go Reference
Go version
release
license

The identify comes from the Plimsoll line, the load restrict painted on the skin
of a ship’s hull the place anybody can examine it. plimsoll does the identical for the
isolation tier: how sturdy the wall round a run is, said as one among 4
ranges, weakest first: course of, container, kernel, vm. The tier is a price on each
end result, not a sentence in a datasheet. A request can set a flooring, the weakest
tier it is going to settle for, and the official Go shopper checks the tier once more when the end result
comes again.

The identical equipment lets plimsoll run an examination on code an agent wrote, not solely comprise
it. A trial runner constructed into the simulation picture runs the agent’s program in opposition to
situations that program can not learn: every one reaches the runner in a file deleted earlier than
this system begins. Every run returns SHA-256 hashes of the request as despatched and the
end result as returned, which the official shoppers recompute (how). A
separate program outdoors the daemon, plimsoll-attest, indicators
the checked outcomes, refuses a sequence of calls with one lacking, and replays a run. The
examination itself (the simulated system, the situations, the cross mark) stays yours: plimsoll
provides the boundary and the proof.

An AI agent wrote a controller: a program that reads a system’s state at
each tick (right here, one 10 ms step of the simulation) and decides the way to push it. plimsoll
ran it in opposition to a simulated cart-pole, a cart on a rail with a pole hinged on
high, which the controller retains upright by pushing the cart left and proper. The simulator
is compiled to WebAssembly, a transportable bytecode that runs inside a bunch program,
and constructed into the sandbox picture. The controller was the one file the caller despatched, by way of
the atypical mission API, and it ran on the container tier.

The physics oracle: a cart with a pole balanced on it by an agent-written controller, beside the SHA-256 fingerprint of each run's trajectory

The cart-pole is the usual instructing drawback in management engineering. The runner, a
program constructed into the picture that runs the controller in opposition to the simulator, information
the run’s trajectory, each state at each tick, and hashes it right into a
fingerprint: a SHA-256 hash, so equal fingerprints imply equivalent numbers. Run
the accepted controller twice and the 2 fingerprints match. The agent’s first draft had
two good points (multipliers in its formulation) with the incorrect signal: it drops the pole at 5.96
seconds, and its fingerprint differs.

Pole angle and cart position over twenty seconds, for the accepted controller and for the draft that fell

Open the live run report ↗
to replay each runs within the browser, learn the controller the agent wrote, and see what the
web page intentionally doesn’t declare. One execution of the instance makes three sandbox runs: the accepted
controller twice and the draft as soon as. Reproduce them with
make docker-images && go run ./examples/oracle.

It studies the wall. Every end result states the isolation tier the run truly bought.
Every request can set a flooring, and a request whose flooring the daemon can not meet is refused
earlier than any code runs: ErrInsufficientIsolation means nothing ran.

res, err := supplier.Sandbox.RunJavaScript(ctx, sandbox.Request{
    Code:             "console.log(6 * 7)",
    MinimalIsolation: sandbox.IsolationVM, // ErrInsufficientIsolation means nothing ran
})
// res.Isolation studies the boundary that really ran.

Every refusal that ran nothing is marked as one, with a purpose (request, permission,
protocol, unsupported, isolation, setting, capability). sandbox.NotDispatchedReason(err)
reads the mark, whether or not the supplier (the backend that runs the code) is in
your individual course of or behind the daemon. An error with out the mark might have come after the
code began, so it’s by no means protected to retry robotically. A mark counts solely on a solution
that carries the shopper’s request ID again, so a proxy that serves one request’s reply
for one more can not make a name that ran learn “nothing ran”
(what comes back, and what it means).

The tier is proof the daemon collected: its configuration and its personal startup checks.
It will not be attestation, cryptographic proof from the {hardware} of what software program
is working, and no supplier right here affords that.
docs/isolation-tiers.md lists what every tier rests on.

It lets agent-written code name your API with out ever holding your credential, your
API’s handle, or open community entry.
A grant offers one run permission to
name listed routes of 1 API. The code calls by way of a small shopper plimsoll places within the
sandbox, and the dealer, the a part of plimsoll outdoors the sandbox that makes
the true name, checks the caller and the precise route, then attaches the credential itself.
The credential is minted per run: plimsoll asks for it as soon as per run, so it may possibly
be a contemporary short-lived token, and it by no means enters the sandbox. Code that skips the shopper
and calls out by hand will get no additional, as a result of the dealer, not the shopper, enforces the
guidelines. See docs/capability-grants.md.

Use it from an agent framework

These agent frameworks can hand their mannequin’s code to plimsoll by way of a software or code
executor of their very own (Trigger.dev, one among them, is a hosted TypeScript job
runner). Each row names what to put in, the information, and a web page that replays recorded calls
from that framework’s software to a sandbox and again, refusals included.

Give a Trigger.dev agent a code software

The TypeScript add-on offers an
executeCode software to a chat agent on Trigger.dev. On a daemon with a session, one sandbox stored
for a number of calls, every name is a cell, code run contained in the
interpreter stored for that dialog:
variables and recordsdata can survive the following name. Each software end result says whether or not
they’ll survive, whether or not this name began contemporary, the isolation tier it ran
behind, and the SHA-256 of the run report, a receipt for what was
despatched and returned that the shopper checked earlier than returning. The caller units a
minimal tier, so a weaker supplier refuses earlier than working code.

See the feature and deployment guide for the precise
ensures and manufacturing flooring. The
deployable Trigger.dev starter
has a chat agent and a job that proves two cells share one interpreter by way of
an actual daemon, with out making an AI mannequin name.

Run one thing in a single minute

The instance wants no daemon, no docker, no credentials and no community: it makes use of the wasm
supplier, which runs JavaScript on QuickJS, a small JavaScript engine
compiled to WebAssembly, inside your individual course of. Cloning and fetching Go modules do use
the community.

git clone https://github.com/plimsollmark/plimsoll && cd plimsoll
go run ./examples/minimal
supplier   wasm
isolation  course of
exit code  0
timed out  false
truncated  stdout=false stderr=false
length   304ms
stdout     {"Engineering":59000000,"Sales":20300000,"Operations":9800000}

The isolation line comes from the run’s end result, not from the instance’s personal textual content.
course of means there isn’t any operating-system wall in any respect: don’t use it for hostile
code.
The identical snippet runs on the kernel tier as soon as gVisor is put in.
gVisor is a layer between a container and your machine’s kernel that handles the
container’s requests to the working system itself, so the code by no means talks to your
kernel instantly. sudo ./docker/install-gvisor.sh installs a pinned gVisor
launch (one precise model, modified solely by modifying this repository) and registers its
runtime, runsc, with docker. Nothing else about this system modifications:

make docker-images   # builds the mission picture (plimsoll/sandbox) the docker supplier checks for
SANDBOX_PROVIDER=docker SANDBOX_DOCKER_RUNTIME=runsc go run ./examples/minimal

With SANDBOX_PROVIDER unset, plimsoll makes use of the Disabled supplier, which runs nothing, so
code execution isn’t on by chance.

Next: docs/getting-started.md walks by way of the identical items
on one machine: begin the daemon, create a caller credential, write your individual shopper, watch
a flooring refuse a run, then transfer the daemon from course of to kernel with out altering the
shopper. It additionally covers embedding the Go package deal as an alternative of working the daemon.

flowchart LR
  A["Agent or MCP gateway (MCP: how an AI app offers tools to a model)"] -->|"Run: one payload, a protocol quantity, minimum_isolation"| AU

  subgraph D["plimsolld"]
    AU["authenticate the caller"] --> FL["compare the floor with current provider evidence"]
    FL --> DI["dispatch"]
    BR["broker: holds the minted credential, matches the exact route, counts every call"]
  finish

  DI --> W["wasm: QuickJS on wazero (process tier)"]
  DI --> Ok["docker with runsc (kernel tier)"]
  DI --> V["e2b Firecracker (VM tier)"]

  W -. "host.get / host.publish" .-> BR
  Ok -. "per-run Unix socket" .-> BR
  V -. "authenticated guard" .-> BR
  BR ==> |"your credential, connected host-side"| API["Your API"]



Loading


The code contained in the sandbox, the visitor, by no means holds the credential and by no means
reaches the community instantly. A run with no grant has no community in any respect.

plimsolld is the plimsoll server. Each one runs precisely one supplier, chosen by
SANDBOX_PROVIDER:

Provider Boundary Tier reported Use
wasm QuickJS on wazero (a WebAssembly runtime written in Go), inside plimsolld itself course of Fast native improvement. An escape (a bug that lets code out of its sandbox) within the engine lands inside your daemon.
docker with runc, docker’s default runtime a container sharing your machine’s kernel container Self-hosting when a kernel bug will not be one of many assaults you propose for. With a mission picture it retains a session: one container for a lot of calls, with a JavaScript interpreter (and a Python one with plimsoll/sandbox-python because the mission picture) whose variables survive between calls.
docker with runsc gVisor, as soon as the startup checks affirm docker has runsc registered kernel Hostile code by yourself machines. Keeps periods too.
e2b a microVM (a small digital machine made for one run, then destroyed) from E2B, a hosted service that runs them on Firecracker, AWS’s open-source VM monitor vm Hostile code, on E2B’s machines reasonably than yours; billed per run. Keeps periods too, billed whereas the microVM runs; a droop pauses it, which retains the recordsdata however not an interpreter’s variables.
dockercloud a microVM from Docker Cloud Sandboxes, Docker’s hosted sandbox service vm Hostile code, on Docker’s machines; billed per run. It speaks two APIs, chosen by SANDBOX_DOCKERCLOUD_API, and each handed the dwell suite on 2026-10-04: the one Docker launched earlier than launch, now not documented, which is the default as a result of solely it has grants and picture proof, and the REST API Docker paperwork, stored as a backup till it covers these (no grants, refused below PLIMSOLL_HARDENED=1, runs of at most 270 s). The account’s community coverage have to be deny-all (no connection until a rule permits it), and each run checks that. Grants work by way of the identical guard as E2B (an handle on the plimsoll server, the one place the microVM might connect with) when SANDBOX_DOCKERCLOUD_GUARD_URL is about. Unlike E2B, the visitor holds its personal run’s short-lived credential for the guard, and the one community rule is about by way of a Docker name outdoors its revealed API.
openshell a sandbox from OpenShell, NVIDIA’s agent sandbox runtime, created by its gateway server on docker container Agent platforms that already run an OpenShell gateway. Each run will get its personal sandbox with no community; plimsoll reads its settings again, refuses to run on any distinction, and deletes it afterwards. Tested in opposition to a v0.1.2 gateway on 2026-09-28. Grants attain the dealer by way of a relay contained in the sandbox that plimsoll connects to from outdoors, so the sandbox wants no community guidelines. Keeps periods too.
unset nothing runs n/a The default.

Each tier rests on the daemon’s configuration, what the supplier studies, and an actual take a look at
run at startup; none is attestation. docs/isolation-tiers.md
lists the proof for every tier and reveals how a request units its flooring. A session trades
the contemporary sandbox of each name for velocity and stored state: code an earlier name ran can
change what later calls see, and a name with API entry wants a grant that permits periods
(docs/sessions.md). A session is one belief
area, and plimsoll ties it to the authenticated caller, to not that caller’s clients:
a service that runs many shoppers’ code by way of one credential should preserve every buyer
in a session of their very own
(who may share a session). The docker
supplier also can apply the shipped seccomp profile, an inventory of the one
system calls (requests to the kernel, reminiscent of opening a file) its containers
might make (docs/seccomp.md).

Page What it reveals
Simulation replay pages, all eight simulators ↗ The eight simulators constructed into the simulation picture for controller runs: bathe, buck converter (an influence provide that steps a voltage down by switching it on and off), ship heading, black gap orbit, relativistic rocket, satellite tv for pc clock, double slit, and cart-pole swing-up (the picture additionally carries the fashions the module-run exams use). Each web page runs a failing and a passing hand-written controller by way of the sandbox and replays each trajectories with their fingerprints.
A controller in C, compiled in the sandbox ↗ The identical cart-pole simulator, with a swing-up controller (it swings the pole up from hanging, then balances it) written in C and compiled to WebAssembly by the run’s personal first step, so the simulator and the controller are each WebAssembly. The run report compares it with the JavaScript model tick by tick. The two languages’ cos features disagree within the final bit on about one enter in 100, so in three of the 4 situations one or two power values differ, by just a few representable doubles (at most 14). The fingerprint catches that distinction, and the movement itself is equivalent to the bit. Source and caveats in its README; reproduce with make docker-images && go run ./examples/wasm-controller.
A buck converter controller in C ↗ An influence provide’s management regulation (the formulation its controller applies at every tick) written in C, the language converter firmware is written in. It is compiled to WebAssembly within the sandbox and run in opposition to a simulated converter, whose output it should maintain at 5 V whereas the load modifications out of the blue. It calls no library operate, so its trajectory equals the JavaScript model’s byte for byte in all 4 situations; the web page charts one among them tick by tick. Source and caveats in its README; reproduce with make docker-images && go run ./examples/wasm-buck.
Same run, different sandboxes ↗ The cart-pole run from the highest of this web page, on native runc and gVisor, an OpenShell gateway, E2B and Docker Cloud Sandboxes: three isolation tiers, two Node variations, and one fingerprint, the identical one the primary report revealed. Reproduce the configured rows with go run ./examples/suppliers; the cloud rows are billed.
One sandbox, five calls ↗ A session on an OpenShell sandbox: a failing take a look at, a patch, the take a look at passing with out the recordsdata being despatched once more, and a leftover course of that’s passed by the following name. Each name’s run report (the daemon’s assertion of what was despatched, what got here again and the place it ran) is signed and linked to the earlier one. The verifier accepts the signed set, and refuses it when one name is dropped or one byte is modified. Reproduce with go run ./examples/periods and a gateway.
The efficiency advisor’s report ↗ The advisor reads the API calls a run made and factors out wasteful patterns. One measured run: the identical query requested of an API as 13 calls, then as 1, and the advisor’s discovering that names the route that solutions it in a single name.
Twelve interactive lessons ↗ How a run is executed, the suppliers, the API dealer, and connecting an AI agent. Static pages: no community calls, no analytics, no third-party scripts.

  • Pre-1.0, single writer, no exterior customers but. Breaking modifications land and not using a
    deprecation path, on goal.
  • No third-party safety audit has ever been carried out, and the writer’s personal
    self-review ledgers should not revealed both. SECURITY.md says what
    exists, what it’s price, and what you possibly can examine your self as an alternative.
  • CI runs the gate, within the open, and every examine means what it ran. The
    audit workflow has three jobs. audit runs plain
    make audit on each push to essential and each pull request: construct, vet, race exams, lint,
    buf lint, a generated-code drift examine, and govulncheck. After audit, shoppers
    runs make clients-suite: npm ci in shoppers/typescript and
    examples/trigger-chat, then the Python and TypeScript Go take a look at packages in
    required mode. A lacking runtime, dependency or skipped Go take a look at fails that job.
    audit-docker additionally follows audit and
    runs make docker-suite with the photographs ready, in required mode: a lacking
    daemon, a lacking picture or a skipped take a look at fails the job. A inexperienced audit-docker
    due to this fact means the docker suite ran below runc with the shipped seccomp profile
    and proved a read-only root, sized noexec writable mounts, and the dealer’s
    refusals. The gvisor workflow runs the identical suite
    below runsc, the kernel tier, from the pinned installer. The race detector runs in
    the plain audit job and each docker-suite jobs; the shopper job runs with out it.
    No examine workouts E2B or Docker Cloud Sandboxes: these suites drive dwell paid companies and are
    intentionally by no means wired to a runner. See CONTRIBUTING.md.

The interactive lessons are the
quickest approach in in the event you would reasonably learn than clone. Start with
Plain English
for the concept earlier than the API, or
Quick start to run
one thing. Every time period these docs use has a one-sentence definition within the
glossary. The classes
dwell in docs/trainers/ and work offline: open any file from a clone in a
browser. GitHub reveals .html recordsdata as supply as an alternative of rendering them, which is why the
hyperlinks above level on the revealed copy.

Each of those solutions one query, finish to finish.

Document Answers
docs/getting-started.md How do I construct it, embed it, begin it as a service with authentication, and watch a flooring refuse a run?
docs/example-programs.md What do the runnable examples show, and which ought to I learn first?
docs/isolation-tiers.md What does every tier relaxation on, and the way do I demand one per request?
docs/capability-grants.md How does agent code name my API with out ever holding my credential?
docs/run-results.md What comes again, and when is a failure an error reasonably than a end result?
docs/run-records.md What does every run’s report state, how do I recompute it in one other language, and the way do I signal, confirm and replay information outdoors the daemon?
docs/sessions.md How do I preserve one sandbox for a lot of calls, and what holds between the calls, an interpreter’s variables included?
clients/python How do I name plimsolld from Python?
clients/typescript How do I name it from TypeScript, and provides a Trigger.dev or Mastra agent a code software that retains its state?
docs/trigger-dev.md How do calls that reuse one interpreter, an isolation flooring and checked run information seem collectively in a Trigger.dev software, and the way do I deploy a job?
docs/ai-sdk.md How do I give a Vercel AI SDK agent an executeCode software, contemporary per name or stored for one consumer’s dialog?
docs/google-adk.md How do I make plimsoll the code executor of a Google ADK agent, with enter recordsdata, output artifacts and a refusal under the ground?
docs/placement.md I run a number of daemons: how do I choose one per request, and when is a refusal protected to retry elsewhere?
docs/inner-loop-workflow.md How do I iterate quick domestically with out delivery a weak sandbox to manufacturing?
docs/efficiency-advisor.md What does the advisor see, why can what it information by no means embody the info the code despatched or acquired, and the way do I select what it emits?
docs/hardened-mode.md How do I make the daemon refuse to begin until each manufacturing safeguard is about?
docs/dependencies.md Which dependencies have to be trusted for the sandbox to carry, and the way are the instruments that examine them pinned?
docs/limitations.md What does this intentionally not do?
docs/dockercloud.md What does the Docker Cloud Sandboxes supplier want from the operator, and what does every run examine?
docs/openshell.md What does the OpenShell supplier want from the operator, and what does every run examine?
docs/releasing.md Why is the module path public, why do releases begin at v0.2.0, and why is there no checksum exemption?
docs/callers.md How do I create, rotate and revoke caller credentials?
docs/seccomp.md and docs/gvisor.md What do the seccomp filter and gVisor implement?
docs/guest-dependencies.md How do npm packages get right into a sandbox that has no community?
docs/architecture/credential-minting.md Where does a per-run credential come from, and the place does it keep?
docs/seams.md Where are the deliberate extension factors?
Glossary ↗ What does this phrase imply? One plain sentence per time period.
AGENTS.md The structure reference: suppliers, the principles no change might break, and each setting variable.

Apache-2.0.



Source link