Sandbox Port Exposure Design

On-demand port forwarding from host to a running sandbox's HTTP and nREPL ports.

Problem

Sandboxes run isolated in Docker with internal ports not exposed to the host. To browse the ERP web UI or connect an editor to nREPL, we need a way to forward host ports into a specific sandbox.

Solution

Use host-side socat processes to forward TCP connections from host ports to the target sandbox container's IP.

Host                          Docker Network (orcha-sandbox-foo_default)
┌─────────────────┐           ┌──────────────────────────────────────┐
│                 │           │                                      │
│  browser        │           │   claude container (172.20.0.4)      │
│    │            │           │     └─ ERP listening on :8888        │
│    ▼            │           │                                      │
│  localhost:8888 │           │   postgres (172.20.0.2)              │
│    │            │           │   localstack (172.20.0.3)            │
│    ▼            │           │                                      │
│  socat ─────────────────────────▶ 172.20.0.4:8888                  │
│  (background)   │           │                                      │
└─────────────────┘           └──────────────────────────────────────┘

CLI Interface

bb sandbox:expose <name>    # Forward host ports to sandbox
bb sandbox:unexpose         # Stop forwarding
bb sandbox:exposed          # Show which sandbox is currently exposed

Port Mappings

Host Port Container Port Service
7777 7777 HTTP
8888 8888 ERP
9999 9999 HTTP
7888 7888 nREPL

State Storage

.worktrees/
└── .sandbox-expose/
    ├── current           # Contains feature name (e.g., "booking-ocr")
    └── pids              # Space-separated socat PIDs

Behavior

bb sandbox:expose foo

  1. Check sandbox running: docker ps --filter name=orcha-sandbox-foo-claude -q
  2. If not running: error "sandbox 'foo' is not running"
  3. If something already exposed: kill existing socat processes (auto-switch)
  4. Get container IP via docker inspect
  5. Spawn 4 socat processes (backgrounded)
  6. Write feature name to .worktrees/.sandbox-expose/current
  7. Write PIDs to .worktrees/.sandbox-expose/pids

bb sandbox:unexpose

  1. Read PIDs from file, kill each
  2. Delete state files

bb sandbox:exposed

  1. If current file exists and PIDs are still running: print feature name
  2. Else: "No sandbox exposed"

Requirements

Notes