You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Today the harness runs every Pi tool call (read/write/bash/grep) inside a sandbox pod via kubectl exec — the harness dials into the pod through the kube API. That only works when the harness can reach the sandbox's API server, which rules out sandboxes behind NAT, on-prem, on a laptop, or in another cloud — and blocks the top driver: bring-your-own (untrusted 3rd-party) sandboxes (spec §1).
Target architecture
Invert connectivity: the sandbox dials out to a broker the harness also talks to, over a language-neutral, firewall-friendly protocol (one outbound TLS connection, HTTP/2 on :443), without changing the Pi loop, session backend, or leaf queue (spec §2–§3).
Sandbox (laptop / on-prem / other cloud / same cluster)
worker (Go static binary) — SandboxWorker.Attach client
│ outbound only, TLS :443, HTTP/2 (WorkerFrame ↑ / ServerFrame ↓)
▼
Kubernetes
relay (Deployment, 1 replica) — parks Attach streams; mirrors presence → Redis pool
│ in-cluster gRPC (SandboxExec.Exec/Abort)
▼
Harness (central brain: Pi loop + LLM) Redis sandbox pool
SandboxTransport.exec(...) ← the ONE seam
├─ [local] KubectlTransport (existing kubectl-exec, renamed)
└─ [remote] GrpcRelayTransport (new; calls the relay)
select-sandbox → leases pod OR remote record → returns a transport
Contract is a Protobuf IDL (sandbox/v1), not a TypeScript interface. Any gRPC-capable language can host a worker; TS is just one generated client (§4).
gRPC-native over HTTP/2 on :443, not Connect — full-duplex bidi needs HTTP/2 regardless, so Connect adds a second toolchain for no gain on the streaming core (§5).
Single-replica, presence-only relay — no matchmaking, no presence glue beyond mirroring workers into the existing Redis pool; select-sandbox matches unchanged (§6).
One Go reference worker — the honest proof the contract is genuinely language-neutral (§7).
Per-sandbox bearer token at the edge day-one; SPIFFE/mTLS upgrades into the same Attach seam later (§9).
What does NOT change
The Pi orchestration loop, run-turn, the session backend (RedisSessionBackend), the leaf queue (@sh/work-queue), and the sandbox pool/lease logic. The change slots strictly below the current ExecInPod call sites (spec §3).
Two tracks (build in parallel off the shared contract)
The worker build is separated from the backend and coded by a different contributor; per the repo's lifecycle conventions, implementation plans are local-only (docs/plans/, gitignored) and per-contributor — one for the backend, one for the worker.
Epic: SandboxTransport — language-neutral remote sandbox exec over gRPC
Tracks the work in the SandboxTransport spec (Status: Design — approved for planning) and ADR-0024 (Accepted). Landed via #78.
Origin
Today the harness runs every Pi tool call (read/write/bash/grep) inside a sandbox pod via
kubectl exec— the harness dials into the pod through the kube API. That only works when the harness can reach the sandbox's API server, which rules out sandboxes behind NAT, on-prem, on a laptop, or in another cloud — and blocks the top driver: bring-your-own (untrusted 3rd-party) sandboxes (spec §1).Target architecture
Invert connectivity: the sandbox dials out to a broker the harness also talks to, over a language-neutral, firewall-friendly protocol (one outbound TLS connection, HTTP/2 on
:443), without changing the Pi loop, session backend, or leaf queue (spec §2–§3).Key decisions (locked) — see ADR-0024
x-api-key: null→ Record<string, string>) #1).sandbox/v1), not a TypeScript interface. Any gRPC-capable language can host a worker; TS is just one generated client (§4).:443, not Connect — full-duplex bidi needs HTTP/2 regardless, so Connect adds a second toolchain for no gain on the streaming core (§5).select-sandboxmatches unchanged (§6).Attachseam later (§9).What does NOT change
The Pi orchestration loop,
run-turn, the session backend (RedisSessionBackend), the leaf queue (@sh/work-queue), and the sandbox pool/lease logic. The change slots strictly below the currentExecInPodcall sites (spec §3).Two tracks (build in parallel off the shared contract)
The worker build is separated from the backend and coded by a different contributor; per the repo's lifecycle conventions, implementation plans are local-only (
docs/plans/, gitignored) and per-contributor — one for the backend, one for the worker..proto.Phases (dependency-ordered)
sandbox/v1Protobuf contract (shared foundation; build first)SandboxTransportseam +KubectlTransportrename (pure refactor, ships first)GrpcRelayTransport+ presence mirror +select-sandbox(behindSH_REMOTE_SANDBOX; depends on ST1: sandbox/v1 Protobuf contract — language-neutral wire IDL (shared foundation) #84, ST2: backend — SandboxTransport seam + KubectlTransport rename (pure refactor) #85)Deferred / follow-up (spec §13.4, ADR-0024) — not scheduled here
Attachendpoint.Working conventions
docs/specs/2026-07-08-sandbox-transport-grpc-design.md+ registry row (docs/specs/README.md). Decision:docs/adrs/0024-sandbox-transport-remote-exec.md.docs/plans/(local-only, gitignored), named for the spec, deleted once coded.git commit -s; attribution trailerAssisted-By: Claude (Anthropic AI) <noreply@anthropic.com>(neverCo-authored-by).