From 48f7dc9f74ef2cc38a700775688fef06b2876254 Mon Sep 17 00:00:00 2001 From: jordan Date: Sat, 24 Jan 2026 23:48:43 -0700 Subject: [PATCH] docs: add v0.5.0 history - API key authentication Documents the complete API key authentication system: - Key format, hashing, and scopes - Database schema and migrations - Auth middleware and endpoints - Build/deploy instructions - Fixes for chi middleware ordering and Colima cross-compilation Co-Authored-By: Claude Opus 4.5 --- history/v0.5.0.md | 250 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 250 insertions(+) create mode 100644 history/v0.5.0.md diff --git a/history/v0.5.0.md b/history/v0.5.0.md new file mode 100644 index 0000000..bb7a54f --- /dev/null +++ b/history/v0.5.0.md @@ -0,0 +1,250 @@ +# rdev v0.5.0 - API Key Authentication + +**Date**: 2026-01-25 +**Status**: Deployed + +## Summary + +API key authentication system for rdev-api. External clients must authenticate with API keys that have scoped permissions. Includes admin key bootstrapping, auto-running migrations, and a full key management API. + +## What Was Built + +### Authentication System + +- **API Key Format**: `rdev_sk_<8-char-prefix>_<32-char-hex>` +- **SHA-256 Hashing**: Keys stored as hashes, secrets never persisted +- **Scoped Permissions**: Fine-grained access control +- **Expiration Options**: 30d, 60d, 90d, 1y, never +- **Admin Bootstrap**: `RDEV_ADMIN_KEY` env var for initial access + +### Scopes + +| Scope | Description | +|-------|-------------| +| `projects:read` | List and get project info | +| `projects:execute` | Run commands (claude, shell, git) | +| `keys:read` | List API keys | +| `keys:write` | Create/revoke API keys | +| `admin` | Full access (includes all scopes) | + +### New API Endpoints + +| Method | Path | Description | Required Scope | +|--------|------|-------------|----------------| +| GET | `/keys` | List all API keys | keys:read | +| POST | `/keys` | Create new API key | keys:write | +| GET | `/keys/{id}` | Get key details | keys:read | +| DELETE | `/keys/{id}` | Revoke API key | keys:write | + +### Packages Created + +``` +internal/ +├── db/ +│ ├── postgres.go # Auto-migrating database connection +│ └── migrations/ +│ └── 001_create_api_keys.sql +├── auth/ +│ ├── keys.go # Key generation and hashing +│ ├── scopes.go # Scope validation +│ ├── service.go # CRUD operations +│ └── middleware.go # HTTP auth middleware +└── handlers/ + └── keys.go # Key management handlers +``` + +### Database Schema + +```sql +CREATE TABLE api_keys ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + name VARCHAR(255) NOT NULL, + key_hash VARCHAR(64) NOT NULL UNIQUE, + key_prefix VARCHAR(8) NOT NULL, + scopes TEXT[] NOT NULL DEFAULT '{}', + project_ids TEXT[] DEFAULT NULL, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + expires_at TIMESTAMPTZ, + last_used_at TIMESTAMPTZ, + revoked_at TIMESTAMPTZ, + created_by VARCHAR(255) +); +``` + +## Files Created/Modified + +``` +# New files +internal/db/postgres.go +internal/db/migrations/001_create_api_keys.sql +internal/auth/keys.go +internal/auth/scopes.go +internal/auth/service.go +internal/auth/middleware.go +internal/handlers/keys.go +Dockerfile.api.simple # Pre-built binary deployment + +# Modified files +cmd/rdev-api/main.go # Added auth middleware, key endpoints +pkg/api/app.go # Fixed middleware ordering +deployments/k8s/base/rdev-api.yaml # Added DB env vars, secrets +``` + +## Dependencies Added + +``` +github.com/lib/pq # PostgreSQL driver +``` + +## Deployment + +### Prerequisites + +1. PostgreSQL running in `databases` namespace +2. `rdev` database created with `appuser` access +3. K8s secret `rdev-credentials` with: + - `DB_PASSWORD`: PostgreSQL password + - `RDEV_ADMIN_KEY`: Bootstrap admin key + +### Build and Deploy + +```bash +cd /path/to/rdev + +# Build binary natively (avoids Colima cross-compile issues) +CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o rdev-api ./cmd/rdev-api + +# Build and push image +docker build -f Dockerfile.api.simple -t ghcr.io/orchard9/rdev-api:v0.5.0 . +docker push ghcr.io/orchard9/rdev-api:v0.5.0 + +# Deploy +export KUBECONFIG=~/.kube/orchard9-k3sf.yaml +kubectl apply -k deployments/k8s/base +kubectl rollout restart deployment/rdev-api -n rdev +``` + +### Verify + +```bash +# Check API is running +kubectl get pods -n rdev -l app=rdev-api +kubectl logs -n rdev deployment/rdev-api + +# Port forward for testing +kubectl port-forward -n rdev svc/rdev-api 8080:8080 + +# Test with admin key +export ADMIN_KEY="rdev_sk_6d49dcad_..." +curl -H "Authorization: Bearer $ADMIN_KEY" http://localhost:8080/projects + +# Create a new key +curl -X POST -H "Authorization: Bearer $ADMIN_KEY" \ + -H "Content-Type: application/json" \ + http://localhost:8080/keys \ + -d '{"name": "discord-bot", "scopes": ["projects:read", "projects:execute"], "expires_in": "90d"}' +``` + +## Usage Examples + +### Authentication + +All requests (except `/health`, `/ready`, `/docs`, `/openapi.json`) require authentication: + +```bash +curl -H "Authorization: Bearer rdev_sk_..." http://localhost:8080/projects +``` + +### Create API Key + +```bash +curl -X POST http://localhost:8080/keys \ + -H "Authorization: Bearer $ADMIN_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "discord-bot", + "scopes": ["projects:read", "projects:execute"], + "expires_in": "90d" + }' + +# Response includes the secret (only shown once) +{ + "data": { + "key": { + "id": "bceb2ff6-807d-48fa-8e99-cd3468cece42", + "name": "discord-bot", + "key_prefix": "3e7afcc3", + "scopes": ["projects:read", "projects:execute"], + "expires_at": "2026-04-25T06:27:33Z" + }, + "secret": "rdev_sk_3e7afcc3_8b296dbc18924b4cf41cdcd99f9255b5" + } +} +``` + +### List Keys + +```bash +curl -H "Authorization: Bearer $ADMIN_KEY" http://localhost:8080/keys +``` + +### Revoke Key + +```bash +curl -X DELETE -H "Authorization: Bearer $ADMIN_KEY" \ + http://localhost:8080/keys/bceb2ff6-807d-48fa-8e99-cd3468cece42 +``` + +## Fixes Applied + +### Chi Middleware Ordering + +**Problem**: `panic: chi: all middlewares must be defined before routes on a mux` + +**Cause**: Health endpoints were registered in `New()` before auth middleware was added via `Use()`. + +**Fix**: Moved `setupHealthEndpoints()` from `New()` to `Run()` in `pkg/api/app.go`. + +### Colima Cross-Compilation + +**Problem**: Building inside Docker on Mac/Colima caused segfaults in crypto packages. + +**Fix**: Build binary natively on Mac, then copy into minimal Alpine container: +```bash +CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o rdev-api ./cmd/rdev-api +``` + +## Keys Created + +| Name | Prefix | Scopes | Expires | +|------|--------|--------|---------| +| admin (env) | 6d49dcad | admin | never | +| discord-bot | 3e7afcc3 | projects:read, projects:execute | 2026-04-25 | + +## What's Next (v0.6) + +- DNS setup: `rdev.orchard9.ai` +- End-to-end testing +- Discord bot integration +- Rate limiting +- Key rotation + +## Troubleshooting + +### 401 Unauthorized +- Check `Authorization: Bearer ` header is present +- Verify key hasn't expired or been revoked +- Ensure key has required scopes for the endpoint + +### 403 Forbidden +- Key is valid but lacks required scope +- Check key's scopes match the endpoint requirements + +### Database connection failed +- Verify `DB_*` env vars are set correctly +- Check postgres pod is running: `kubectl get pods -n databases` +- Ensure `rdev` database exists with `appuser` access + +### Migration failed +- Check postgres logs for errors +- Verify appuser has CREATE TABLE permissions on rdev database