v0.2 - Real Workspaces: - Project-specific claudebox StatefulSets (pantheon, aeries) - Init containers for git clone via SSH - Deploy key secrets template - Project ConfigMaps for CLAUDE.md v0.3 - Git Integration: - Dockerfile with rdev-bot git identity - openssh-client for SSH operations - Image version bump to v0.3.0 v0.4 - API Server: - Go REST API with chi router - Endpoints: /projects, /claude, /shell, /git, /events - SSE streaming for real-time output - OpenAPI docs via Scalar at /docs - Kubernetes RBAC for pod exec - Executor and project registry packages Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
13 KiB
rdev Development Plan
Remote Developer - Claude Code on k3s with API control.
Vision
Run Claude Code in isolated Kubernetes pods, controlled via a REST API with SSE streaming. External clients (Discord bots, Slack bots, CLI tools) connect to the API - keeping the core infrastructure separate from integration concerns.
Architecture
External Clients rdev (k3s namespace)
┌─────────────────┐ ┌─────────────────────────────────────┐
│ Discord Bot │──┐ │ │
│ (orchard9/ │ │ │ ┌─────────────────────────────┐ │
│ rdev-discord) │ │ │ │ rdev-api (Go) │ │
└─────────────────┘ │ │ │ │ │
│ HTTP/SSE │ │ POST /projects/:id/claude │ │
┌─────────────────┐ │ ─────────────▶ │ │ POST /projects/:id/shell │ │
│ Slack Bot │──┤ │ │ POST /projects/:id/git │ │
│ (future) │ │ │ │ GET /projects/:id/events │ │
└─────────────────┘ │ │ │ GET /projects │ │
│ │ │ GET /health │ │
┌─────────────────┐ │ │ └──────────────┬──────────────┘ │
│ CLI Tool │──┘ │ │ │
│ (future) │ │ │ kubectl exec │
└─────────────────┘ │ ▼ │
│ ┌──────────────────────────────┐ │
│ │ claudebox pods │ │
│ │ │ │
│ │ ┌────────────────────────┐ │ │
│ │ │ claudebox-pantheon │ │ │
│ │ │ /workspace: pantheon │ │ │
│ │ │ Claude Code CLI │ │ │
│ │ └────────────────────────┘ │ │
│ │ │ │
│ │ ┌────────────────────────┐ │ │
│ │ │ claudebox-aeries │ │ │
│ │ │ /workspace: aeries │ │ │
│ │ │ Claude Code CLI │ │ │
│ │ └────────────────────────┘ │ │
│ └──────────────────────────────┘ │
└─────────────────────────────────────┘
Versions
v0.1 - Base Case ✅
Status: Complete (2026-01-24)
Single claudebox pod running Claude Code CLI.
Delivered:
- claudebox Docker image (
ghcr.io/orchard9/rdev-claudebox:v0.1.0) - Kubernetes manifests (StatefulSet, PVCs, Service)
- Manual interaction via
kubectl exec - Claude authentication persists in PVC
Verify:
export KUBECONFIG=~/.kube/orchard9-k3sf.yaml
kubectl exec -it -n rdev claudebox-0 -- claude "say hello"
v0.2 - Real Workspaces
Status: Ready (pending deploy key setup)
Mount actual project repos (pantheon, aeries) into dedicated claudebox pods.
Deliverables:
claudebox-pantheonStatefulSet with pantheon repoclaudebox-aeriesStatefulSet with aeries repo- Init container that clones repo on first start
- Git SSH deploy keys as Kubernetes secrets (template)
- Project-specific CLAUDE.md mounted via ConfigMap
Manual Steps Required:
- Generate deploy keys:
./scripts/generate-deploy-key.sh <project> - Add public keys to GitHub repo settings
- Update
secrets.yamlwith base64-encoded private keys - Deploy:
kubectl apply -k deployments/k8s/base
Architecture:
# Each project gets its own StatefulSet
claudebox-pantheon:
initContainer:
- clone github.com/orchard9/pantheon if /workspace empty
- else: git fetch origin
volumes:
- workspace-pantheon (PVC, 20Gi)
- claude-config-pantheon (PVC, 1Gi)
- ssh-keys (Secret)
- project-config (ConfigMap with CLAUDE.md)
Init Container Logic:
if [ ! -d /workspace/.git ]; then
git clone git@github.com:orchard9/pantheon.git /workspace
else
cd /workspace && git fetch origin
fi
Deploy Keys:
- Generate per-repo deploy key:
ssh-keygen -t ed25519 -f pantheon-deploy-key - Add public key to GitHub repo Settings → Deploy Keys (read/write)
- Store private key in K8s secret
Verify:
kubectl exec -n rdev claudebox-pantheon-0 -- ls /workspace
# Should show pantheon repo files
kubectl exec -n rdev claudebox-pantheon-0 -- git -C /workspace status
# Should show git status
v0.3 - Git Integration
Status: Ready (requires image rebuild)
Pods can commit and push changes back to GitHub.
Deliverables:
- SSH keys mounted at
/root/.ssh/(done in v0.2) - Git config (user.name, user.email) set in container
- known_hosts includes github.com (done in v0.2)
- Image updated to v0.3.0 with git identity
Deploy Steps:
- Build image:
./scripts/build-push.sh v0.3.0 - Apply manifests:
kubectl apply -k deployments/k8s/base - Restart pods:
kubectl rollout restart statefulset -n rdev --all
Git Config (via ConfigMap or Dockerfile):
git config --global user.name "rdev-bot"
git config --global user.email "rdev@orchard9.ai"
SSH Setup:
volumes:
- name: ssh-keys
secret:
secretName: github-deploy-keys
defaultMode: 0600
items:
- key: pantheon-deploy-key
path: id_ed25519
- key: known_hosts
path: known_hosts
Verify:
kubectl exec -n rdev claudebox-pantheon-0 -- bash -c "
cd /workspace &&
echo 'test' >> test.txt &&
git add test.txt &&
git commit -m 'test commit from rdev' &&
git push origin HEAD
"
v0.4 - API Server
Status: Ready (requires image build)
Go API server for controlling claudebox pods.
Deliverables:
rdev-apiGo service- REST endpoints for claude, shell, git commands
- SSE endpoint for streaming output
- Kubernetes RBAC for pod exec
- Project registry (which pods exist)
Deploy Steps:
- Build images:
./scripts/build-push.sh v0.4.0 - Apply manifests:
kubectl apply -k deployments/k8s/base - Test:
kubectl port-forward -n rdev svc/rdev-api 8080:8080 - Visit:
http://localhost:8080/docs
API Endpoints:
| Method | Path | Description |
|---|---|---|
| GET | /health |
Health check |
| GET | /projects |
List available projects |
| GET | /projects/:id |
Get project details |
| POST | /projects/:id/claude |
Run claude command |
| POST | /projects/:id/shell |
Run shell command |
| POST | /projects/:id/git |
Run git command |
| GET | /projects/:id/events |
SSE stream for output |
Request/Response:
# Start a claude command
POST /projects/pantheon/claude
Content-Type: application/json
{
"prompt": "fix the bug in auth handler",
"stream_id": "abc123" # For SSE correlation
}
# Response
{
"id": "cmd-xyz",
"status": "running",
"stream_url": "/projects/pantheon/events?stream_id=abc123"
}
# Stream output via SSE
GET /projects/pantheon/events?stream_id=abc123
event: output
data: {"line": "Reading auth handler...", "timestamp": "..."}
event: output
data: {"line": "Found issue on line 142", "timestamp": "..."}
event: complete
data: {"exit_code": 0, "duration_ms": 4532}
Architecture:
cmd/rdev-api/
├── main.go
internal/
├── api/
│ ├── handlers.go # HTTP handlers
│ ├── sse.go # SSE streaming
│ └── middleware.go # Auth, logging
├── executor/
│ ├── kubectl.go # kubectl exec wrapper
│ └── stream.go # Output streaming
├── projects/
│ └── registry.go # Project configuration
└── config/
└── config.go
RBAC:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: rdev-api
namespace: rdev
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list"]
- apiGroups: [""]
resources: ["pods/exec"]
verbs: ["create"]
Verify:
curl http://rdev-api.rdev.svc/health
# {"status": "ok"}
curl -X POST http://rdev-api.rdev.svc/projects/pantheon/claude \
-H "Content-Type: application/json" \
-d '{"prompt": "what files are in this project?"}'
v0.5 - Streaming & Events
Status: Planned
Real-time output streaming via SSE.
Deliverables:
- SSE endpoint with proper event formatting
- Output buffering and chunking
- Connection management (heartbeats, reconnection)
- Event types: output, error, complete, heartbeat
SSE Event Types:
event: heartbeat
data: {"timestamp": "2026-01-24T20:00:00Z"}
event: output
data: {"line": "Analyzing code...", "stream": "stdout"}
event: error
data: {"line": "Permission denied", "stream": "stderr"}
event: complete
data: {"exit_code": 0, "duration_ms": 1234}
Client Example:
const events = new EventSource('/projects/pantheon/events?stream_id=abc123');
events.addEventListener('output', (e) => {
const data = JSON.parse(e.data);
console.log(data.line);
});
events.addEventListener('complete', (e) => {
const data = JSON.parse(e.data);
console.log(`Done in ${data.duration_ms}ms`);
events.close();
});
v0.6 - Production Ready
Status: Planned
Hardening for production use.
Deliverables:
- Authentication (API keys or OAuth)
- Rate limiting
- Project-level permissions
- Audit logging
- Health monitoring and alerts
- Auto-commit checkpoints
- Metrics (Prometheus)
Auth Options:
- API Keys: Simple, stored in K8s secret, passed via header
- OAuth/OIDC: Integrate with existing auth (Discord OAuth?)
- Service Accounts: For bot-to-API communication
Audit Log:
{
"timestamp": "2026-01-24T20:00:00Z",
"project": "pantheon",
"action": "claude",
"prompt": "fix the auth bug",
"user": "discord:123456",
"duration_ms": 4532,
"exit_code": 0
}
File Structure
rdev/
├── CLAUDE.md # Project instructions
├── PLAN.md # This file
├── README.md # Quick start
├── Dockerfile # claudebox image
├── cmd/
│ └── rdev-api/
│ └── main.go # API server entry (v0.4+)
├── internal/ # Go packages (v0.4+)
│ ├── api/
│ ├── executor/
│ └── projects/
├── deployments/
│ └── k8s/
│ └── base/
│ ├── kustomization.yaml
│ ├── namespace.yaml
│ ├── claudebox.yaml # Generic claudebox (v0.1)
│ ├── claudebox-pantheon.yaml # Project-specific (v0.2+)
│ ├── claudebox-aeries.yaml # Project-specific (v0.2+)
│ ├── rdev-api.yaml # API server (v0.4+)
│ ├── pvc.yaml
│ ├── secrets.yaml # Deploy keys (v0.3+)
│ └── rbac.yaml # API permissions (v0.4+)
├── scripts/
│ ├── build-push.sh
│ ├── deploy.sh
│ └── verify.sh
├── docs/
│ └── reference.md # Original reference + k3s notes
└── history/
├── v0.1.0.md # Release notes
├── v0.2.0.md
└── ...
Timeline
| Version | Scope | Dependencies |
|---|---|---|
| v0.1 ✅ | Base case | None |
| v0.2 | Real workspaces | Deploy keys created |
| v0.3 | Git integration | v0.2 |
| v0.4 | API server | v0.3 |
| v0.5 | SSE streaming | v0.4 |
| v0.6 | Production ready | v0.5 |
Open Questions
- Project config: Where do we define which projects exist? ConfigMap? Code?
- Ingress: Expose API externally or keep internal? If external, auth is critical.
- Multi-tenancy: One rdev instance per org, or shared with project isolation?
- Claude context: Should API support conversation IDs for context continuity?