← Ravi and agents

What a Ravi Sandbox Needs

A sandbox starts empty and can't reach anything. Four problems stand in the way of real work.

TL;DR

Ravi can already run a task in a disposable E2B sandbox (#586), but the sandbox starts empty and can't reach anything. Four problems stand between that and sandboxes doing real client work. Each has options that trade risk, cost and UX differently.

  • Context: start with a read-only snapshot at boot, then a live read-only connection, then writes.
  • Reaching the control plane: route through the Console relay, so neither a Mac nor a VPS opens a port.
  • Shared company data: shared records in Console Bases, runtime data in one Ravi daemon per org.
  • Secrets in the sandbox: short-lived, scoped keys now; whether the model key stays out entirely is still undecided.

This is the follow-up to Where Does the Agent Live?, which explains why the agents are moving into sandboxes in the first place.

Problems we are solving

Sandboxes isolate agents well, but four problems block using them for real work.

#ProblemWhy it matters
1Sandboxes have no contextEach sandbox starts with a clean database. A task like "fix the bug Maria reported yesterday" needs the conversation, memory files and task history.
2The sandbox can't reach RaviE2B runs in the cloud. A Mac behind a home router has no public address, and a VPS would need a public endpoint.
3Companies share dataSeveral people and agents read and write the same records (Bases, Pages). A sandbox working for a company must see them, and only them.
4Secrets inside the sandboxAnything inside can be read by the agent, including one tricked by a prompt-injected document. Today the real Claude token and GITHUB_TOKEN go in.

Options and trade-offs

Each problem has its own options, and none of them is free. For each one the table shows what it costs in risk, effort and day-to-day use, and the line under it says where the trade-off leans.

1. Context in the sandbox

OptionRiskCostUX
Ephemeral (today)Lowest: only the repo is insideNone, shipped in #586Agent knows nothing; the user re-explains every time
Snapshot at boot: read-only copy of AGENTS.md, memory files, skillsLow: a copy of memory sits in the sandbox for the run; nothing writes backSmall: copy files at startAgent has background, but it can go stale mid-run and what it learns is lost
Connected: live ravi … calls through the gateway with a scoped keyMedium: needs a network path (problem 2); a hijacked agent can use its key for up to 1 hour, within its scopeMedium: key issuing and revocation, plus the network pathBest: looks things up and records results like a local agent; stalls if the control plane is down
Long-lived sandbox per client (pause and resume)Medium: client state and longer-lived secrets stay in E2BOngoing E2B cost per client; a lifecycle to manageFast warm start; its state can drift from the control plane

Where it leans: snapshot first, then connected read-only, then writes per command group. Connected mode looks like this:

The sandbox reaches Ravi only through the gateway, never the databaseHeavy line: the one link we must build and secure ourselvesControl plane (org VPS or your Mac)Ravi daemon + gatewaychecks key, permissions, auditravi.dbnever leaves this machineE2B sandbox (microVM)Agent (Claude or Codex)runs ravi … via RAVI_GATEWAY_URLSnapshot, read-onlyAGENTS.md, memory files, skillsRavi ConsoleBases and Pages; owns its own authHTTPS + scopedtoken, 1 hourcopied at bootscoped Console tokenown Console login
connected mode · control plane, sandbox, Console

The database never leaves the control plane; the sandbox gets a read-only snapshot at boot and a short-lived key for live calls.

2. How the sandbox reaches the control plane

The sandbox always opens the connection; the answer comes back on it. The control plane never connects into the sandbox.

OptionRiskCostUX
Console relay: the daemon keeps an outgoing connection to Console (an outbound relay); Console forwards sandbox calls down itLow to medium: no open port anywhere; Console sees requests in plain text; the relay's job grows from read-only Pages apps to CLI callsMedium: extend the relay from read-only Pages apps to CLI calls; Console must carry the loadNothing to install on a Mac or VPS; one path for both; if Console is down, connected sandboxes stall
Cloudflare Tunnel, set up by a new ravi gateway exposeLow: no open port; Cloudflare's firewall and rate limitsMedium: a second integration to maintainOne command, but the customer needs a Cloudflare account and a domain
Public port 443 behind a reverse proxy that forwards only /api/v1Medium: a public endpoint to patch; the daemon's port also serves webhooks and the tool bridge, so the proxy must filterLow on a VPS; impossible on a MacCustomer or ops sets up the proxy, certificate and DNS
Tailscale inside the sandboxLow: private network, nothing publicMedium: another vendor; an auth key inside the sandbox; slower bootInvisible once set up
Fixed-IP egress proxy plus IP allowlistMedium: an IP is not an identity; if the proxy is down the sandbox loses internetHigh: run a proxy; E2B has no static IPs and its own proxy option is in private betaNo change for users

Where it leans: Console relay. Offer Cloudflare Tunnel to enterprises that won't route through Console. Whatever the path, the daemon still checks every key and permission.

3. Shared company data

There are two kinds of state: shared records (Bases, Pages, projects) and runtime state (sessions, transcripts, permissions, keys, memory). Pages are the frontend; Bases hold the data they show.

OptionRiskCostUX
Split: shared records in Console Bases; runtime in one Ravi daemon per org (its own ravi.db)Low: Console already owns authorization for Bases; runtime stays behind the gatewayLow: mostly exists todayThe whole org sees the same Bases and Pages; conversations stay per org
One shared Postgres for all runtime stateMedium: a multi-tenant database where one bug can cross orgsHigh: migrate off SQLiteNeeded only if one org runs several daemons
Everything in ConsoleMedium: Console sits in every agent turn's pathHighest: rewrite runtime storageOne place for all data
Sandbox opens the database directlyHigh: skips permissions and auditLowRejected

Where it leans: the split. A sandbox gets two scoped tokens, one for the org's gateway and one for Console, and never a database file.

4. Secrets inside the sandbox

Assume anything inside the sandbox can be stolen, so each secret must be worth little when it is.

OptionRiskCostUX
Real keys as env vars (today)High: the agent can read the Claude token and GITHUB_TOKEN; the GitHub token also sits in E2B's logsNoneSimplest
Scoped, short-lived keys: a child rctx_ key, a fine-grained GitHub token, a downscoped Console tokenLow to medium: a leak is limited to one scope and about 1 hourMedium: issue and revoke per sandboxInvisible to users
Model broker: the real model key never enters; the agent calls a forwarder with a dummy keyLowest for the model keyMedium: Ravi already has a broker (src/runtime/model-broker.ts, local forwarder with leases), but it would need a remote mode reachable from the sandboxInvisible; if the broker is down, agents stop

Where it leans: scoped keys now. Whether the model key stays out entirely, behind a remote broker, is still undecided.

Background: how it works today

Everything that matters happens on one machine, so nothing on the agent path crosses a network.

On your Mac (personal install)

  1. PM2 keeps the Ravi daemon running. The daemon owns ~/.ravi/ravi.db (SQLite), talks to a local NATS server, and runs channels (Slack, WhatsApp via Omni).
  2. When a message arrives, the daemon starts a Codex or Claude process for that session. It gives the process a key in the env var RAVI_CONTEXT_KEY (rctx_…).
  3. When that agent runs ravi …, the CLI does not touch the database. It sends the command to the daemon through ~/.ravi/cli-gateway.sock with the key attached.
  4. The daemon looks up the key, finds which agent and session it belongs to, checks permissions, runs the command and writes an audit event.
  5. If the socket cannot be reached, the CLI refuses with GATEWAY_REQUIRED instead of running the command itself (#584). Before #584 an agent could fall back to local mode and act as the operator.

In an E2B sandbox (ravi sandbox run, #586)

  1. Your Mac asks E2B (over HTTPS) to boot a microVM from the ravi-runner template: Bun, nats-server and a built Ravi from dev.
  2. Inside it: clone the repo, start a fresh daemon with a fresh ravi.db, create a worker agent, run the task.
  3. Your Mac copies back TASK.md, task.json, the git patch and logs, then kills the sandbox. Everything inside is deleted.

So the sandbox is a complete, private, throwaway Ravi. Its agent can only see the repo it cloned.

Glossary

TermWhat it means here
DaemonThe long-running Ravi process (under PM2). It owns the database and runs agents.
Unix socketA special file that lets two processes on the same machine talk. Data goes through the kernel, never the network. cli-gateway.sock is one.
Mode 0600File permissions: owner can read and write, nobody else can do anything. On a socket it means only your user can connect.
GatewayRavi's API for running CLI commands remotely: POST /api/v1/<group>/<command>. Served on the socket and on 127.0.0.1:7777.
rctx_* key18 random bytes. Identifies one agent session and its permissions. Normal agents get a new one each turn, valid for 1 hour.
TLSEncryption for data crossing a network (the "S" in HTTPS). Not needed when both ends are on the same machine.
Control planeThe part that owns state and decides: daemon, database, NATS, channels.
Execution planeWhere agents actually run code: local processes today, E2B sandboxes in the proposal.
E2B / envdE2B runs each sandbox as a Firecracker microVM (its own kernel). envd is E2B's agent inside it that receives commands and files.
Ravi ConsoleThe cloud side of Ravi (Pages, Bases, projects). Owns its own authorization and OAuth scopes.

Evidence

Related work (filipexyz/ravi)

  • #586: ravi sandbox run and ravi sandbox template build
  • #584: agents can't run ravi commands without the gateway
  • #594: outbound relay from a local install to Console (ticket, WSS, JWKS)

Code (upstream dev)

  • src/sandbox/e2b.ts: sandbox template and run flow
  • src/cli/host-cli-gateway.ts: the Unix socket server; src/cli/remote-gateway.ts: the CLI side, including RAVI_GATEWAY_URL
  • src/runtime/context-registry.ts, src/runtime/runtime-request-context.ts: key generation, 1-hour per-turn keys, revocation
  • src/bases/client.ts: Bases as a Console client

E2B docs