Imported from evertonstz/netclodinho (
AGENTS.md). Install upstream withnpx skills add evertonstz/netclodinho. Copyright stays with the author.
Repository Guidelines
Project Overview
Netclode is a self-hosted coding agent that lets you run AI-powered development sessions inside isolated microVMs, controlled from a native iOS/macOS app or CLI. This fork uses Docker Compose + BoxLite microVMs instead of the upstream Kubernetes+Kata deployment.
The monorepo spans three languages: Go (control plane, bots, CLI), TypeScript (agent SDK runner), and SwiftUI (iOS/macOS client). All inter-service communication uses Protocol Buffers + Connect RPC over HTTP/2 bidirectional streams.
Architecture & Data Flow
Client (iOS/CLI) ←→ Control Plane (Go) ←→ Agent (Node.js inside BoxLite VM)
↕ Redis
GitHub Bot (Go)
- Clients connect to the control plane via
ClientService.Connect— a single bidirectional Connect RPC stream carrying all requests (create/list/open sessions, send prompts, terminal I/O, snapshots, git operations). - Control plane manages session lifecycle, orchestrates BoxLite/K8s sandboxes, and forwards prompts to agents via
AgentService.Connect(agents connect to the control plane, not the other way around). - Agent is a Node.js process inside a BoxLite microVM. It wraps multiple SDKs (Claude, OpenCode, Copilot, Codex) behind a unified interface and streams prompt events back to the control plane.
- Redis (with Streams) persists all session state and event history. Clients can disconnect/reconnect without losing events.
- Secret Proxy sits outside the sandbox and injects real API keys into requests via MITM, so secrets never enter the VM.
- GitHub Bot listens for webhooks, creates sessions on @mentions, and auto-reviews dependency update PRs.
Session Lifecycle
- Client sends
CreateSessionRequest(SDK type, repos, model, resources). - Control plane allocates a BoxLite VM (or picks from warm pool in K8s mode). In Docker mode, warm pool is disabled.
- Agent boots, connects to control plane, receives
SessionConfig(API keys as placeholders, repo list, SDK config). - Agent clones repos, initializes SDK session, streams events. Secret proxy transparently substitutes real keys for placeholders on outbound LLM API calls.
- Client streams messages/events in real time. Terminal access available via attached PTY.
- On pause, VM is stopped but persistent storage remains. Resume remounts and continues.
Key Directories
| Path | Purpose |
|---|---|
services/control-plane/ |
Go — session orchestration, BoxLite/K8s runtime, Connect API server |
services/control-plane/internal/config/ |
Configuration from env vars |
services/control-plane/internal/api/ |
HTTP/Connect server, client + agent handlers |
services/control-plane/internal/session/ |
Session manager (the core — 127KB), state, agent handlers |
services/control-plane/internal/storage/ |
Redis-backed session persistence |
services/control-plane/internal/boxlite/ |
BoxLite runtime (Docker mode) |
services/control-plane/internal/k8s/ |
Kubernetes runtime (upstream mode) |
services/control-plane/internal/github/ |
GitHub App client for repo access |
services/agent/src/ |
TypeScript — SDK runner entry point and bidirectional transport |
services/agent/src/sdk/ |
SDK abstraction layer: types, factory, runtime, adapters |
services/agent/src/sdk/claude/ |
Claude Code SDK adapter |
services/agent/src/sdk/opencode/ |
OpenCode adapter |
services/agent/src/sdk/copilot/ |
GitHub Copilot adapter |
services/agent/src/sdk/codex/ |
OpenAI Codex adapter |
services/agent/src/services/ |
Session mapping, terminal PTY, prompt title generation |
services/agent/auth-proxy/ |
Go — K8s SA token authentication proxy (upstream only) |
services/secret-proxy/ |
Go — MITM proxy injecting API keys outside sandbox |
services/github-bot/ |
Go — webhook handler, @mention bot, dependency review |
clients/cli/ |
Go — debug CLI (Cobra-based) |
clients/ios/ |
SwiftUI — native iOS/macOS app |
proto/ |
Protobuf definitions, buf config |
third_party/boxlite-go-sdk/ |
Vendored BoxLite Go SDK (CGo) |
docs/ |
Architecture, deployment, operations docs |
infra/docker/ |
Docker deployment docs |
Development Commands
Proto
make proto # Generate code from proto files (Go, TS, Swift)
make proto-lint # Lint proto files
make proto-breaking # Check for breaking changes against main
Generated code destinations:
- Go:
services/control-plane/gen/ - TypeScript:
services/agent/gen/ - Swift:
clients/ios/Netclode/Generated/
Agent (TypeScript)
cd services/agent
npm run dev # Watch mode with tsx
npm run build # Bundle with esbuild → dist/agent.js
npm run typecheck # tsc --noEmit
npm test # vitest run
npm run test:watch # vitest watch mode
Control Plane / Go Services
All Go services share the go.work workspace at root (Go 1.25.5). Build/test from service directories:
cd services/control-plane
go build ./cmd/control-plane
go test ./...
# Other services:
cd services/secret-proxy && go test ./...
cd services/github-bot && go test ./...
CLI
cd clients/cli
make build # Build binary
make install # Install to $GOPATH/bin
make build-all # Cross-compile for 4 platforms
Run the CLI directly after build:
./netclode sessions list
NETCLODE_URL=https://your-server ./netclode sessions list
iOS/macOS
make test-ios # Run iOS unit tests (XCTest on macOS)
make run-macos # Build and run macOS Catalyst app
make run-ios # Build and run iOS simulator
make run-device # Build and run on connected iPhone
Docker Compose
docker compose up -d # Start control-plane + redis + tailscale
docker compose --profile github-bot up -d # Include GitHub bot
Code Conventions & Common Patterns
Go
- Module path:
github.com/angristan/netclode/services/<name>for services,github.com/angristan/netclode/clients/clifor CLI. - Go workspace (
go.work) at root ties togetherclients/cli,services/control-plane,services/github-bot,services/secret-proxy, andservices/agent/auth-proxy. internal/packages enforce visibility — all implementation lives underinternal/, onlycmd/andgen/are importable externally.- Configuration: Each service has a
config.Load()that reads env vars with sensible defaults. Pattern:getEnv("KEY", "default"),getEnvInt("KEY", 0),getEnvBool("KEY", false). - Logging:
log/slogstructured JSON logging with Datadog trace correlation. Useslog.Info("message", "key", value). - Metrics: DogStatsD via
github.com/DataDog/datadog-go/v5. Init withmetrics.Init(), close withmetrics.Close(). - Error handling: Standard
fmt.Errorf("context: %w", err)wrapping. Functions returnerroras last value. - Dependency injection: Interfaces defined at use-site (e.g.,
storage.Storage,k8s.Runtime), concrete implementations injected inmain(). - Context propagation:
context.Contextas first parameter, used for cancellation and tracing. - Graceful shutdown: SIGTERM/SIGINT → cancel context → drain connections → close resources.
TypeScript
- Module system: ESM (
"type": "module"). Use.jsextensions in imports. - TypeScript config: Strict mode, ES2022 target,
verbatimModuleSyntax,isolatedModules. Base config intsconfig.base.json. - SDK adapter pattern: Each AI provider (Claude, OpenCode, Copilot, Codex) implements a common
NetclodePromptBackendinterface. Factory (createNetclodeAgent) composes backends with runtime services. - Async generators: Prompt backends return
AsyncGenerator<PromptEvent>— events are yielded as they stream from the LLM. - Session state: In-memory
Map<string, string>persisted to disk (/agent/.session-mapping.json) to survive agent restarts. - Node modules:
node-ptyfor terminal PTY,undicifor HTTP,@bufbuild/protobuf+@connectrpc/connectfor RPC. - Bundle:
esbuildbundles to a single ESM file for the runtime image.
Protobuf
- Package:
netclode.v1 - Services:
ClientService(client ↔ control plane),AgentService(agent ↔ control plane) - Code generation:
buf generatewith remote plugins for Go (protobuf, gRPC, Connect), TypeScript (Protobuf-ES v2), Swift (SwiftProtobuf + Connect-Swift). - Go import path:
github.com/angristan/netclode/services/control-plane/gen/netclode/v1 - Streaming: Both services use bidirectional streams —
rpc Connect(stream Message) returns (stream Message).
Swift/iOS
- Architecture: MVVM with
@Observablestore classes (SwiftUI Observation framework). - Key services:
ConnectService(Connect RPC client — 61KB),MessageRouter(message dispatch),AppStateCoordinator,SpeechService. - Stores:
ChatStore,EventStore,SessionStore,GitHubStore,TerminalStore,SnapshotStore. - Code generation: Proto → Swift types in
Netclode/Generated/. - Testing:
XCTestwith@testable import Netclode.
Important Files
| File | Role |
|---|---|
docker-compose.yml |
Single-server deployment with control-plane, redis, tailscale |
go.work |
Go workspace tying all modules together |
Makefile |
Root build orchestration (proto, rollout, iOS) |
proto/buf.gen.yaml |
Code generation plugins and output paths |
proto/buf.yaml |
Buf config (lint: STANDARD, breaking: FILE) |
services/control-plane/cmd/control-plane/main.go |
Control plane entry point |
services/control-plane/internal/config/config.go |
All env-var configuration |
services/control-plane/internal/session/manager.go |
Core session lifecycle (127KB) |
services/control-plane/internal/api/connect_agent.go |
Agent bidirectional stream handler |
services/control-plane/internal/api/connect_client.go |
Client bidirectional stream handler |
services/control-plane/internal/storage/redis.go |
Redis Streams persistence |
services/control-plane/internal/boxlite/runtime.go |
BoxLite VM lifecycle |
services/agent/src/index.ts |
Agent entry point |
services/agent/src/connect-client.ts |
Agent ↔ control plane transport (23KB) |
services/agent/src/sdk/types.ts |
Core agent types and interfaces |
services/agent/src/sdk/factory.ts |
Agent runtime composition |
services/agent/src/sdk/runtime.ts |
ComposedNetclodeAgent — orchestrates backends |
services/agent/entrypoint.sh |
VM boot script (git config, env, startup log shipping) |
services/agent/Dockerfile |
Multi-stage image (Node + CLI tools + agent bundle) |
services/control-plane/Dockerfile |
BoxLite runtime + Go binary |
clients/cli/cmd/root.go |
CLI root command (Cobra) |
clients/ios/Netclode/App/NetclodeApp.swift |
iOS app entry |
tsconfig.base.json |
Shared TypeScript strict config |
Runtime/Tooling Preferences
- Go: 1.25.5 (workspace mode). Build with
CGO_ENABLED=1for BoxLite CGo bindings. - Node.js: 24 (managed via mise in Docker image). Production bundle uses
esbuild. - TypeScript: 5.7.x, strict mode,
moduleResolution: "bundler". - Package manager: npm (no lockfile preference —
package-lock.jsonpresent). - Container base:
debian:trixie-slim(agent),debian:bookworm-slim(control plane),golang:1.25-bookworm(build stage). - Orchestration: Docker Compose (this fork). Upstream uses k3s/Kubernetes.
- Observability: Datadog (tracing, profiling, metrics via DogStatsD). Slog JSON logging with trace correlation.
- Proxy: The secret-proxy is a standalone Go service using
elazarl/goproxyfor MITM certificate injection. - iOS: SwiftUI targeting iOS 26+, macOS Catalyst. Signing via
DEVELOPMENT_TEAMenv var. - Protobuf:
bufCLI (install viabrew install bufbuild/buf/buf). All generation through remote plugins.
Testing & QA
Go Tests
- Standard library
testingpackage. - Test files:
*_test.goalongside source. - Mock pattern: Interfaces enable test doubles (e.g.,
storage.Storagemocked withminiredisin control plane tests). - Table-driven tests: Used extensively, especially in config and bot detection tests.
- Run:
go test ./...from service directory orgo test ./internal/...for specific packages.
TypeScript Tests
- Framework: Vitest (
vitest runorvitestfor watch). - Test files:
*.test.tsalongside source. - Key test areas: SDK factory composition (
factory.test.ts), secret materialization (secret-materialization.test.ts), git operations (git.test.ts), connect client (connect-client.test.ts), terminal PTY (terminal.test.ts), prompt generation (prompt.test.ts). - Run:
npm testornpm run test:watchinservices/agent/.
Swift Tests
- Framework: XCTest (
NetclodeTeststarget). - Key test files:
EventStoreTests.swift(19KB),MessageRouterTests.swift(7KB). - Run:
make test-iosorxcodebuild test -scheme NetclodeTests -destination 'platform=macOS' -quiet.
Integration Tests
third_party/boxlite-go-sdk/network_secrets_integration_test.go— BoxLite network + secrets integration.- Use build tags or env guards for tests requiring live infrastructure.
- CLI
shell_test.go— integration-style tests for the shell command.