# Production Deployment Guide ## Overview AOC runs as a set of Docker containers orchestrated by Docker Compose: - **nginx** — reverse proxy, TLS termination, static file serving - **backend** — FastAPI application (Gunicorn + Uvicorn workers) - **mongo** — MongoDB data store (not exposed externally) - **valkey** — Redis-compatible cache and async job queue (not exposed externally) ## Prerequisites - Docker Engine 24+ and Docker Compose plugin - A server with ports 80/443 reachable from your users - TLS certificates (place in `nginx/ssl/` or use Let's Encrypt) - A valid `.env` file at the repo root (see `.env.example`) ## Quick start 1. **Clone / pull the latest release** ```bash git checkout v1.7.14 ``` 2. **Copy and edit environment variables** ```bash cp .env.example .env # Edit .env and fill in real credentials ``` 3. **Set the release version** ```bash export AOC_VERSION=v1.7.14 ``` 4. **Deploy** ```bash docker compose -f docker-compose.prod.yml pull docker compose -f docker-compose.prod.yml up -d ``` 5. **Verify** ```bash curl http://localhost/health curl http://localhost/api/events ``` ## Updating to a new release ```bash export AOC_VERSION=v1.7.14 docker compose -f docker-compose.prod.yml pull docker compose -f docker-compose.prod.yml up -d ``` ## Enabling HTTPS ### Option A: Use your own certificates 1. Place `cert.pem` and `key.pem` in `nginx/ssl/` 2. Uncomment the HTTPS server block in `nginx/nginx.conf` 3. Uncomment the HTTP → HTTPS redirect server block 4. Reload nginx: ```bash docker compose -f docker-compose.prod.yml exec nginx nginx -s reload ``` ### Option B: Let's Encrypt with Certbot Replace the `nginx` service in `docker-compose.prod.yml` with a Certbot-friendly setup (e.g., use the `nginx-proxy` + `acme-companion` stack) or mount the Certbot certificates into `nginx/ssl/`. ## Security Hardening - MongoDB is **not exposed** to the host — only the backend container can reach it. - Valkey/Redis is **not exposed** to the host — only the backend container can reach it. - The backend runs as a non-root (`aoc`) user inside the container. - nginx adds security headers (`X-Frame-Options`, `X-Content-Type-Options`, etc.). - Keep `.env` out of version control — it is listed in `.gitignore`. - Set `AUTH_ENABLED=true` and configure `AUTH_ALLOWED_ROLES` or `AUTH_ALLOWED_GROUPS` to restrict access to admin/security roles. - Set explicit `CORS_ORIGINS` — do not use `*` in production when auth is enabled. - Set `DOCS_ENABLED=false` to hide OpenAPI docs (`/docs`, `/openapi.json`). - Configure `WEBHOOK_CLIENT_SECRET` to validate Graph webhook notifications. - Set `LLM_ALLOWED_DOMAINS` if using AI features (e.g. `api.openai.com,*.openai.azure.com`). - Set `SIEM_ALLOWED_DOMAINS` if using SIEM forwarding. - Review `METRICS_ALLOWED_IPS` — defaults to private networks + loopback. ## Azure Key Vault (Optional) To eliminate long-lived secrets from `.env`: 1. Create an Azure Key Vault and add these secrets: - `aoc-client-secret` — your Graph app `CLIENT_SECRET` - `aoc-llm-api-key` — your `LLM_API_KEY` (if using AI) - `aoc-mongo-uri` — your `MONGO_URI` - `aoc-webhook-client-secret` — your `WEBHOOK_CLIENT_SECRET` 2. Uncomment `azure-identity` and `azure-keyvault-secrets` in `backend/requirements.txt` 3. Set `AZURE_KEY_VAULT_NAME=your-keyvault-name` in `.env` 4. Grant the container identity `Get` permission on secrets: - If using Azure Container Instances / AKS: assign a managed identity - If using VM: assign a managed identity or use a service principal - If using local Docker: authenticate via `az login` on the host 5. Rebuild and redeploy: ```bash docker compose -f docker-compose.prod.yml up -d --build ``` ## Rollback ```bash export AOC_VERSION=v1.7.13 docker compose -f docker-compose.prod.yml pull docker compose -f docker-compose.prod.yml up -d ``` ## Monitoring - Prometheus metrics: `http://your-host/metrics` (IP-restricted by default) - Health check: `http://your-host/health` - Container logs: ```bash docker compose -f docker-compose.prod.yml logs -f backend docker compose -f docker-compose.prod.yml logs -f nginx docker compose -f docker-compose.prod.yml logs -f mongo docker compose -f docker-compose.prod.yml logs -f valkey ``` ## Troubleshooting - **Auth warning in logs**: "AUTH_ENABLED is true but no AUTH_ALLOWED_ROLES or AUTH_ALLOWED_GROUPS are configured" — set these to restrict access. - **CORS issues**: Set `CORS_ORIGINS` to your exact frontend origin(s). Wildcard with auth enabled disables credentials. - **Rate limiting 429s**: Check Redis/Valkey connectivity. The rate limiter fails closed (returns 429) when Redis is down. - **LLM errors**: Verify `LLM_BASE_URL` is in `LLM_ALLOWED_DOMAINS` if the allowlist is configured. - **SIEM not forwarding**: Verify `SIEM_WEBHOOK_URL` uses HTTPS and is in `SIEM_ALLOWED_DOMAINS`.