{"slug": "show-hn-plimsoll-a-self-hosted-sandbox-for-code-your-ai-agent-writes", "title": "Show HN: Plimsoll, a self-hosted sandbox for code your AI agent writes", "summary": "Plimsoll, a self-hosted sandbox for running untrusted code such as code written by an AI agent, was released as an open-source project that reports the isolation tier of every run as one of four levels — process, container, kernel, or vm — and refuses to execute anything when a request's minimum isolation floor cannot be met. The Go client checks the tier on the returned result, and a separate plimsoll-attest program signs checked results, rejects a call chain with one missing link, and replays runs; every run returns SHA-256 hashes of the request as sent and the result as returned. In a cart-pole example, an agent-written controller ran at the container tier, where the accepted controller produced matching trajectory fingerprints across two runs while the agent's first draft, with two gains of the wrong sign, dropped the pole at 5.96 seconds and produced a different fingerprint.", "body_md": "**plimsoll runs untrusted code, such as code an AI agent wrote, inside a sandbox, and\nevery result says how strong that sandbox was. A request can demand a minimum strength;\nif the sandbox is weaker, nothing runs.**\n\nThe name comes from the *Plimsoll line*, the load limit painted on the outside\nof a ship's hull where anyone can check it. plimsoll does the same for the\n*isolation tier*: how strong the wall around a run is, stated as one of four\nlevels, weakest first: `process`, `container`, `kernel`, `vm`. The tier is a value on every\nresult, not a sentence in a datasheet. A request can set a *floor*, the weakest\ntier it will accept, and the official Go client checks the tier again when the result\ncomes back.\n\nThe same machinery lets plimsoll run an exam on code an agent wrote, not only contain\nit. A trial runner built into the simulation image runs the agent's program against\nscenarios that program cannot read: each one reaches the runner in a file deleted before\nthe program starts. Every run returns SHA-256 hashes of the request as sent and the\nresult as returned, which the official clients recompute ([how](https://github.com/plimsollmark/plimsoll/blob/main/docs/run-records.md)). A\nseparate program outside the daemon, [`plimsoll-attest`](https://github.com/plimsollmark/plimsoll/blob/main/cmd/plimsoll-attest), signs\nthe checked results, refuses a chain of calls with one missing, and replays a run. The\nexam itself (the simulated system, the scenarios, the pass mark) stays yours: plimsoll\nsupplies the boundary and the evidence.\n\nAn AI agent wrote a *controller*: a program that reads a system's state at\nevery *tick* (here, one 10 ms step of the simulation) and decides how to push it. plimsoll\nran it against a simulated *cart-pole*, a cart on a rail with a pole hinged on\ntop, which the controller keeps upright by pushing the cart left and right. The simulator\nis compiled to *WebAssembly*, a portable bytecode that runs inside a host program,\nand built into the sandbox image. The controller was the only file the caller sent, through\nthe ordinary project API, and it ran at the `container` tier.\n\nThe cart-pole is the standard teaching problem in control engineering. The runner, a\nprogram built into the image that runs the controller against the simulator, records\nthe run's *trajectory*, every state at every tick, and hashes it into a\n*fingerprint*: a SHA-256 hash, so equal fingerprints mean identical numbers. Run\nthe accepted controller twice and the two fingerprints match. The agent's first draft had\ntwo gains (multipliers in its formula) with the wrong sign: it drops the pole at 5.96\nseconds, and its fingerprint differs.\n\n**[Open the live run report ↗](https://plimsollmark.github.io/plimsoll/examples/oracle/index.html)**\nto replay both runs in the browser, read the controller the agent wrote, and see what the\npage deliberately does not claim. One execution of the example makes three sandbox runs: the accepted\ncontroller twice and the draft once. Reproduce them with\n`make docker-images && go run ./examples/oracle`.\n\n**It reports the wall.** Every result states the isolation tier the run actually got.\nEvery request can set a floor, and a request whose floor the daemon cannot meet is refused\nbefore any code runs: `ErrInsufficientIsolation` means nothing ran.\n\n```\nres, err := provider.Sandbox.RunJavaScript(ctx, sandbox.Request{\n    Code:             \"console.log(6 * 7)\",\n    MinimumIsolation: sandbox.IsolationVM, // ErrInsufficientIsolation means nothing ran\n})\n// res.Isolation reports the boundary that actually ran.\n```\n\nEvery refusal that ran nothing is marked as one, with a reason (`request`, `permission`,\n`protocol`, `unsupported`, `isolation`, `environment`, `capacity`). `sandbox.NotDispatchedReason(err)`\nreads the mark, whether the *provider* (the backend that runs the code) is in\nyour own process or behind the daemon. An error without the mark may have come after the\ncode started, so it is never safe to retry automatically. A mark counts only on an answer\nthat carries the client's request ID back, so a proxy that serves one request's answer\nfor another cannot make a call that ran read \"nothing ran\"\n([what comes back, and what it means](https://github.com/plimsollmark/plimsoll/blob/main/docs/run-results.md#did-anything-run-the-error-says-so)).\n\nThe tier is evidence the daemon collected: its configuration and its own startup checks.\nIt is not *attestation*, cryptographic proof from the hardware of what software\nis running, and no provider here offers that.\n[docs/isolation-tiers.md](https://github.com/plimsollmark/plimsoll/blob/main/docs/isolation-tiers.md) lists what each tier rests on.\n\n**It lets agent-written code call your API without ever holding your credential, your\nAPI's address, or open network access.** A *grant* gives one run permission to\ncall listed routes of one API. The code calls through a small client plimsoll puts in the\nsandbox, and the *broker*, the part of plimsoll outside the sandbox that makes\nthe real call, checks the caller and the exact route, then attaches the credential itself.\nThe credential is *minted* per run: plimsoll asks for it once per run, so it can\nbe a fresh short-lived token, and it never enters the sandbox. Code that skips the client\nand calls out by hand gets no further, because the broker, not the client, enforces the\nrules. See [docs/capability-grants.md](https://github.com/plimsollmark/plimsoll/blob/main/docs/capability-grants.md).\n\nThese agent frameworks can hand their model's code to plimsoll through a tool or code\nexecutor of their own (*Trigger.dev*, one of them, is a hosted TypeScript job\nrunner). Each row names what to install, the guide, and a page that replays recorded calls\nfrom that framework's tool to a sandbox and back, refusals included.\n\n| Framework | Package or extra | Guide | See a run | \n|---|---|---|---|\n| Vercel AI SDK | `@plimsollmark/client/ai-sdk` | [Vercel AI SDK guide (INTERNAL · documentation →)](https://github.com/plimsollmark/plimsoll/blob/main/docs/ai-sdk.md) | [Vercel AI SDK page (INTERNAL · plimsoll site →)](https://plimsollmark.github.io/plimsoll/integrations/vercel-ai-sdk/index.html) | \n| Google ADK | `plimsoll-client[adk]` | [Google ADK guide (INTERNAL · documentation →)](https://github.com/plimsollmark/plimsoll/blob/main/docs/google-adk.md) | [Google ADK page (INTERNAL · plimsoll site →)](https://plimsollmark.github.io/plimsoll/integrations/google-adk/index.html) | \n| Agno | `plimsoll-client[agno]` | [Python client guide, Agno and CrewAI tools (INTERNAL · documentation →)](https://github.com/plimsollmark/plimsoll/blob/main/clients/python/README.md#agno-and-crewai-tools) | [Agno page (INTERNAL · plimsoll site →)](https://plimsollmark.github.io/plimsoll/integrations/agno/index.html) | \n| CrewAI | `plimsoll-client[crewai]` | [Python client guide, Agno and CrewAI tools (INTERNAL · documentation →)](https://github.com/plimsollmark/plimsoll/blob/main/clients/python/README.md#agno-and-crewai-tools) | [CrewAI page (INTERNAL · plimsoll site →)](https://plimsollmark.github.io/plimsoll/integrations/crewai/index.html) | \n| [Trigger.dev](https://github.com/plimsollmark/plimsoll/blob/main/docs/trainers/glossary.html#trigger-dev) | `@plimsollmark/client/trigger` | [Trigger.dev guide (INTERNAL · documentation →)](https://github.com/plimsollmark/plimsoll/blob/main/docs/trigger-dev.md) | [Trigger.dev page (INTERNAL · plimsoll site →)](https://plimsollmark.github.io/plimsoll/integrations/trigger-dev/index.html) | \n| Mastra | `@plimsollmark/client/mastra` | [TypeScript client guide, Mastra (INTERNAL · documentation →)](https://github.com/plimsollmark/plimsoll/blob/main/clients/typescript/README.md#mastra) | [Mastra page (INTERNAL · plimsoll site →)](https://plimsollmark.github.io/plimsoll/integrations/mastra/index.html) | \n\nThe [TypeScript add-on](https://github.com/plimsollmark/plimsoll/blob/main/clients/typescript/README.md#triggerdev) gives an\n`executeCode` tool to a chat agent on Trigger.dev. On a daemon with a *session*, one sandbox kept\nfor several calls, each call is a *cell*, code run inside the\ninterpreter kept for that conversation:\nvariables and files can survive the next call. Each tool result says whether\nthey can survive, whether this call started fresh, the isolation tier it ran\nbehind, and the SHA-256 of the *run record*, a receipt for what was\nsent and returned that the client checked before returning. The caller sets a\nminimum tier, so a weaker provider refuses before running code.\n\n[See the feature and deployment guide](https://github.com/plimsollmark/plimsoll/blob/main/docs/trigger-dev.md) for the exact\nguarantees and production floor. The\n[deployable Trigger.dev starter](https://github.com/plimsollmark/plimsoll-trigger-starter)\nhas a chat agent and a task that proves two cells share one interpreter through\na real daemon, without making an AI model call.\n\nThe example needs no daemon, no docker, no credentials and no network: it uses the `wasm`\nprovider, which runs JavaScript on *QuickJS*, a small JavaScript engine\ncompiled to WebAssembly, inside your own process. Cloning and fetching Go modules do use\nthe network.\n\n```\ngit clone https://github.com/plimsollmark/plimsoll && cd plimsoll\ngo run ./examples/minimal\nprovider   wasm\nisolation  process\nexit code  0\ntimed out  false\ntruncated  stdout=false stderr=false\nduration   304ms\nstdout     {\"Engineering\":59000000,\"Sales\":20300000,\"Operations\":9800000}\n```\n\nThe `isolation` line comes from the run's result, not from the example's own text.\n**`process` means there is no operating-system wall at all: do not use it for hostile\ncode.** The same snippet runs at the `kernel` tier once *gVisor* is installed.\ngVisor is a layer between a container and your machine's kernel that handles the\ncontainer's requests to the operating system itself, so the code never talks to your\nkernel directly. `sudo ./docker/install-gvisor.sh` installs a *pinned* gVisor\nrelease (one exact version, changed only by editing this repository) and registers its\nruntime, *runsc*, with docker. Nothing else about the program changes:\n\n```\nmake docker-images   # builds the project image (plimsoll/sandbox) the docker provider checks for\nSANDBOX_PROVIDER=docker SANDBOX_DOCKER_RUNTIME=runsc go run ./examples/minimal\n```\n\nWith `SANDBOX_PROVIDER` unset, plimsoll uses the Disabled provider, which runs nothing, so\ncode execution is never on by accident.\n\n**Next:** [docs/getting-started.md](https://github.com/plimsollmark/plimsoll/blob/main/docs/getting-started.md) walks through the same pieces\non one machine: start the daemon, create a caller credential, write your own client, watch\na floor refuse a run, then move the daemon from `process` to `kernel` without changing the\nclient. It also covers embedding the Go package instead of running the daemon.\n\n```\nflowchart LR\n  A[\"Agent or MCP gateway (MCP: how an AI app offers tools to a model)\"] -->|\"Run: one payload, a protocol number, minimum_isolation\"| AU\n\n  subgraph D[\"plimsolld\"]\n    AU[\"authenticate the caller\"] --> FL[\"compare the floor with current provider evidence\"]\n    FL --> DI[\"dispatch\"]\n    BR[\"broker: holds the minted credential, matches the exact route, counts every call\"]\n  end\n\n  DI --> W[\"wasm: QuickJS on wazero (process tier)\"]\n  DI --> K[\"docker with runsc (kernel tier)\"]\n  DI --> V[\"e2b Firecracker (VM tier)\"]\n\n  W -. \"host.get / host.post\" .-> BR\n  K -. \"per-run Unix socket\" .-> BR\n  V -. \"authenticated guard\" .-> BR\n  BR ==> |\"your credential, attached host-side\"| API[\"Your API\"]\n```\n\nThe code inside the sandbox, the *guest*, never holds the credential and never\nreaches the network directly. A run with no grant has no network at all.\n\n`plimsolld` is the plimsoll server. Each one runs exactly one provider, chosen by\n`SANDBOX_PROVIDER`:\n\n| Provider | Boundary | Tier reported | Use | \n|---|---|---|---|\n| `wasm` | QuickJS on *wazero* (a WebAssembly runtime written in Go), inside`plimsolld` itself | `process` | Fast local development. An *escape* (a bug that lets code out of its sandbox) in the engine lands inside your daemon. | \n| `docker` with*runc* , docker's default runtime | a container sharing your machine's kernel | `container` | Self-hosting when a kernel bug is not one of the attacks you plan for. With a project image it keeps a session: one container for many calls, with a JavaScript interpreter (and a Python one with `plimsoll/sandbox-python` as the project image) whose variables survive between calls. | \n| `docker` with`runsc` | gVisor, once the startup checks confirm docker has `runsc` registered | `kernel` | Hostile code on your own machines. Keeps sessions too. | \n| `e2b` | a *microVM* (a small virtual 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 rather than yours; billed per run. Keeps sessions too, billed while the microVM runs; a suspend pauses it, which keeps the files but not an interpreter's variables. | \n| `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 both passed the live suite on 2026-10-04: the one Docker released before launch, no longer documented, which is the default because only it has grants and image evidence, and the REST API Docker documents, kept as a backup until it covers those (no grants, refused under`PLIMSOLL_HARDENED=1` , runs of at most 270 s). The account's network policy must be*deny-all* (no connection unless a rule allows it), and every run checks that. Grants work through the same*guard* as E2B (an address on the plimsoll server, the only place the microVM may connect to) when`SANDBOX_DOCKERCLOUD_GUARD_URL` is set. Unlike E2B, the guest holds its own run's short-lived credential for the guard, and the one network rule is set through a Docker call outside its published API. | \n| `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 gets its own sandbox with no network; plimsoll reads its settings back, refuses to run on any difference, and deletes it afterwards. Tested against a v0.1.2 gateway on 2026-09-28. Grants reach the broker through a relay inside the sandbox that plimsoll connects to from outside, so the sandbox needs no network rules. Keeps sessions too. | \n| unset | nothing runs | n/a | The default. | \n\nEach tier rests on the daemon's configuration, what the provider reports, and a real test\nrun at startup; none is attestation. [docs/isolation-tiers.md](https://github.com/plimsollmark/plimsoll/blob/main/docs/isolation-tiers.md)\nlists the evidence for each tier and shows how a request sets its floor. A session trades\nthe fresh sandbox of every call for speed and kept state: code an earlier call ran can\nchange what later calls see, and a call with API access needs a grant that allows sessions\n([docs/sessions.md](https://github.com/plimsollmark/plimsoll/blob/main/docs/sessions.md#what-a-session-gives-up)). A session is one trust\ndomain, and plimsoll ties it to the authenticated caller, not to that caller's customers:\na service that runs many customers' code through one credential must keep each customer\nin a session of their own\n([who may share a session](https://github.com/plimsollmark/plimsoll/blob/main/docs/sessions.md#who-may-share-a-session)). The docker\nprovider can also apply the shipped *seccomp* profile, a list of the only\n*system calls* (requests to the kernel, such as opening a file) its containers\nmay make ([docs/seccomp.md](https://github.com/plimsollmark/plimsoll/blob/main/docs/seccomp.md)).\n\n| Page | What it shows | \n|---|---|\n| [Simulation replay pages, all eight simulators ↗](https://plimsollmark.github.io/plimsoll/examples/envs/index.html) | The eight simulators built into the simulation image for controller runs: shower, *buck converter* (a power supply that steps a voltage down by switching it on and off), ship heading, black hole orbit, relativistic rocket, satellite clock, double slit, and cart-pole swing-up (the image also carries the models the module-run tests use). Each page runs a failing and a passing hand-written controller through the sandbox and replays both trajectories with their fingerprints. | \n| [A controller in C, compiled in the sandbox ↗](https://plimsollmark.github.io/plimsoll/examples/wasm-controller/index.html) | The same 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 own first step, so the simulator and the controller are both WebAssembly. The run report compares it with the JavaScript version tick by tick. The two languages' `cos` functions disagree in the last bit on about one input in a hundred, so in three of the four scenarios one or two force values differ, by a few representable doubles (at most 14). The fingerprint catches that difference, and the motion itself is identical to the bit. Source and caveats in[its README](https://github.com/plimsollmark/plimsoll/blob/main/examples/wasm-controller/README.md) ; reproduce with`make docker-images && go run ./examples/wasm-controller` . | \n| [A buck converter controller in C ↗](https://plimsollmark.github.io/plimsoll/examples/wasm-buck/index.html) | A power supply's *control law* (the formula its controller applies at each tick) written in C, the language converter firmware is written in. It is compiled to WebAssembly in the sandbox and run against a simulated converter, whose output it must hold at 5 V while the load changes suddenly. It calls no library function, so its trajectory equals the JavaScript version's byte for byte in all four scenarios; the page charts one of them tick by tick. Source and caveats in[its README](https://github.com/plimsollmark/plimsoll/blob/main/examples/wasm-buck/README.md) ; reproduce with`make docker-images && go run ./examples/wasm-buck` . | \n| [Same run, different sandboxes ↗](https://plimsollmark.github.io/plimsoll/examples/providers/index.html) | The cart-pole run from the top of this page, on local `runc` and gVisor, an OpenShell gateway, E2B and Docker Cloud Sandboxes: three isolation tiers, two Node versions, and one fingerprint, the same one the first report published. Reproduce the configured rows with`go run ./examples/providers` ; the cloud rows are billed. | \n| [One sandbox, five calls ↗](https://plimsollmark.github.io/plimsoll/examples/sessions/index.html) | A session on an OpenShell sandbox: a failing test, a patch, the test passing without the files being sent again, and a leftover process that is gone by the next call. Each call's run record (the daemon's statement of what was sent, what came back and where it ran) is signed and linked to the previous one. The verifier accepts the signed set, and refuses it when one call is dropped or one byte is changed. Reproduce with `go run ./examples/sessions` and a gateway. | \n| [The efficiency advisor's report ↗](https://plimsollmark.github.io/plimsoll/examples/advisor/report.html) | The advisor reads the API calls a run made and points out wasteful patterns. One measured run: the same question asked of an API as 13 calls, then as 1, and the advisor's finding that names the route that answers it in one call. | \n| [Twelve interactive lessons ↗](https://plimsollmark.github.io/plimsoll/trainers/) | How a run is executed, the providers, the API broker, and connecting an AI agent. Static pages: no network calls, no analytics, no third-party scripts. | \n\n- **Pre-1.0** , single author, no external users yet. Breaking changes land without a\ndeprecation path, on purpose.\n- **No third-party security audit has ever been performed** , and the author's own\nself-review ledgers are not published either.[SECURITY.md](https://github.com/plimsollmark/plimsoll/blob/main/SECURITY.md) says what\nexists, what it is worth, and what you can check yourself instead.\n- **CI runs the gate, in the open, and each check means what it ran.** The[audit workflow](https://github.com/plimsollmark/plimsoll/blob/main/.github/workflows/audit.yml) has three jobs.`audit` runs plain`make audit` on every push to`main` and every pull request: build, vet, race tests, lint,`buf lint` , a generated-code drift check, and`govulncheck` . After`audit` ,`clients` runs`make clients-suite` :`npm ci` in`clients/typescript` and`examples/trigger-chat` , then the Python and TypeScript Go test packages in\nrequired mode. A missing runtime, dependency or skipped Go test fails that job.`audit-docker` also follows`audit` and\nruns`make docker-suite` with the images prepared, in required mode: a missing\ndaemon, a missing image or a skipped test fails the job. A green`audit-docker` therefore means the docker suite ran under runc with the shipped seccomp profile\nand proved a read-only root, sized`noexec` writable mounts, and the broker's\nrefusals. The[gvisor workflow](https://github.com/plimsollmark/plimsoll/blob/main/.github/workflows/gvisor.yml) runs the same suite\nunder runsc, the kernel tier, from the pinned installer. The race detector runs in\nthe plain audit job and both docker-suite jobs; the client job runs without it.\nNo check exercises E2B or Docker Cloud Sandboxes: those suites drive live paid services and are\ndeliberately never wired to a runner. See[CONTRIBUTING.md](https://github.com/plimsollmark/plimsoll/blob/main/CONTRIBUTING.md) .\n\nThe [interactive lessons](https://plimsollmark.github.io/plimsoll/trainers/) are the\nfastest way in if you would rather read than clone. Start with\n**[Plain English](https://plimsollmark.github.io/plimsoll/trainers/plain-english.html)**\nfor the idea before the API, or\n**[Quick start](https://plimsollmark.github.io/plimsoll/trainers/quick-start.html)** to run\nsomething. Every term these docs use has a one-sentence definition in the\n**[glossary](https://plimsollmark.github.io/plimsoll/trainers/glossary.html)**. The lessons\nlive in [docs/trainers/](https://github.com/plimsollmark/plimsoll/blob/main/docs/trainers) and work offline: open any file from a clone in a\nbrowser. GitHub shows `.html` files as source instead of rendering them, which is why the\nlinks above point at the published copy.\n\nEach of these answers one question, end to end.\n\n| Document | Answers | \n|---|---|\n| [docs/getting-started.md](https://github.com/plimsollmark/plimsoll/blob/main/docs/getting-started.md) | How do I build it, embed it, start it as a service with authentication, and watch a floor refuse a run? | \n| [docs/example-programs.md](https://github.com/plimsollmark/plimsoll/blob/main/docs/example-programs.md) | What do the runnable examples prove, and which should I read first? | \n| [docs/isolation-tiers.md](https://github.com/plimsollmark/plimsoll/blob/main/docs/isolation-tiers.md) | What does each tier rest on, and how do I demand one per request? | \n| [docs/capability-grants.md](https://github.com/plimsollmark/plimsoll/blob/main/docs/capability-grants.md) | How does agent code call my API without ever holding my credential? | \n| [docs/run-results.md](https://github.com/plimsollmark/plimsoll/blob/main/docs/run-results.md) | What comes back, and when is a failure an error rather than a result? | \n| [docs/run-records.md](https://github.com/plimsollmark/plimsoll/blob/main/docs/run-records.md) | What does each run's record state, how do I recompute it in another language, and how do I sign, verify and replay records outside the daemon? | \n| [docs/sessions.md](https://github.com/plimsollmark/plimsoll/blob/main/docs/sessions.md) | How do I keep one sandbox for many calls, and what holds between the calls, an interpreter's variables included? | \n| [clients/python](https://github.com/plimsollmark/plimsoll/blob/main/clients/python/README.md) | How do I call plimsolld from Python? | \n| [clients/typescript](https://github.com/plimsollmark/plimsoll/blob/main/clients/typescript/README.md) | How do I call it from TypeScript, and give a Trigger.dev or Mastra agent a code tool that keeps its state? | \n| [docs/trigger-dev.md](https://github.com/plimsollmark/plimsoll/blob/main/docs/trigger-dev.md) | How do calls that reuse one interpreter, an isolation floor and checked run records appear together in a Trigger.dev tool, and how do I deploy a task? | \n| [docs/ai-sdk.md](https://github.com/plimsollmark/plimsoll/blob/main/docs/ai-sdk.md) | How do I give a Vercel AI SDK agent an `executeCode` tool, fresh per call or kept for one user's conversation? | \n| [docs/google-adk.md](https://github.com/plimsollmark/plimsoll/blob/main/docs/google-adk.md) | How do I make plimsoll the code executor of a Google ADK agent, with input files, output artifacts and a refusal below the floor? | \n| [docs/placement.md](https://github.com/plimsollmark/plimsoll/blob/main/docs/placement.md) | I run several daemons: how do I pick one per request, and when is a refusal safe to retry elsewhere? | \n| [docs/inner-loop-workflow.md](https://github.com/plimsollmark/plimsoll/blob/main/docs/inner-loop-workflow.md) | How do I iterate fast locally without shipping a weak sandbox to production? | \n| [docs/efficiency-advisor.md](https://github.com/plimsollmark/plimsoll/blob/main/docs/efficiency-advisor.md) | What does the advisor see, why can what it records never include the data the code sent or received, and how do I choose what it emits? | \n| [docs/hardened-mode.md](https://github.com/plimsollmark/plimsoll/blob/main/docs/hardened-mode.md) | How do I make the daemon refuse to start unless every production safeguard is set? | \n| [docs/dependencies.md](https://github.com/plimsollmark/plimsoll/blob/main/docs/dependencies.md) | Which dependencies must be trusted for the sandbox to hold, and how are the tools that check them pinned? | \n| [docs/limitations.md](https://github.com/plimsollmark/plimsoll/blob/main/docs/limitations.md) | What does this deliberately not do? | \n| [docs/dockercloud.md](https://github.com/plimsollmark/plimsoll/blob/main/docs/dockercloud.md) | What does the Docker Cloud Sandboxes provider need from the operator, and what does each run check? | \n| [docs/openshell.md](https://github.com/plimsollmark/plimsoll/blob/main/docs/openshell.md) | What does the OpenShell provider need from the operator, and what does each run check? | \n| [docs/releasing.md](https://github.com/plimsollmark/plimsoll/blob/main/docs/releasing.md) | Why is the module path public, why do releases start at v0.2.0, and why is there no checksum exemption? | \n| [docs/callers.md](https://github.com/plimsollmark/plimsoll/blob/main/docs/callers.md) | How do I create, rotate and revoke caller credentials? | \n| [docs/seccomp.md](https://github.com/plimsollmark/plimsoll/blob/main/docs/seccomp.md) and[docs/gvisor.md](https://github.com/plimsollmark/plimsoll/blob/main/docs/gvisor.md) | What do the seccomp filter and gVisor enforce? | \n| [docs/guest-dependencies.md](https://github.com/plimsollmark/plimsoll/blob/main/docs/guest-dependencies.md) | How do npm packages get into a sandbox that has no network? | \n| [docs/architecture/credential-minting.md](https://github.com/plimsollmark/plimsoll/blob/main/docs/architecture/credential-minting.md) | Where does a per-run credential come from, and where does it stay? | \n| [docs/seams.md](https://github.com/plimsollmark/plimsoll/blob/main/docs/seams.md) | Where are the deliberate extension points? | \n| [Glossary ↗](https://plimsollmark.github.io/plimsoll/trainers/glossary.html) | What does this word mean? One plain sentence per term. | \n| [AGENTS.md](https://github.com/plimsollmark/plimsoll/blob/main/AGENTS.md) | The architecture reference: providers, the rules no change may break, and every environment variable. |", "url": "https://wpnews.pro/news/show-hn-plimsoll-a-self-hosted-sandbox-for-code-your-ai-agent-writes", "canonical_source": "https://github.com/plimsollmark/plimsoll", "published_at": "2026-10-11 23:47:59+00:00", "updated_at": "2026-10-12 00:06:04.243426+00:00", "lang": "en", "topics": ["ai-agents", "ai-safety", "developer-tools", "ai-tools"], "entities": ["Plimsoll", "plimsoll-attest", "WebAssembly", "Go", "SHA-256"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/show-hn-plimsoll-a-self-hosted-sandbox-for-code-your-ai-agent-writes", "markdown": "https://wpnews.pro/news/show-hn-plimsoll-a-self-hosted-sandbox-for-code-your-ai-agent-writes.md", "text": "https://wpnews.pro/news/show-hn-plimsoll-a-self-hosted-sandbox-for-code-your-ai-agent-writes.txt", "jsonld": "https://wpnews.pro/news/show-hn-plimsoll-a-self-hosted-sandbox-for-code-your-ai-agent-writes.jsonld"}}