# CASAN local full stack (macOS) This guide runs the entire currently implemented CASAN stack on one Mac. It is a local production-like lab, not a production compliance claim. The Linux server is deliberately not used in this phase. ## What runs locally | Component | Address | Role | |---|---|---| | Vault dev / Transit | `http://127.0.0.1:18200` | KMS signing and rotation exercises | | Mock OIDC / JWKS | `http://127.0.0.1:18081` | Approval JWT validation | | MinIO S3 API | `http://127.0.0.1:19090` | S3-compatible object storage | | MinIO Console | `http://127.0.0.1:19091` | S3 administration UI | | MinIO WORM bucket | `casan-worm` | Object Lock / 1-day compliance retention | | Alert mock | `http://127.0.0.1:19092` | Alert webhook target | | Billing mock | `http://127.0.0.1:19093/usage` | Provider-usage import target | | AgentOps dashboard | `http://127.0.0.1:18080` | Dashboard behind nginx basic auth | | CASAN Control Panel | `https://localhost:18443` | TLS + mock OIDC + oauth2-proxy + nginx + NestJS + React | | OmniRoute | `http://127.0.0.1:20128/v1` | Existing local OpenAI-compatible model gateway | The Control Panel uses a different mock IdP port (`18082`) so it can run at the same time as the approval IdP (`18081`). ## 1. Prerequisites - Docker Desktop running, with at least 6 GB RAM allocated. - Node.js 20+ and npm (needed only for host-side development and CASAN pipeline execution; the local services run in Docker). - OmniRoute already reachable at `http://127.0.0.1:20128/v1` when model mode is required. Confirm Docker first: ```bash docker version docker compose version ``` ## 2. Start every local service From the repository root: ```bash bash packages/casan-harness/scripts/bash/local-full.sh start ``` The first run builds the mock services and Control Panel images. Open: - Dashboard: `http://127.0.0.1:18080` — credentials `casan` / `casan`. - MinIO Console: `http://127.0.0.1:19091` — credentials `casanadmin` / `casanadmin123`. - Control Panel: `https://localhost:18443` — accept the local self-signed certificate. The mock OIDC flow signs in automatically as `oidc-ops` with `org-admin` for local testing only. Check running services at any time: ```bash bash packages/casan-harness/scripts/bash/local-full.sh status ``` ## 3. Verify infrastructure controls ```bash bash packages/casan-harness/scripts/bash/local-full.sh verify ``` This performs live Vault Transit sign/verify, gets an RS256 JWT from the mock OIDC IdP and validates it via JWKS, writes a retained object to MinIO, sends an alert, imports billing telemetry, and accesses the authenticated dashboard. For the stricter TLS/OIDC Control Panel browser smoke: ```bash bash packages/casan-harness/scripts/bash/local-full.sh smoke ``` `smoke` is intentionally isolated and stops its temporary Control Panel stack when complete. Run `start` again if you want the UI to remain up afterwards. ## 4. Connect CASAN to OmniRoute CASAN now supports an explicit OpenAI-compatible gateway model syntax. It does not relax the public OpenAI endpoint hardening: the custom URL is required and, by default, may only be localhost. Keep secrets out of shell history and Git. First load your key into the current shell using the mechanism you normally use (for example a password manager or a non-tracked `.env.local`). Then list model IDs that OmniRoute exposes: ```bash curl -fsS http://127.0.0.1:20128/v1/models | python3 -m json.tool ``` Configure a returned model ID. `CASAN_OPENAI_COMPATIBLE_API_KEY` may be a dedicated OmniRoute key; if omitted, the router falls back to `OPENAI_API_KEY`. ```bash export CASAN_OPENAI_COMPATIBLE_BASE_URL=http://127.0.0.1:20128/v1 export CASAN_OPENAI_COMPATIBLE_API_KEY="$OPENAI_API_KEY" export CASAN_MODEL_PRIMARY='openai-compatible:auto/best-coding' export CASAN_CHAT_MODEL_MODE=model export CASAN_CHAT_MODEL_PROVIDER=omniroute export CASAN_PREFLIGHT=1 ``` For the Dockerized Control Panel, copy the provided template and replace only the key locally. Docker uses `host.docker.internal` so the Linux container can reach OmniRoute on the Mac host. ```bash cp infra/local-prod/casan.local.env.example infra/local-prod/casan.local.env # Edit casan.local.env in your editor and replace the placeholder key. bash packages/casan-harness/scripts/bash/local-full.sh start ``` Do not set a bare `localhost` URL in this file: inside the Control Panel container, `localhost` means the container itself, not your Mac. Run a non-destructive router smoke. A successful response records real token usage in `.specify/logs/level5/provider-usage.jsonl`. ```bash printf 'Return exactly: CASAN_OK\n' >/tmp/casan-prompt.txt bash packages/casan-harness/scripts/bash/model-router.sh \ /tmp/casan-prompt.txt /tmp/casan-model-result.json --role generate \ --model "$CASAN_MODEL_PRIMARY" python3 -m json.tool /tmp/casan-model-result.json ``` The configured `omniroute` chat provider is classified as a gateway and always forces CASAN's preflight data-governance check before prompt content is sent. This is intentional: OmniRoute could route a request to a cloud model even when the first selected model is local. To use a gateway on a private-LAN host later, explicitly name it; never use an open allowlist: ```bash export CASAN_OPENAI_COMPATIBLE_BASE_URL=http://192.168.1.5:20128/v1 export CASAN_OPENAI_COMPATIBLE_ALLOWED_HOSTS=192.168.1.5 ``` Use HTTPS and a real certificate before moving this beyond a trusted LAN. ### Local Ornith default for the Control Panel The local Docker Control Panel now defaults to the existing host Ollama model `ornith:9b`. It reaches Docker Desktop's host bridge at `host.docker.internal:11434`; CASAN permits only that exact bridge when the local compose profile explicitly enables it. If Ollama is unavailable, chat returns its deterministic evidence answer instead of fabricating a model result. Use OmniRoute/OpenAI only when you deliberately want the gateway route. Create `casan.local.env` as described above and set: ```bash CASAN_CHAT_MODEL_PROVIDER=omniroute CASAN_OPENAI_COMPATIBLE_BASE_URL=http://host.docker.internal:20128/v1 CASAN_OPENAI_COMPATIBLE_API_KEY=your-key ``` ## 5. Run CASAN with local models For model-backed source generation, use the same environment and enable it only for the invocation: ```bash CASAN_GEN_MODE=model CASAN_PREFLIGHT=1 \ node scripts/casan-step.mjs 01-srs 1 ``` CASAN continues to fall back safely to deterministic templates if the gateway is unavailable or the output is rejected by a harness gate. For governed chat, start the Control Panel and select the `omniroute` provider through the existing model configuration; the default remains deterministic/offline. ## 6. Stop and reset Stop all containers while preserving MinIO data: ```bash bash packages/casan-harness/scripts/bash/local-full.sh stop ``` To remove only the lab's persisted object-store data, first stop the lab, then: ```bash docker volume rm casan-local-prod_minio-data ``` This is destructive and cannot be undone. Do not use it if the MinIO bucket has evidence you need to retain. ## Current scope and next phase This local environment includes every currently implemented CASAN component. It intentionally uses Vault dev mode, self-signed TLS, mock IdPs, local MinIO, and mock alert/billing endpoints. Production promotion to the Linux server should replace those with managed Vault/KMS or HSM, real S3 Object Lock, real OIDC, HTTPS certificates, a secret manager, backup/restore tests, and firewall rules; it is a separate deployment exercise.