# Sandbox Implementation Plan ## Overview This document outlines the implementation plan for the Sandbox module, which provides persistent Docker containers for external CLI agents like Claude Code. **Estimated Code**: ~1200 lines --- ## Phase 1: Core Interfaces & Types ✅ COMPLETED ### Goals - Define all core types and interfaces - Set up package structure ### Implemented Files ``` sandbox/ ├── manager.go # Manager implementation ├── types.go # Container, ExecOptions, ExecResult, FileInfo ├── config.go # Configuration types ├── errors.go # Custom errors ├── helpers.go # Helper functions └── ipc/ ├── manager.go # IPC Manager ├── session.go # IPC Session └── types.go # JSON-RPC types ``` ### Completed - [x] Create package structure - [x] Define types in `types.go` - `Config` struct - `Container` struct - `ExecOptions` struct - `ExecResult` struct - `FileInfo` struct - [x] Define `Manager` in `manager.go` - Container lifecycle: `GetOrCreate`, `Stop`, `Start`, `Remove`, `List`, `Cleanup` - Command execution: `Stream`, `Exec` - Filesystem: `WriteFile`, `ReadFile`, `ListDir`, `Stat`, `MkDir`, `RemoveFile`, `CopyToContainer`, `CopyFromContainer` - [x] Define IPC types in `ipc/` - `Session` struct - `Manager` struct - `AgentContext` struct - `MCPTool` struct - JSON-RPC request/response types - [x] Unit tests for type helpers --- ## Phase 2: Docker Container Management ✅ COMPLETED ### Goals - Implement container lifecycle management - Handle container creation, start, stop, remove ### Completed - [x] Initialize Docker client (`NewManager`) - [x] Implement `createContainer()` - Generate container name: `yao-sandbox-{userID}-{chatID}` - Create workspace directory on host - Configure mounts (workspace, IPC socket) - Set resource limits (memory, CPU) - Apply security options (`--cap-drop ALL`, `no-new-privileges`) - [x] Implement `GetOrCreate()` with double-check locking - [x] Implement `ensureImage()` - auto-pull missing Docker images - [x] Implement `ensureRunning()` - [x] Implement `Stop()` and `Start()` - [x] Implement `Remove()` - [x] Implement `List()` - [x] Concurrency limit (`ErrTooManyContainers`) --- ## Phase 3: Command Execution & Filesystem ✅ COMPLETED ### Goals - Execute commands inside containers - Support both streaming and blocking execution - Full filesystem operations ### Completed #### Command Execution - [x] Implement `Stream()` - returns io.ReadCloser - [x] Implement `Exec()` - blocking execution with result - [x] Handle timeout via context - [x] Handle environment variables #### Filesystem Operations - [x] `WriteFile()` - tar archive + CopyToContainer - [x] `ReadFile()` - CopyFromContainer + extract tar - [x] `ListDir()` - execute `ls -la` and parse - [x] `Stat()` - execute `stat` and parse - [x] `MkDir()` - execute `mkdir -p` - [x] `RemoveFile()` - execute `rm -rf` - [x] `CopyToContainer()` - tar + Docker API - [x] `CopyFromContainer()` - Docker API + extract --- ## Phase 4: IPC System ✅ COMPLETED ### Goals - Implement Unix socket IPC - Handle MCP JSON-RPC protocol ### Completed - [x] Implement `ipc.Manager` - `NewManager(sockDir string) *Manager` - `Create(ctx, sessionID, agentCtx, mcpTools) (*Session, error)` - `Close(sessionID) error` - `Get(sessionID) (*Session, bool)` - `CloseAll()` - [x] Implement `ipc.Session` - Create Unix socket listener - Set socket permissions (0660) - Handle connection lifecycle - [x] Implement message loop - Accept connection - Read NDJSON lines - Parse JSON-RPC requests - Dispatch to handlers - Write JSON-RPC responses - [x] Implement MCP handlers - `initialize` → handshake response - `tools/list` → return authorized tools - `tools/call` → execute Yao process - `resources/list` → list resources - `resources/read` → read resource - [x] JSON-RPC error handling with proper error codes --- ## Phase 5: yao-bridge & Docker Image ✅ COMPLETED ### Goals - Build yao-bridge binary - Create Docker images ### Implemented Files ``` sandbox/ ├── bridge/ │ └── main.go # yao-bridge source ├── docker/ │ ├── base/ │ │ └── Dockerfile.base # Common base image │ ├── claude/ │ │ ├── Dockerfile # Default: Claude + Node + Python │ │ └── Dockerfile.full # + Go │ └── build.sh # Build script ``` ### Completed - [x] Implement yao-bridge (`sandbox/bridge/main.go`) - stdin/stdout ↔ Unix socket bridge - Signal handling for graceful shutdown - [x] Create Dockerfiles - `base/Dockerfile.base` - Ubuntu 22.04, git, curl, yao-bridge - `claude/Dockerfile` - + Node.js 20, Python 3.11 - `claude/Dockerfile.full` - + Go 1.23 - [x] Create build script (`sandbox/docker/build.sh`) - Builds yao-bridge as static binary - Builds all image variants --- ## Phase 6: ClaudeExecutor Integration 🔲 PENDING ### Goals - Integrate Sandbox with ClaudeExecutor - End-to-end execution flow ### Tasks - [ ] Add Sandbox Manager to ClaudeExecutor ```go type ClaudeExecutor struct { Assistant *Assistant SandboxManager *sandbox.Manager IPCManager *ipc.Manager } ``` - [ ] Implement `Stream()` method 1. Get or create container 2. Create IPC session 3. Generate .mcp.json 4. Setup skills 5. Build Claude CLI args 6. Execute in container 7. Parse output - [ ] Implement `writeMCPConfig()` - Generate MCP config with yao-bridge - Include external MCP servers - Write to workspace - [ ] Implement `setupSkills()` - Symlink skills directory to .claude/skills/ - [ ] Implement output parsing - Parse NDJSON stream - Extract text content - Extract file changes from tool_use - Handle result message - [ ] Handle session mapping - Map Yao ChatID to Claude SessionID - Support `--resume` for continuation ### Deliverables - [ ] ClaudeExecutor with Sandbox - [ ] MCP config generation - [ ] Skills setup - [ ] Output parsing - [ ] Integration tests --- ## Phase 7: Cleanup & Testing ✅ COMPLETED ### Goals - Implement cleanup strategies - Comprehensive testing - Documentation ### Completed - [x] Implement cleanup loop (every 5 minutes) - [x] Implement `Cleanup(ctx) error` - [x] Unit tests (no Docker required) - `config_test.go` - Config parsing, validation, env vars, edge cases - `helpers_test.go` - parseMemory, mapToSlice, parseLS, parseStat, tar operations - `ipc/jsonrpc_test.go` - JSON-RPC parsing, serialization - [x] Integration tests (Docker required) - `manager_test.go` - Container lifecycle, exec, filesystem operations - `ipc/manager_test.go` - IPC session management - `ipc/session_test.go` - Session message handling, MCP protocol - [x] README.md with usage examples ### Test Files | File | Tests | Description | | --------------------- | ----- | ------------------------------------ | | `config_test.go` | 10 | Config parsing, env vars, edge cases | | `helpers_test.go` | 6 | Utility functions | | `ipc/jsonrpc_test.go` | 8 | JSON-RPC types | | `manager_test.go` | 18 | Container lifecycle (Docker) | | `ipc/manager_test.go` | 10 | IPC sessions | | `ipc/session_test.go` | 11 | Session handlers (Docker optional) | ### Unit Tests (No Docker) ``` ✅ TestDefaultConfig ✅ TestConfigInit ✅ TestConfigInitWithEnv ✅ TestConfigInitWithWorkspaceEnv ✅ TestConfigInitWithPresetValues ✅ TestConfigInitInvalidEnvValues ✅ TestConfigInitNegativeValues ✅ TestConfigInitZeroMax ✅ TestContainerName ✅ TestConfigEnvPriority ✅ TestParseMemory ✅ TestMapToSlice ✅ TestParseLS ✅ TestParseStat ✅ TestParseLSMode ✅ TestCreateAndExtractTar ✅ TestJSONRPCRequestParsing ✅ TestJSONRPCResponseSerialization ✅ TestJSONRPCErrorResponse ✅ TestToolCallParams ✅ TestToolResult ✅ TestToolsListResult ✅ TestInitializeResult ``` ### Integration Tests (Docker Required) ``` ✅ TestNewManager ✅ TestNewManagerWithNilConfig ✅ TestGetOrCreate ✅ TestContainerStartStopRemove ✅ TestExec ✅ TestExecWithEnv ✅ TestExecWithTimeout ✅ TestFileOperations ✅ TestCopyOperations ✅ TestListContainers ✅ TestConcurrencyLimit ✅ TestConcurrentAccess ✅ TestContainerNotFound ✅ TestCleanup ✅ TestGetAccessors ✅ TestEnsureImageAutoPull ✅ TestManagerWithYaoApp (requires YAO_TEST_APPLICATION) ``` ### IPC Tests ``` ✅ TestNewManager ✅ TestCreateSession ✅ TestGetSession ✅ TestCloseSession ✅ TestCloseNonExistentSession ✅ TestCloseAllSessions ✅ TestSessionReplace ✅ TestConcurrentSessionAccess ✅ TestSessionConnection ✅ TestToolsList ✅ TestMethodNotFound ✅ TestParseError ✅ TestInitializedNotification ✅ TestSessionHandleInitialize ✅ TestSessionHandleResourcesList ✅ TestSessionHandleResourcesRead ✅ TestSessionHandleToolsCallInvalidParams ✅ TestSessionHandleToolsCallUnauthorized ✅ TestSessionToolsCallWithYaoApp (requires YAO_TEST_APPLICATION) ✅ TestSessionMultipleRequests ✅ TestSessionClose ✅ TestSessionEmptyLines ``` ### Running Tests ```bash # Unit tests only (no Docker needed) go test -v ./sandbox/... -run "Test(Default|Config|Parse|Map|LS|Stat|Tar|JSONRPC|Tool)" # Integration tests (Docker required) source env.local.sh go test -v ./sandbox/... # With Yao application (full integration) export YAO_TEST_APPLICATION=/path/to/yao-dev-app source env.local.sh go test -v ./sandbox/... # Using Makefile (pulls test images automatically) make unit-test-sandbox ``` --- ## Phase Summary | Phase | Description | Status | | ----- | ------------------------------ | ------------ | | 1 | Core Interfaces & Types | ✅ COMPLETED | | 2 | Docker Container Management | ✅ COMPLETED | | 3 | Command Execution & Filesystem | ✅ COMPLETED | | 4 | IPC System | ✅ COMPLETED | | 5 | yao-bridge & Docker Image | ✅ COMPLETED | | 6 | ClaudeExecutor Integration | 🔲 PENDING | | 7 | Cleanup & Testing | ✅ COMPLETED | --- ## Implementation Summary ### Files Created | File | Lines | Description | | --------------------------------------- | ----- | ------------------------------ | | `sandbox/errors.go` | 22 | Error types | | `sandbox/types.go` | 52 | Core type definitions | | `sandbox/config.go` | 90 | Configuration with env vars | | `sandbox/helpers.go` | 305 | Helper functions | | `sandbox/manager.go` | 541 | Main manager implementation | | `sandbox/ipc/types.go` | 139 | IPC type definitions | | `sandbox/ipc/manager.go` | 101 | IPC session manager | | `sandbox/ipc/session.go` | 252 | Session handling | | `sandbox/bridge/main.go` | 59 | yao-bridge binary | | `sandbox/docker/base/Dockerfile.base` | 34 | Base Docker image (multi-arch) | | `sandbox/docker/claude/Dockerfile` | 43 | Claude image | | `sandbox/docker/claude/Dockerfile.full` | 34 | Full Claude image (multi-arch) | | `sandbox/docker/build.sh` | 145 | Build script (multi-arch) | | `sandbox/config_test.go` | 175 | Config tests | | `sandbox/helpers_test.go` | 190 | Helper tests | | `sandbox/manager_test.go` | 520 | Manager integration tests | | `sandbox/ipc/jsonrpc_test.go` | 236 | JSON-RPC tests | | `sandbox/ipc/manager_test.go` | 330 | IPC manager tests | | `sandbox/ipc/session_test.go` | 420 | IPC session tests | | `sandbox/README.md` | 152 | Documentation | **Total**: ~3800 lines --- ## Dependencies ### External - Docker Engine (or Docker Desktop) - Claude CLI (placeholder in Dockerfile) ### Go Packages - `github.com/docker/docker/client` - Docker SDK - `github.com/docker/docker/api/types/container` - Docker types ### Internal - `github.com/yaoapp/gou/process` - Yao process execution --- ## Next Steps 1. **Phase 6: ClaudeExecutor Integration** - Implement in `yao/agent/assistant/executor/claude/` - Wire up sandbox with assistant execution flow 2. **CI/CD for Docker Images** ✅ COMPLETED - Images already built and pushed to Docker Hub: - `yaoapp/sandbox-base:latest` (amd64, arm64) - `yaoapp/sandbox-claude:latest` (amd64, arm64) - `yaoapp/sandbox-claude-full:latest` (amd64, arm64) - Set up automated builds on version tags 3. **CI/CD for Tests** ✅ COMPLETED - Sandbox tests run separately from core tests - Makefile: `make unit-test-sandbox` - GitHub Actions workflows updated: - `unit-test.yml`: Added `sandbox-test` job - `pr-test.yml`: Added `SandboxTest` job - Test images pre-pulled before tests: - `alpine:latest` - `yaoapp/sandbox-base:latest` - `yaoapp/sandbox-claude:latest` --- ## Success Criteria ### Functional ✅ (Sandbox Core) - [x] Can create/start/stop/remove containers - [x] Can execute commands in containers - [x] IPC communication works bidirectionally - [ ] Claude CLI can call Yao MCP tools (requires Phase 6) - [x] Data persists across container restarts ### Performance (To be validated) - [ ] Container creation < 5 seconds - [ ] Command execution latency < 100ms overhead - [ ] IPC round-trip < 10ms ### Reliability - [x] Handles connection drops gracefully - [x] Cleans up resources on errors - [x] No resource leaks (cleanup loop) ### Security - [x] User isolation enforced (one container per user+chat) - [x] Resource limits enforced (memory, CPU) - [x] No privilege escalation (`--cap-drop ALL`, `no-new-privileges`)