--- status: draft last_updated: 2026-05-25 --- # Infrastructure: Server & Network Layout ## Overview The hub runs as a Docker container on a dedicated server, connecting to Postgres and Redis. Spokes connect to the hub over the internet via WebSocket. > **Note**: This document describes the runtime architecture and configuration patterns. Specific server IPs, hostnames, and credentials are managed through the encrypted config system (see hub-config.md) and are NOT stored in this repository. ## Server Requirements ### Hub Server | Property | Requirement | | --------------- | ---------------------------------- | | Runtime | Deno (latest stable) | | HTTP | Hono server on configured port | | WebSocket | Hono WebSocket upgrade at `/ws` | | Postgres | 16+ (connected via encrypted config) | | Redis | 7+ (connected via encrypted config) | | TLS | Via reverse proxy (nginx, caddy) | ### Spoke Runtime Any environment with Deno and a WebSocket connection to the hub. No Postgres, no Redis, no HTTP server needed. ## Network Architecture ``` Internet │ ├─── Hub (api.alk.dev or configured hostname) │ ├── Hono HTTP server │ ├── WebSocket endpoint (/ws) │ ├── MCP endpoint (/mcp) │ ├── Postgres connection (encrypted config) │ └── Redis connection (encrypted config) │ └─── Spokes (dev env, compute, client) └── WebSocket connection to hub ``` ## Postgres - **Version**: 16+ - **Connection**: Configured via `HubConfig.postgres` (encrypted in config file) - **Auth**: Credentials in encrypted config, never in environment variables - **Database**: `alkdev` (default, configurable) - **Migrations**: Drizzle ORM programmatic migrator at startup (see hub-startup.md) - **Accessible from**: Hub container only (or WireGuard VPN for development) See storage/README.md for Drizzle setup and migration strategy. ## Redis - **Version**: 7+ - **Connection**: Configured via `HubConfig.redis` (encrypted in config file) - **Usage**: PubSub event transport, API key cache, session token cache, spoke health - **Two connections**: One for publishing, one for subscribing (Redis pub/sub requires dedicated subscriber) See pubsub-redis.md for Redis EventTarget configuration. ## Deployment ### Hub (Docker) The hub reads config from `/etc/alkhub/config.json` and master key from `/run/secrets/hub_master_key`. See hub-config.md for the full config system and hub-startup.md for the startup sequence. ```bash docker run -d \ --name alkdev-hub \ -p ::3000 \ --tmpfs /run/secrets:mode=0400,uid=0 \ -v /path/to/config.json:/etc/alkhub/config.json:ro \ -v /path/to/master-key:/run/secrets/hub_master_key:ro \ alkdev/hub:latest ``` A reverse proxy (nginx, caddy) handles TLS termination and proxies to the hub. ### Development For local development, Postgres and Redis can be run via Docker Compose or connected to over a VPN. The hub's `development: true` flag enables pretty-print logging and stricter error handling. ```bash # Local development with Docker Compose docker compose up postgres redis deno task dev ``` ## Health Check ```dockerfile HEALTHCHECK --interval=30s --timeout=5s --retries=3 \ CMD curl -f http://localhost:3000/health || exit 1 ``` The `/health` endpoint returns: - `200 { "status": "ok" }` when all systems ready - `503 { "status": "starting", "step": "" }` during startup - `503 { "status": "degraded", "issues": [...] }` if a subsystem fails after startup Step names: `resolve-config`, `load-config`, `init-logger`, `connect-postgres`, `run-migrations`, `connect-redis`, `init-keyring`, `init-drizzle`, `init-subsystems`, `start-server`, `ready` ## Security - **No secrets in environment variables**: All secrets come from encrypted config or Docker secrets (see hub-config.md) - **No secrets in git**: The `.gitignore` excludes `.env*`, `*.key`, `*.pem`, dev config files - **Config file encryption**: Sensitive fields are AES-256-GCM encrypted, see hub-config.md - **Postgres**: Not exposed to public internet. Connection details in encrypted config only - **Redis**: Not exposed to public internet. Connection details in encrypted config only - **API keys**: Managed by keypal, stored in `api_keys` table (hashed, never plaintext) - **Client secrets**: Encrypted at rest with key versioning (see ADR-008) - **WebSocket auth**: Bearer token at upgrade or first message (see spoke-runner.md Open Questions) ## References - [hub-config.md](hub-config.md) — Config system, encrypted fields, master key - [hub-startup.md](hub-startup.md) — Startup sequence, failure modes - [storage/README.md](storage/README.md) — Drizzle setup, migration strategy - [spoke-runner.md](spoke-runner.md) — Spoke authentication - [pubsub-redis.md](pubsub-redis.md) — Redis EventTarget configuration