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 <noreply@anthropic.com>
This commit is contained in:
parent
fa66a69120
commit
48f7dc9f74
250
history/v0.5.0.md
Normal file
250
history/v0.5.0.md
Normal file
@ -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 <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 `rdev` database exists with `appuser` access
|
||||
|
||||
### Migration failed
|
||||
- Check postgres logs for errors
|
||||
- Verify appuser has CREATE TABLE permissions on rdev database
|
||||
Loading…
Reference in New Issue
Block a user