# Deployment Guide

## Docker Single-Instance

```bash
docker pull gobha/armature:latest
docker run -p 8080:80 \
  -e ARMATURE_ADMIN_USERNAME=admin \
  -e ARMATURE_ADMIN_PASSWORD=changeme \
  -e JWT_SECRET="$(openssl rand -hex 32)" \
  -e ENCRYPTION_KEY="$(openssl rand -hex 32)" \
  -v armature-data:/data \
  gobha/armature:latest
```

This runs with SQLite and PVC storage. Suitable for evaluation and small teams.

## Docker Compose with PostgreSQL

```yaml
services:
  postgres:
    image: postgres:16
    environment:
      POSTGRES_DB: armature
      POSTGRES_USER: armature
      POSTGRES_PASSWORD: secretpassword
    volumes:
      - pg_data:/var/lib/postgresql/data

  armature:
    image: gobha/armature:latest
    ports:
      - "8080:80"
    environment:
      DATABASE_URL: "postgres://armature:secretpassword@postgres:5432/armature?sslmode=disable"
      JWT_SECRET: "change-me-in-production"
      ENCRYPTION_KEY: "change-me-in-production"
      ARMATURE_ADMIN_USERNAME: admin
      ARMATURE_ADMIN_PASSWORD: changeme
      STORAGE_BACKEND: pvc
      STORAGE_PATH: /data/storage
    volumes:
      - armature_storage:/data/storage
    depends_on:
      - postgres

volumes:
  pg_data:
  armature_storage:
```

## Kubernetes

See the `k8s/` directory for example manifests. Key considerations:

- Set `POSTGRES_HOST`, `POSTGRES_USER`, `POSTGRES_PASSWORD`, `POSTGRES_DB` env vars (assembled into DSN automatically).
- Liveness probe: `/healthz/live`. Readiness probe: `/healthz/ready`.
- Mount a PVC at `/data/storage` or configure S3.
- Store `JWT_SECRET` and `ENCRYPTION_KEY` in Kubernetes Secrets.
- Registry: `registry.gobha.me:5000/xcaliber/armature`.

## Environment Variables

| Variable | Default | Description |
|----------|---------|-------------|
| `PORT` | `8080` | Backend API port |
| `DB_DRIVER` | auto | `postgres` or `sqlite` |
| `DATABASE_URL` | | PostgreSQL DSN or SQLite path |
| `JWT_SECRET` | `dev-secret-change-me` | Token signing key -- **must change** |
| `ENCRYPTION_KEY` | | AES-256 key for credential vault |
| `AUTH_MODE` | `builtin` | `builtin`, `mtls`, `oidc` |
| `TLS_MODE` | (empty) | `native` for node-to-node mTLS. Requires `MTLS_CERT_PATH` and `MTLS_KEY_PATH`. |
| `STORAGE_BACKEND` | auto | `pvc` or `s3` |
| `STORAGE_PATH` | `/data/storage` | PVC mount point |
| `BASE_PATH` | | URL prefix (e.g., `/armature`) |
| `LOG_FORMAT` | `text` | `text` or `json` |
| `LOG_LEVEL` | `info` | `debug`, `info`, `warn`, `error` |
| `CORS_ALLOWED_ORIGINS` | | Comma-separated allowed origins |
| `BUNDLED_PACKAGES` | (empty) | `""` defaults, `"*"` all, or comma-separated |
| `SKIP_BUNDLED_PACKAGES` | `false` | Disable bundled package install |
| `BUNDLED_PACKAGES_DIR` | `/app/bundled-packages` | Custom bundle directory |
| `ARMATURE_ADMIN_USERNAME` | | Bootstrap admin username |
| `ARMATURE_ADMIN_PASSWORD` | | Bootstrap admin password |
| `SEED_USERS` | | Dev seed users (ignored in production) |

### S3 Storage Variables

| Variable | Description |
|----------|-------------|
| `S3_BUCKET` | Bucket name |
| `S3_ENDPOINT` | Endpoint URL (for MinIO, Ceph, etc.) |
| `S3_ACCESS_KEY` | Access key |
| `S3_SECRET_KEY` | Secret key |
| `S3_FORCE_PATH_STYLE` | `true` for MinIO/Ceph |

### Cluster Variables (Multi-Replica)

| Variable | Default | Description |
|----------|---------|-------------|
| `CLUSTER_NODE_ID` | hostname-PID | Override for deterministic node identity |
| `CLUSTER_HEARTBEAT_INTERVAL` | `10s` | Heartbeat tick frequency |
| `CLUSTER_STALE_THRESHOLD` | `30s` | 3x heartbeat = stale node |
| `CLUSTER_ENDPOINT` | auto-detect | Advertised address for peer mesh |

## Database Configuration

**PostgreSQL** (recommended for production):

```bash
DATABASE_URL="postgres://user:pass@host:5432/armature?sslmode=require"
```

**SQLite** (dev, test, edge deployments):

```bash
DB_DRIVER=sqlite
DATABASE_URL=/data/armature.db
```

Both databases are first-class -- every query compiles and passes tests on both. The driver is auto-detected from `DATABASE_URL` if `DB_DRIVER` is not set.

## Cluster Mode

Multi-replica HA requires PostgreSQL. The kernel uses PG-backed WebSocket tickets and rate limit counters to coordinate across replicas. A cluster registry with heartbeat and stale sweep tracks active nodes.

View cluster status in Admin > Cluster or via `GET /api/v1/admin/cluster`.

## Storage Backends

**PVC**: Auto-detected if the storage path is writable. Mount a volume at `/data/storage`.

**S3-compatible**: Set `STORAGE_BACKEND=s3` and provide S3 variables. Works with AWS S3, MinIO, and Ceph.

## Backup and Restore

Available via Admin UI or API:

```
POST /api/v1/admin/backup       # Create backup
GET  /api/v1/admin/backups      # List backups
GET  /api/v1/admin/backups/:name  # Download backup
POST /api/v1/admin/restore      # Restore from backup
DELETE /api/v1/admin/backups/:name  # Delete backup
```

## Monitoring

| Endpoint | Purpose |
|----------|---------|
| `/health` | Basic health check |
| `/healthz/live` | Kubernetes liveness probe |
| `/healthz/ready` | Kubernetes readiness probe |
| `/metrics` | Prometheus metrics |
