# Copy to .env and fill in. Never commit .env. # --- Airtable --- # Personal Access Token (read-only scope on the base below). # Generate at https://airtable.com/create/tokens — scopes: data.records:read on base appoByydxIQANKtSh, tables Resource + Booking Resource. AIRTABLE_PAT= AIRTABLE_BASE_ID=appoByydxIQANKtSh # --- Session / auth --- # Random 32+ bytes. Generate with: python -c "import secrets; print(secrets.token_urlsafe(48))" SESSION_SECRET= # Local admin (used while AUTH_MODE=local). # Generate hash with: python -c "from passlib.hash import bcrypt; print(bcrypt.hash('your-password-here'))" # IMPORTANT: wrap the hash in SINGLE QUOTES — bcrypt hashes contain $ characters # that docker-compose will otherwise interpret as variable substitutions. ADMIN_USERNAME=admin ADMIN_PASSWORD_BCRYPT='' # AUTH_MODE: local (v1) or azure (v2 — not yet implemented). AUTH_MODE=local # Role for the local-admin user. Drives tab visibility per the RBAC matrix. # One of: global-lead | dept-lead | forecast # global-lead → full access (all tabs + AI chat) # dept-lead → Department + Tutorial + AI chat # forecast → Forecast tab only, no AI chat (external/client view) ADMIN_ROLE=global-lead # --- Anthropic (AI chat) --- # Leave ANTHROPIC_API_KEY empty to disable chat (the /api/chat endpoint # returns a friendly 503 in that case). Get a key from console.anthropic.com. ANTHROPIC_API_KEY= ANTHROPIC_MODEL=claude-sonnet-4-6 # Skip auth entirely in local dev. NEVER set true in production. DEV_AUTH_BYPASS=false # --- Server --- # Host port to bind the container to. Auto-picked by deploy.sh from 8200-8299 and persisted here. UTILISATION_DEPT_PORT=8200 # Optional Sentry-style overrides for cache TTLs (seconds). CACHE_TTL_RESOURCES=600 CACHE_TTL_BOOKINGS=60 CACHE_TTL_META=600 # --- Azure AD (unused in v1, present for v2 swap) --- AZURE_TENANT_ID= AZURE_CLIENT_ID=