Two Go services that together carry the agent traffic per architecture
docs §1-§3 (NOT tmux).
pty-server (products/sandbox/pty-server, :7681):
POST /sessions spawn child in fresh PTY
GET /sessions[/{id}] list / describe
WS /sessions/{id}/attach bidi raw byte stream, replays 256 KiB ring
WS /sessions/{id}/cards JSON card stream (mobile alt surface)
POST /sessions/{id}/resize rows/cols -> SIGWINCH
POST /sessions/{id}/signal INT|QUIT|TERM|HUP to process group
DELETE /sessions/{id} graceful SIGTERM, 5s, SIGKILL
GET /healthz liveness
Built on creack/pty + gorilla/websocket; fan-out drops only for slow
consumers; graceful shutdown closes every session on SIGTERM.
openova-sandbox-mcp (products/sandbox/mcp-server, stdio JSON-RPC):
initialize / tools/list / tools/call / ping wired end-to-end.
Tool catalogue stubs across the required namespaces:
gitea.* (8), k8s.read.* (4), sandbox.db.* (4), sandbox.auth.* (3),
sandbox.session.* (2 — whoami/info for agent self-discovery).
Every stub returns {"status":"not_implemented","tool":...,"wave":2}
so the agent can list and dispatch the full surface before backends
land in Wave 3+.
Both binaries: distroless/static-debian12:nonroot containers, scratch
deps, no chart bumps, no UI changes, READ-ONLY clusters preserved.
Verified locally:
go build ./... clean for both modules.
go vet ./... clean for both modules.
MCP smoke: stdin {initialize} -> framed response with
serverInfo.name = openova-sandbox-mcp; tools/list returns 21 tools
sorted by name.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>