PLAN.md covers: - v0.1: Base case (complete) - v0.2: Real workspaces with init container clone - v0.3: Git integration with deploy keys - v0.4: Go REST API for controlling claudebox pods - v0.5: SSE streaming for real-time output - v0.6: Production hardening (auth, rate limits, audit) Architecture: External clients (Discord, Slack, CLI) connect to rdev-api which kubectl exec's into claudebox pods. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
405 lines
13 KiB
Markdown
405 lines
13 KiB
Markdown
# 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**:
|
|
```bash
|
|
export KUBECONFIG=~/.kube/orchard9-k3sf.yaml
|
|
kubectl exec -it -n rdev claudebox-0 -- claude "say hello"
|
|
```
|
|
|
|
---
|
|
|
|
### v0.2 - Real Workspaces
|
|
**Status**: Planned
|
|
|
|
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
|
|
- [ ] Project-specific CLAUDE.md mounted via ConfigMap
|
|
|
|
**Architecture**:
|
|
```yaml
|
|
# 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**:
|
|
```bash
|
|
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**:
|
|
```bash
|
|
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**: Planned
|
|
|
|
Pods can commit and push changes back to GitHub.
|
|
|
|
**Deliverables**:
|
|
- [ ] SSH keys mounted at `/root/.ssh/`
|
|
- [ ] Git config (user.name, user.email) set in container
|
|
- [ ] known_hosts includes github.com
|
|
- [ ] Test push from inside pod
|
|
|
|
**Git Config** (via ConfigMap or Dockerfile):
|
|
```bash
|
|
git config --global user.name "rdev-bot"
|
|
git config --global user.email "rdev@orchard9.ai"
|
|
```
|
|
|
|
**SSH Setup**:
|
|
```yaml
|
|
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**:
|
|
```bash
|
|
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**: Planned
|
|
|
|
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)
|
|
|
|
**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**:
|
|
|
|
```bash
|
|
# 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"
|
|
}
|
|
```
|
|
|
|
```bash
|
|
# 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**:
|
|
```yaml
|
|
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**:
|
|
```bash
|
|
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**:
|
|
```javascript
|
|
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**:
|
|
```json
|
|
{
|
|
"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?
|