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 <noreply@anthropic.com>
6.8 KiB
6.8 KiB
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_KEYenv 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
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
- PostgreSQL running in
databasesnamespace rdevdatabase created withappuseraccess- K8s secret
rdev-credentialswith:DB_PASSWORD: PostgreSQL passwordRDEV_ADMIN_KEY: Bootstrap admin key
Build and Deploy
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
# 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:
curl -H "Authorization: Bearer rdev_sk_..." http://localhost:8080/projects
Create API Key
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
curl -H "Authorization: Bearer $ADMIN_KEY" http://localhost:8080/keys
Revoke Key
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:
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 <key>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
rdevdatabase exists withappuseraccess
Migration failed
- Check postgres logs for errors
- Verify appuser has CREATE TABLE permissions on rdev database