# Local production-like smoke for Plan-13 Control Panel: # self-signed TLS + mock OIDC IdP + oauth2-proxy + nginx auth_request. services: control-panel-api: build: context: . dockerfile: Dockerfile.control-panel-api image: casan-control-panel-api:local restart: unless-stopped environment: CASAN_PROFILE: prod CASAN_CP_STRICT: "1" CASAN_CP_TRUST_AUTH_PROXY: "1" CP_BIND: 0.0.0.0 CP_PORT: "3010" CASAN_APP_ROOT: /app CASAN_DASHBOARD_STALE_S: "315360000" # Optional local model gateway. Values come from infra/local-prod/casan.local.env # via local-full.sh and are never committed with credentials. # The Mac-hosted Ornith/Ollama model is the local default. If it is not # reachable, the governed chat falls back safely to deterministic answers. CASAN_CHAT_MODEL_MODE: ${CASAN_CHAT_MODEL_MODE:-model} CASAN_CHAT_MODEL_PROVIDER: ${CASAN_CHAT_MODEL_PROVIDER:-local} CASAN_OLLAMA_HOST: host.docker.internal:11434 CASAN_ALLOW_DOCKER_HOST_OLLAMA: "1" OLLAMA_HOST: host.docker.internal:11434 CASAN_OPENAI_COMPATIBLE_BASE_URL: ${CASAN_OPENAI_COMPATIBLE_BASE_URL:-} CASAN_OPENAI_COMPATIBLE_API_KEY: ${CASAN_OPENAI_COMPATIBLE_API_KEY:-} CASAN_OPENAI_COMPATIBLE_ALLOWED_HOSTS: ${CASAN_OPENAI_COMPATIBLE_ALLOWED_HOSTS:-} # Direct provider credentials are separate from the OpenAI-compatible # gateway; a gateway key must never be assumed valid for api.openai.com. OPENAI_API_KEY: ${OPENAI_API_KEY:-} ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY:-} CASAN_GOAL_OPENAI_MODEL: ${CASAN_GOAL_OPENAI_MODEL:-gpt-5.3-codex} CASAN_GOAL_ANTHROPIC_MODEL: ${CASAN_GOAL_ANTHROPIC_MODEL:-claude-3-5-sonnet-latest} CASAN_GOAL_ENABLE_GATEWAY_PATCH_REPAIR: "1" CASAN_GOAL_LOCAL_TIMEOUT_SEC: "600" CASAN_GOAL_MODEL_TIMEOUT: "600" CASAN_GOAL_MODEL_PREFLIGHT: "1" CASAN_GOAL_MODEL_PREFLIGHT_TIMEOUT_SEC: "30" CASAN_GOAL_LOCAL_MODEL_PREFLIGHT_TIMEOUT_SEC: "90" CASAN_GOAL_MODEL_PREFLIGHT_TTL_SEC: "600" CASAN_PREFLIGHT: ${CASAN_PREFLIGHT:-0} CASAN_AUTH_BRIDGE_URL: http://host.docker.internal:20130 CASAN_AUTH_BRIDGE_TOKEN: ${CASAN_AUTH_BRIDGE_TOKEN:-} CASAN_PROVIDER_ACCOUNT_AUTH_ENABLED: "1" # Goal approval decisions are signed by the local OIDC test IdP and then # verified by H5. The UI never receives or stores this short-lived JWT. CASAN_APPROVAL_TOKEN_URL: http://idp:8080/token CASAN_APPROVAL_TOKEN_AUTH_TOKEN: ${CASAN_APPROVAL_SIGNER_TOKEN:-local-approval-signer-secret} CASAN_APPROVAL_TOKEN_ALLOWED_HOSTS: idp CASAN_APPROVAL_TOKEN_ALLOW_HTTP: "1" CASAN_APPROVAL_SIGNER_ROLE: ops CASAN_IDP_JWKS_URL: http://idp:8080/.well-known/jwks.json volumes: # Persist only governed state and registered project roots. Dependencies # stay inside the Linux image, avoiding host/container native-binary drift. - ./.specify:/app/.specify - ./docs/output:/app/docs/output - ./docs/technical_architecture.md:/app/docs/technical_architecture.md:ro - ./apps/okr:/app/apps/okr - ./apps/service-desk:/app/apps/service-desk - ./apps/projects:/app/apps/projects - ./packages/casan-harness/config/project-registry.json:/app/packages/casan-harness/config/project-registry.json expose: - "3010" networks: - casan-control-local control-panel-ui: build: context: . dockerfile: Dockerfile.control-panel-ui image: casan-control-panel-ui:local restart: unless-stopped depends_on: - control-panel-api - oauth2-proxy ports: - "18443:443" volumes: - ./tmp/control-panel-local/tls:/etc/nginx/tls:ro networks: - casan-control-local oauth2-proxy: image: quay.io/oauth2-proxy/oauth2-proxy:v7.6.0 restart: unless-stopped command: - --http-address=0.0.0.0:4180 - --provider=oidc - --skip-oidc-discovery=true - --oidc-issuer-url=http://localhost:18082 - --login-url=http://localhost:18082/authorize - --redeem-url=http://idp:8080/token - --profile-url=http://idp:8080/userinfo - --oidc-jwks-url=http://idp:8080/.well-known/jwks.json - --client-id=casan-control-panel - --client-secret=local-secret - --redirect-url=https://localhost:18443/oauth2/callback - --whitelist-domain=localhost:18443 - --cookie-secret=0123456789abcdef0123456789abcdef - --cookie-secure=true - --cookie-httponly=true - --cookie-samesite=lax - --cookie-expire=8h - --cookie-refresh=1h - --email-domain=* - --scope=openid email profile groups - --oidc-groups-claim=groups - --reverse-proxy=true - --set-xauthrequest=true - --pass-access-token=false - --pass-authorization-header=false - --skip-provider-button=true - --ssl-insecure-skip-verify=true expose: - "4180" networks: - casan-control-local idp: build: context: ./infra/local-prod/idp environment: CASAN_IDP_PORT: "8080" CASAN_IDP_ISSUER: http://localhost:18082 CASAN_IDP_SUB: oidc-ops CASAN_IDP_EMAIL: oidc-ops@example.com CASAN_IDP_GROUPS: casan-org-admin,casan-approver,casan-project:AINative_OKR_CASAN4 CASAN_APPROVAL_SIGNER_TOKEN: ${CASAN_APPROVAL_SIGNER_TOKEN:-local-approval-signer-secret} ports: - "18082:8080" networks: - casan-control-local networks: casan-control-local: driver: bridge