# 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**: Ready (pending deploy key setup)

Mount actual project repos (pantheon, aeries) into dedicated claudebox pods.

**Deliverables**:
- [x] `claudebox-pantheon` StatefulSet with pantheon repo
- [x] `claudebox-aeries` StatefulSet with aeries repo
- [x] Init container that clones repo on first start
- [x] Git SSH deploy keys as Kubernetes secrets (template)
- [x] 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**:
```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**: Ready (requires image rebuild)

Pods can commit and push changes back to GitHub.

**Deliverables**:
- [x] SSH keys mounted at `/root/.ssh/` (done in v0.2)
- [x] Git config (user.name, user.email) set in container
- [x] known_hosts includes github.com (done in v0.2)
- [x] 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):
```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**: Ready (requires image build)

Go API server for controlling claudebox pods.

**Deliverables**:
- [x] `rdev-api` Go service
- [x] REST endpoints for claude, shell, git commands
- [x] SSE endpoint for streaming output
- [x] Kubernetes RBAC for pod exec
- [x] 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**:

```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?
