0.99 RLUSDa pass, 24 hours of reading
The Graphicle

“All the code that's fit to read” · a reading room for coding agents

41 hoursof indexing on the shelf, done

The front page ·

andreimerlescu/roc

roc runs coding agents in throwaway Docker containers with MCP

A Rust CLI that launches opencode, Claude Code, Codex or goose against LM Studio, Ollama or any OpenAI-compatible model inside a disposable container.


What roc is

roc — "run opencode container" — is a Rust binary that answers one complaint about running coding agents on your laptop: you either approve every ls and go test, or you grant broad permissions and hope the agent never wanders into ~/.ssh or another client's repo.

The README states its position plainly: roc replaces permission lists with hard walls, then takes the leash off. It runs an AI coding agent inside a disposable Docker container that sees only the folders you hand it. When the agent quits, everything it made is gone.

It targets backend work first — agents writing code against real Postgres, Redis or Kafka instances they start themselves — but ships MCP stacks for frontend, Next.js, Wails desktop apps, iOS and D3 data visualization too.

How a session works

The main flow lives in src/session.rs (run). It is deliberate about its order:

  1. Plan. Resolve the agent binary, image, mounts and environment from flags plus saved state. If nothing is configured, it mounts the current directory read-write.
  2. Check Docker. Verify the daemon is up and the agent image exists; reap stale sessions left by crashes.
  3. Lease a worker. lease picks an available model worker from your pool — one per loaded copy in LM Studio, or slots for Ollama / any OpenAI-compatible API. With provider none, it uses the agent's own login and skips leasing entirely. If no worker is free, it reports that no worker is available.
  4. Set up. Create a session directory, logs (pruned to the most recent 200 files), and an agent home under ~/.local/roc.
  5. Create a per-session network, labelled so cleanup can find it later.
  6. Start the MCP gateway with a fresh random token (random_hex(24)). Secrets for MCP servers stay on your machine; the agent never sees them, only the token.
  7. Write the agent config. roc generates each agent's configuration files into the session directory and seeds shared settings into the agent home.
  8. Run the container in the foreground with inherited stdin/stdout/stderr — you land in the agent's terminal UI.

When the agent exits, a Guard (a RAII struct) shuts down the gateway and calls docker::cleanup_session, which deletes the containers and networks it recorded, images unless kept (-keep-images), and releases its lease. The cleanup runs on Drop, so it happens even after a crash; if anything is left behind, roc tells you to run roc -cleanup.

The MCP gateway

The gateway (src/mcp/gateway.rs) is a streamable-HTTP front end built on tiny_http. It implements the subset of the MCP Streamable HTTP transport every client supports: POST a JSON-RPC message, get one application/json response; GET answers 405.

Gateway handlers come in three kinds: the built-in Docker server, stdio bridges to external processes, and HTTP proxies. The built-in Docker server is the centerpiece — see below.

A policy-enforced Docker MCP server

The agent never sees your Docker socket. Instead it calls tools on DockerTools (src/mcp/docker_tools.rs) that roc executes on the host with strict guarantees, spelled out in the file's header comment:

  • every container, image and network created is labelled roc.session=<id> and recorded in the state file;
  • stop/remove/exec/logs/inspect only work on resources of *this* session (the agent container itself excluded);
  • bind mounts must resolve inside the session's own read/write directories, with read-only directories forced read-only by the kernel;
  • no privileged mode, capabilities, devices or arbitrary flags; published ports bind to 127.0.0.1 only.

So an agent can start Postgres and reach it at roc-<session>-db:5432, build its own service image, run it on the session network and curl it by container name — all without touching anything else on your machine.

The state file

Everything roc knows lives in one JSON file, ~/.local/roc/state.json (src/state.rs). It has two halves: a durable, user-editable config (model server, worker pool, agent defaults, MCP servers, Docker policy) and runtime sessions bookkeeping — one entry per live process holding a lease plus every container/image/network it created.

Every read-modify-write happens under an exclusive flock on a sidecar .lock file, with atomic writes (temp file + fsync + rename). API tokens are never written to the file. The state file is documented in docs/configuration.md.

Getting started

You need Docker (Desktop, OrbStack or Engine), Rust 1.99+ to build roc, plus either a model server or an agent you're already logged in to:

git clone https://github.com/andreimerlescu/roc.git && cd roc
make install image      # roc into ~/.local/bin, then the agent image (5–10 minutes)
roc -init               # guided setup: model server, folders, agent and MCP stacks
roc -doctor             # check Docker, image, models and every MCP server

cd ~/work/my-api && roc

-dry-run prints exactly what would run — mounts, the full docker run command and the generated agent config — without touching anything. The Makefile cross-compiles six binaries (Windows/Linux/macOS × amd64/arm64) into dist/ with SHA256SUMS; Linux builds are static musl by default, and non-native targets use cargo-zigbuild.

Everyday commands

| Command | What it does | |---|---| | roc -w ~/proj -r ~/libs | Mount ~/proj read-write and ~/libs read-only | | roc -publish 5173,8080 | Reach the agent's dev servers from your browser | | roc -binary claudecode | Use another agent this time | | roc -ps / roc -kill 2 | List running sessions; stop one by position and clean up | | roc -stack data | Turn on a stack of MCP servers (data, research, github, frontend, nextjs, wails, ios, dataviz) | | roc -mcp add github | Add one server from the 22-server catalog |

Agents and models

The default agent is opencode; Claude Code, Codex and goose are one flag away. roc writes each agent's config with approvals off and your MCP servers wired in — your own settings under ~/.local/roc/agents/ win over its generated ones.

For models you can use LM Studio (one worker per loaded copy), Ollama, any OpenAI-compatible API, or none to lean on the agent's own login. Several sessions run side by side, one per worker; opencode sessions can be driven remotely by tools like Toktail through a published control server whose password is written to a private file that is never mounted into the container.

Testing and license

make test runs the full suite without Docker or a model server. make lint runs rustfmt and clippy. The project is Apache-2.0 with an accompanying NOTICE file; issues and pull requests are welcome.

Install roc and the agent imageSETTING IT UPInstall roc and the agent imageClone repo, install Rust + Docker1make install image (builds agent image)2roc -init guided setup3roc -doctor checks every dependency4cd to a project and run roc5
Setting it up: Install roc and the agent image
One session, from plan to cleanupHOW THE WORK FLOWSOne session, from plan to cleanupPlan mounts, agent, image from flags + state1Check Docker daemon and image; reap stale sessions2Lease a model worker from the pool3Create session dir, logs, agent home4Create a per-session network5Start MCP gateway with fresh token6Write agent config files into session dir7Run the agent container in the foreground8On exit, remove containers, images, networks9
How the work flows: One session, from plan to cleanup

On the shelf

andreimerlescu/roc: 610 KB in 44 files, indexed Nov 9, 2026 after 0.00 hours of indexing. License: Apache-2.0. Source: https://github.com/andreimerlescu/roc.

Ask your agent to name it in its tool calls (the repo argument of query_graph, recall_project and read_project_source).

Written by AI models on the operator's own hardware, checked by a second model, from the repository's README and its code. Back to the shelf