rdev/PLAN.md
jordan 0960b17eb2 feat: Implement v0.2-v0.4 (workspaces, git, API)
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>
2026-01-24 21:07:00 -07:00

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-pantheon StatefulSet with pantheon repo
  • claudebox-aeries StatefulSet 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:

  1. Generate deploy keys: ./scripts/generate-deploy-key.sh <project>
  2. Add public keys to GitHub repo settings
  3. Update secrets.yaml with base64-encoded private keys
  4. 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:

  1. Build image: ./scripts/build-push.sh v0.3.0
  2. Apply manifests: kubectl apply -k deployments/k8s/base
  3. 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-api Go 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:

  1. Build images: ./scripts/build-push.sh v0.4.0
  2. Apply manifests: kubectl apply -k deployments/k8s/base
  3. Test: kubectl port-forward -n rdev svc/rdev-api 8080:8080
  4. 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:

  1. API Keys: Simple, stored in K8s secret, passed via header
  2. OAuth/OIDC: Integrate with existing auth (Discord OAuth?)
  3. 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

  1. Project config: Where do we define which projects exist? ConfigMap? Code?
  2. Ingress: Expose API externally or keep internal? If external, auth is critical.
  3. Multi-tenancy: One rdev instance per org, or shared with project isolation?
  4. Claude context: Should API support conversation IDs for context continuity?