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.
| # | Problem | Why it matters |
|---|---|---|
| 1 | Sandboxes have no context | Each sandbox starts with a clean database. A task like "fix the bug Maria reported yesterday" needs the conversation, memory files and task history. |
| 2 | The sandbox can't reach Ravi | E2B runs in the cloud. A Mac behind a home router has no public address, and a VPS would need a public endpoint. |
| 3 | Companies share data | Several people and agents read and write the same records (Bases, Pages). A sandbox working for a company must see them, and only them. |
| 4 | Secrets inside the sandbox | Anything 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
| Option | Risk | Cost | UX |
|---|---|---|---|
| Ephemeral (today) | Lowest: only the repo is inside | None, shipped in #586 | Agent knows nothing; the user re-explains every time |
| Snapshot at boot: read-only copy of AGENTS.md, memory files, skills | Low: a copy of memory sits in the sandbox for the run; nothing writes back | Small: copy files at start | Agent 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 key | Medium: needs a network path (problem 2); a hijacked agent can use its key for up to 1 hour, within its scope | Medium: key issuing and revocation, plus the network path | Best: 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 E2B | Ongoing E2B cost per client; a lifecycle to manage | Fast 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 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.
| Option | Risk | Cost | UX |
|---|---|---|---|
| Console relay: the daemon keeps an outgoing connection to Console (an outbound relay); Console forwards sandbox calls down it | Low to medium: no open port anywhere; Console sees requests in plain text; the relay's job grows from read-only Pages apps to CLI calls | Medium: extend the relay from read-only Pages apps to CLI calls; Console must carry the load | Nothing 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 expose | Low: no open port; Cloudflare's firewall and rate limits | Medium: a second integration to maintain | One command, but the customer needs a Cloudflare account and a domain |
Public port 443 behind a reverse proxy that forwards only /api/v1 | Medium: a public endpoint to patch; the daemon's port also serves webhooks and the tool bridge, so the proxy must filter | Low on a VPS; impossible on a Mac | Customer or ops sets up the proxy, certificate and DNS |
| Tailscale inside the sandbox | Low: private network, nothing public | Medium: another vendor; an auth key inside the sandbox; slower boot | Invisible once set up |
| Fixed-IP egress proxy plus IP allowlist | Medium: an IP is not an identity; if the proxy is down the sandbox loses internet | High: run a proxy; E2B has no static IPs and its own proxy option is in private beta | No 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.
| Option | Risk | Cost | UX |
|---|---|---|---|
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 gateway | Low: mostly exists today | The whole org sees the same Bases and Pages; conversations stay per org |
| One shared Postgres for all runtime state | Medium: a multi-tenant database where one bug can cross orgs | High: migrate off SQLite | Needed only if one org runs several daemons |
| Everything in Console | Medium: Console sits in every agent turn's path | Highest: rewrite runtime storage | One place for all data |
| Sandbox opens the database directly | High: skips permissions and audit | Low | Rejected |
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.
| Option | Risk | Cost | UX |
|---|---|---|---|
| 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 logs | None | Simplest |
Scoped, short-lived keys: a child rctx_ key, a fine-grained GitHub token, a downscoped Console token | Low to medium: a leak is limited to one scope and about 1 hour | Medium: issue and revoke per sandbox | Invisible to users |
| Model broker: the real model key never enters; the agent calls a forwarder with a dummy key | Lowest for the model key | Medium: Ravi already has a broker (src/runtime/model-broker.ts, local forwarder with leases), but it would need a remote mode reachable from the sandbox | Invisible; 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)
- 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). - 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_…). - When that agent runs
ravi …, the CLI does not touch the database. It sends the command to the daemon through~/.ravi/cli-gateway.sockwith the key attached. - The daemon looks up the key, finds which agent and session it belongs to, checks permissions, runs the command and writes an audit event.
- If the socket cannot be reached, the CLI refuses with
GATEWAY_REQUIREDinstead 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)
- Your Mac asks E2B (over HTTPS) to boot a microVM from the
ravi-runnertemplate: Bun, nats-server and a built Ravi fromdev. - Inside it: clone the repo, start a fresh daemon with a fresh
ravi.db, create aworkeragent, run the task. - 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
| Term | What it means here |
|---|---|
| Daemon | The long-running Ravi process (under PM2). It owns the database and runs agents. |
| Unix socket | A 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 0600 | File permissions: owner can read and write, nobody else can do anything. On a socket it means only your user can connect. |
| Gateway | Ravi's API for running CLI commands remotely: POST /api/v1/<group>/<command>. Served on the socket and on 127.0.0.1:7777. |
rctx_* key | 18 random bytes. Identifies one agent session and its permissions. Normal agents get a new one each turn, valid for 1 hour. |
| TLS | Encryption for data crossing a network (the "S" in HTTPS). Not needed when both ends are on the same machine. |
| Control plane | The part that owns state and decides: daemon, database, NATS, channels. |
| Execution plane | Where agents actually run code: local processes today, E2B sandboxes in the proposal. |
| E2B / envd | E2B runs each sandbox as a Firecracker microVM (its own kernel). envd is E2B's agent inside it that receives commands and files. |
| Ravi Console | The cloud side of Ravi (Pages, Bases, projects). Owns its own authorization and OAuth scopes. |
Evidence
Related work (filipexyz/ravi)
- #586:
ravi sandbox runandravi sandbox template build - #584: agents can't run
ravicommands 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 flowsrc/cli/host-cli-gateway.ts: the Unix socket server;src/cli/remote-gateway.ts: the CLI side, includingRAVI_GATEWAY_URLsrc/runtime/context-registry.ts,src/runtime/runtime-request-context.ts: key generation, 1-hour per-turn keys, revocationsrc/bases/client.ts: Bases as a Console client
E2B docs
- Restrict public access
- Create sandbox API
- Custom sandbox templates
- Egress IP ranges: no static IPs on any plan
- Bring your own proxy: SOCKS5 egress, private beta