Four horses standing at a wooden fence, drawn over a field of binary digits.

Sandboxed sessions for coding agents

The model is local.The blast radius should be too.

A local or uncensored model does what the prompt says. The agent around it starts with your whole disk and your whole network.

paddock is the fence. The agent runs in a sandbox the OS enforces, and you pick the tools, the domains and the one writable directory before the first token.

$ curl -fsSL https://paddock.desquaredp.com/install.sh | sh
§ 01

One command, one screen, then it runs

paddock run starts a sandboxed session in the terminal you are already in. No daemon, no tab to open. It opens on the profile you launched last: L, then enter.

The confirm screen is not optional and does not shrink for a repeat launch. The thing granting the permissions says out loud what it granted: groups expanded, paths as they will be.

chooserpaddock run
$ paddock run

paddock   local-model                                          in ~/dev/agentlab

    1 Open       New sandbox
    2 Profile    local-model
  > 3 Backend    msb (a microVM, full isolation)
    4 Agent      Local inference (aider --no-auto-commits)
    5 Tools      none
    6 Network    only Local inference's own API                       (1 domain)
    7 Files      ~/dev/agentlab
    8 Skills     none for this agent
    9 Advanced   name, save as profile, keep running, MCP

  What runs the sandbox. srt wraps the process in the OS policy and starts at
  once. msb boots a microVM, which is slower to start and shares less with this
  machine.

    [ Launch ]        [ Cancel ]

enter edit   ^v move   1-9 jump   L launch   s save   esc cancel   ? keys
L opens the last screen before anything happens
Launch this sandbox?

  session      local-model-4a7f
  backend      msb (a microVM, full isolation)
  agent        Local inference (aider --no-auto-commits)
  profile      local-model, unchanged
  can write    everything in the guest, which goes when it does, and
               ~/dev/agentlab, mounted at /work
  can read     the guest's own filesystem. Your disk is not in there at all, so
               none of it has to be denied
  can reach    1 domain: localhost:11434
  can run      whatever the python:3.12-slim image ships, plus local itself
  can see      its own Local inference login. No other agent's keys. No skills.

  > [ Launch ]      [ ← Back to the form ]      [ Cancel ]

enter choose   <> move   ^v scroll   s save   esc back (keeps your answers)

paddock: pulling the python:3.12-slim image
paddock: installing local in the guest
paddock: the first start takes about 40 seconds
paddock: session local-model-4a7f
Static mock at the 80 column width the screens are drawn to. Every paddock line is the literal string the code composes, from tui.py and screen.py. Standalone paddock run and the port-scoped local rule are on develop at v0.2.0, not yet on main. Full reference.

In herdr the same chooser opens on prefix+s and adds sandboxed tabs, named sessions and re-attach. Outside it, one terminal is one session.

§ 02

Every grant is decided before the first token

No runtime prompts to argue past. Writes and domains are settled in the kernel sandbox, and unticked skills and MCP servers are not in the session's config directory to find.

can run

Only the binaries you tick, plus what the agent cannot start without.

Soft. An absolute path still reaches any host binary. On msb there is none to reach.

can reach

A domain allowlist, by group or by name. Everything else refused by the OS.

Hard, one hole. On msb DNS is all or nothing: every name resolves, then the connection is refused. Names leak.

can write

Denied by default. A scratch directory, or the one host directory you named.

No opt-out. No allow-everything row. A sandbox with no filesystem fence is a local tab, one field up.

can read

Your disk, minus the credential directories: ~/.ssh, ~/.aws, ~/.gnupg, ~/.config/gh. On a microVM none of it is in there.

can see

The agent's own login, nothing else. What it writes back are copies, so no agent gets another's keys, your SSH keys or your cloud credentials.

Two backends enforce it, and they differ in kind, not spelling. The chooser refuses an answer a backend cannot express.

srt policy sandbox

Start
Instant. Milliseconds, which is what a keybinding needs.
Boundary
Seatbelt on macOS, bubblewrap on Linux, through Anthropic's sandbox-runtime.
Shares
This machine's kernel and filesystem, minus what the policy refuses.

msb microVM

Start
175ms from create to a usable VM. A first agent session is about 40s: the agent installs in the guest.
Boundary
A libkrun microVM with its own kernel and filesystem. No server, no daemon, no privileged step.
Shares
Only the directory you mounted.
§ 03

The agent in the VM, the model on your GPU, one port between them

On msb, naming a local port writes one network rule: reach this machine, on that port. Not another port, not the internet, not even DNS.

Host

Your GPU, your server
  • ollama, LM Studio,
  • llama.cpp, vLLM
  • 127.0.0.1:11434
  • never published

Guest

The agent, in a microVM
  • a kernel of its own
  • /work, and nothing else
  • OPENAI_BASE_URL set
  • gone when the session ends

Every runtime is an HTTP server on a port, so all of them are the same shape. Name it once and the guest boots knowing where it is.

# the agent file that opens the port, once
"api_domains": ["localhost:11434"]

# what the guest boots with, in every tab
OPENAI_BASE_URL=http://host.microsandbox.internal:11434/v1
OLLAMA_HOST=http://host.microsandbox.internal:11434

Leave your server on loopback. The gateway connects onward from the host, so 127.0.0.1 is where the request arrives. Serving to the network would hand the model to the whole Wi-Fi, and paddock does not need it.

Another port on the same host, from inside the guest:
wget: can't connect to remote host (172.16.2.213): Connection refused msb 0.6.13, with a 27B model answering from inside the guest, /Users absent and example.com not resolving.

On srt the grant is not scoped: Seatbelt's loopback rule takes no port, so it opens every service on this machine. The confirm screen says so. For the narrow version, use msb.

§ 04

What it is not, yet

A tool that hands out permissions has to be honest about its own.

  • Three enforcement layers, two of them hard. The kernel sandbox and the synthesized config directory sit outside the agent. The generated agent config is the agent enforcing a rule on itself: friction, not a boundary.
  • The tool list is a soft allowlist. It shapes what the agent finds. It does not stop an absolute path. Hard per-binary blocking is on the roadmap.
  • Collection is lazy. Nothing watches for a closed tab, so a session is collected at the next paddock command. Until then its microVM is up and holding memory. paddock gc forces it.

Read the trust model