# Base stack: the parking SERVER (API + SPA) + the VISION (ANPR) service. Branch-aware via # ${REGISTRY}/${TAG} — a deploy on `dev` pulls :dev, on `main` pulls :main. Use an env # override file for the environment: docker-compose.dev.yml (build locally, stub recognizer) # or docker-compose.prod.yml (pull pinned images, fast_alpr). See # wiki/decisions/container-deployment.md. # # local dev : docker compose -f docker-compose.yml -f docker-compose.dev.yml up --build # prod : REGISTRY=… TAG=main docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d services: server: image: ${REGISTRY:-git.infra.msai.al/mca/parking_solution}/parking-server:${TAG:-dev} restart: unless-stopped environment: DATABASE_URL: /data/parking.sqlite # Reach the vision service. DEV: the private compose-network service name (`vision`). # PROD: the server runs on the HOST network (to see the booth LAN / device VLAN — it's the # only container doing device I/O), where compose DNS doesn't resolve, so the prod override # sets VISION_URL=http://127.0.0.1:8089 and vision publishes 8089 on the host loopback. VISION_URL: ${VISION_URL:-http://vision:8089} VISION_ENABLED: ${VISION_ENABLED:-1} # JWT signing secret MUST be provided at deploy (no insecure default — see auth.ts). JWT_SECRET: ${JWT_SECRET:?set JWT_SECRET in the env/.env} # Dedicated ledger-signing key. Falls back to JWT_SECRET (with a warning) if empty; # set a distinct one in prod. See apps/server/.env.example + local-jwt-auth. EVENT_SIGNING_KEY: ${EVENT_SIGNING_KEY:-} # Dedicated backup-ENCRYPTION key (separate from the signing key). Empty = backups stay # off (the in-UI target + retention do nothing without it). Per-booth + unique; escrow it # offsite. See apps/server/.env.example + wiki/concepts/backup-recovery.md. BACKUP_KEY: ${BACKUP_KEY:-} # CRITICAL on the plain-HTTP booth LAN: cookies are Secure (HTTPS-only) by DEFAULT, # so without COOKIE_SECURE=0 the auth cookie is never sent over http and operators # CANNOT LOG IN. Leave unset only behind TLS. See disk-os-hardening "deploy-time runbook". COOKIE_SECURE: ${COOKIE_SECURE:-0} # The booth WS live feed checks the browser Origin — must list the address operators # actually hit (e.g. http://:3000), or the live feed is rejected. WS_ALLOWED_ORIGINS: ${WS_ALLOWED_ORIGINS:-} # Venue modules this site is ENTITLED to (vendor decision, per stack in Komodo; the site # admin activates within this set in Setup → Site). Only variables listed HERE reach the # container — a value in the Komodo stack env alone does nothing (found 2026-09-06: every # booth had Car Wash on). Default = what booths had before modules existed; the server # treats a BLANK value as "every module", so never set it to "" on a booth. MODULES_ENTITLED: ${MODULES_ENTITLED:-parking,validation} volumes: - parking-data:/data depends_on: vision: condition: service_started networks: - parking vision: image: ${REGISTRY:-git.infra.msai.al/mca/parking_solution}/parking-vision:${TAG:-dev} restart: unless-stopped environment: # Engine: stub (no models) by default; prod override sets fast_alpr. VISION_RECOGNIZER: ${VISION_RECOGNIZER:-stub} networks: - parking volumes: parking-data: networks: parking: driver: bridge