services: # Load balancer for multiple controller pods controller-lb: image: nginx:alpine ports: - "${CONTROLLER_SERVICE_PORT:-5000}:80" volumes: - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro networks: - vc_auth depends_on: - controller restart: unless-stopped controller: image: acapy-vc-authn-oidc-controller entrypoint: /bin/bash command: > -c " if [ $DEBUGGER ] && [ "$DEBUGGER" == "true" ]; then echo 'Starting in debug mode...' pip install debugpy -t /tmp && \ python /tmp/debugpy --wait-for-client --listen 0.0.0.0:5678 -m uvicorn api.main:app --reload --host 0.0.0.0 --port 5000; else echo 'Starting in production mode...' uvicorn api.main:app --reload --host 0.0.0.0 --port 5000; fi" environment: - DEBUGGER=${DEBUGGER} - LOG_LEVEL=${LOG_LEVEL} - LOG_WITH_JSON=${LOG_WITH_JSON} - DB_HOST=${MONGODB_HOST} - DB_PORT=${MONGODB_PORT} - DB_NAME=${MONGODB_NAME} - OIDC_CONTROLLER_DB_USER=${OIDC_CONTROLLER_DB_USER} - OIDC_CONTROLLER_DB_USER_PWD=${OIDC_CONTROLLER_DB_USER_PWD} - CONTROLLER_URL=${CONTROLLER_URL} - CONTROLLER_WEB_HOOK_URL=${CONTROLLER_WEB_HOOK_URL} - CONTROLLER_CAMERA_REDIRECT_URL=${CONTROLLER_CAMERA_REDIRECT_URL} - CONTROLLER_PRESENTATION_EXPIRE_TIME=${CONTROLLER_PRESENTATION_EXPIRE_TIME} - CONTROLLER_PRESENTATION_CLEANUP_TIME=${CONTROLLER_PRESENTATION_CLEANUP_TIME} - CONTROLLER_CLEANUP_MAX_PRESENTATION_RECORDS=${CONTROLLER_CLEANUP_MAX_PRESENTATION_RECORDS} - CONTROLLER_CLEANUP_MAX_CONNECTIONS=${CONTROLLER_CLEANUP_MAX_CONNECTIONS} - CONTROLLER_SESSION_TIMEOUT_CONFIG_FILE=${CONTROLLER_SESSION_TIMEOUT_CONFIG_FILE} - CONTROLLER_PRESENTATION_RECORD_RETENTION_HOURS=${CONTROLLER_PRESENTATION_RECORD_RETENTION_HOURS} - CONTROLLER_VARIABLE_SUBSTITUTION_OVERRIDE=${CONTROLLER_VARIABLE_SUBSTITUTION_OVERRIDE} - CONTROLLER_TEMPLATE_DIR=${CONTROLLER_TEMPLATE_DIR} - ACAPY_PROOF_FORMAT=${ACAPY_PROOF_FORMAT} - ACAPY_TENANCY=${AGENT_TENANT_MODE} - ACAPY_AGENT_URL=${AGENT_ENDPOINT} - ACAPY_ADMIN_URL=${AGENT_ADMIN_URL} # Unified Tenant / Wallet Configuration - ACAPY_TENANT_WALLET_ID=${ACAPY_TENANT_WALLET_ID} - ACAPY_TENANT_WALLET_KEY=${ACAPY_TENANT_WALLET_KEY} - ACAPY_TOKEN_CACHE_TTL=3600 # OIDC UserInfo Configuration - CONTROLLER_ENABLE_USERINFO_ENDPOINT=${CONTROLLER_ENABLE_USERINFO_ENDPOINT:-false} - OIDC_ACCESS_TOKEN_TTL=${OIDC_ACCESS_TOKEN_TTL:-3600} # Legacy variables (passed for backward compatibility) - MT_ACAPY_WALLET_ID=${MT_ACAPY_WALLET_ID} - MT_ACAPY_WALLET_KEY=${MT_ACAPY_WALLET_KEY} - ST_ACAPY_ADMIN_API_KEY=${AGENT_ADMIN_API_KEY} - ST_ACAPY_ADMIN_API_KEY_NAME=${ST_ACAPY_ADMIN_API_KEY_NAME} - USE_OOB_LOCAL_DID_SERVICE=${USE_OOB_LOCAL_DID_SERVICE} - USE_CONNECTION_BASED_VERIFICATION=${USE_CONNECTION_BASED_VERIFICATION} - WALLET_DEEP_LINK_PREFIX=${WALLET_DEEP_LINK_PREFIX} - INVITATION_LABEL=${INVITATION_LABEL} - REDIS_MODE=${REDIS_MODE:-single} - REDIS_HOST=${REDIS_HOST} - REDIS_PASSWORD=${REDIS_PASSWORD} - REDIS_DB=${REDIS_DB} - REDIS_SENTINEL_MASTER_NAME=${REDIS_SENTINEL_MASTER_NAME:-mymaster} # Expose debugger ports as a range ports: - "5678-5688:5678" # Remove direct port exposure - goes through load balancer expose: - "5000" deploy: replicas: ${CONTROLLER_REPLICAS:-3} volumes: - ../oidc-controller:/app/src:rw - ./oidc-controller/config/user_variable_substitution.py:/app/controller-config/user_variable_substitution.py - ./oidc-controller/config/sessiontimeout.json:/app/controller-config/sessiontimeout.json - ../html-templates:/app/controller-config/templates:rw networks: - vc_auth depends_on: redis: condition: service_healthy healthcheck: test: ["CMD", "curl", "-f", "localhost:5000/health"] interval: 10s timeout: 3s retries: 3 redis: image: redis:8-alpine ports: - "${REDIS_PORT:-6379}:6379" volumes: - redis-data:/data networks: - vc_auth restart: unless-stopped command: redis-server --appendonly yes healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 10s timeout: 3s retries: 3 controller-db: image: mongo:8.3 environment: - MONGO_INITDB_ROOT_USERNAME=${OIDC_CONTROLLER_DB_USER} - MONGO_INITDB_ROOT_PASSWORD=${OIDC_CONTROLLER_DB_USER_PWD} - MONGO_INITDB_DATABASE=${MONGODB_NAME} volumes: - controller-db-data:/data/db - ./mongo/mongo-init.js:/docker-entrypoint-initdb.d/mongo-init.js:ro ports: - ${MONGODB_PORT}:27017 restart: unless-stopped networks: - vc_auth keycloak: image: quay.io/keycloak/keycloak:26.6.3 command: start-dev --import-realm ports: - 8880:8080 volumes: - ./keycloak/config:/opt/keycloak/data/import environment: KC_DB: ${KEYCLOAK_DB_VENDOR} KC_DB_URL: jdbc:postgresql://${KEYCLOAK_DB_ADDR}/keycloak KC_DB_USERNAME: ${KEYCLOAK_DB_USER} KC_DB_PASSWORD: ${KEYCLOAK_DB_PASSWORD} KC_BOOTSTRAP_ADMIN_USERNAME: ${KEYCLOAK_USER} KC_BOOTSTRAP_ADMIN_PASSWORD: ${KEYCLOAK_PASSWORD} KC_HTTP_ENABLED: true KC_HTTP_RELATIVE_PATH: /auth depends_on: - keycloak-db networks: - vc_auth keycloak-db: image: postgres:17-alpine environment: - POSTGRES_USER=${KEYCLOAK_DB_USER} - POSTGRES_PASSWORD=${KEYCLOAK_DB_PASSWORD} - POSTGRES_DB=${KEYCLOAK_DB_NAME} volumes: - keycloak-db-data:/var/lib/pgsql/data networks: - vc_auth aca-py: build: context: ./agent dockerfile: Dockerfile image: vc-authn-oidc-acapy:webvh environment: - ACAPY_LABEL=${AGENT_NAME} - ACAPY_ENDPOINT=${AGENT_ENDPOINT} - ACAPY_WALLET_NAME=oidc_agent_wallet - ACAPY_WALLET_TYPE=askar-anoncreds - ACAPY_WALLET_KEY=${WALLET_ENCRYPTION_KEY} - ACAPY_WALLET_SEED=${AGENT_WALLET_SEED} - ACAPY_WALLET_LOCAL_DID=true - ACAPY_AUTO_VERIFY_PRESENTATION=true - ACAPY_AUTO_RESPOND_CREDENTIAL_OFFER=true - ACAPY_AUTO_STORE_CREDENTIAL=true - ACAPY_AUTO_RESPOND_PRESENTATION_REQUEST=true - ACAPY_WALLET_STORAGE_TYPE=${WALLET_TYPE} - ACAPY_READ_ONLY_LEDGER=true - ACAPY_GENESIS_TRANSACTIONS_LIST=/tmp/ledgers.yaml - ACAPY_LOG_LEVEL=info - ACAPY_WEBHOOK_URL=${CONTROLLER_WEB_HOOK_URL} - ACAPY_AUTO_PROVISION=true - ACAPY_PRESERVE_EXCHANGE_RECORDS=false - ACAPY_DEBUG_WEBHOOKS=true - POSTGRESQL_WALLET_HOST=${POSTGRESQL_WALLET_HOST} - POSTGRESQL_WALLET_PORT=${POSTGRESQL_WALLET_PORT} - POSTGRESQL_WALLET_USER=${POSTGRESQL_WALLET_USER} - POSTGRESQL_WALLET_PASSWORD=${POSTGRESQL_WALLET_PASSWORD} - ACAPY_PLUGIN_CONFIG=/tmp/plugin-config.yml ports: - ${AGENT_ADMIN_PORT}:${AGENT_ADMIN_PORT} - ${AGENT_HTTP_PORT}:${AGENT_HTTP_PORT} networks: - vc_auth volumes: - ./agent/config/ledgers.yaml:/tmp/ledgers.yaml - ./agent/config/plugin-config.yaml:/tmp/plugin-config.yml healthcheck: test: ["CMD", "curl", "-f", "http://localhost:${AGENT_ADMIN_PORT:-8077}/status/ready", "-H", "X-API-KEY: ${AGENT_ADMIN_API_KEY:-}"] interval: 10s timeout: 5s retries: 10 start_period: 30s depends_on: - wallet-db entrypoint: /bin/bash command: [ "-c", 'sleep 15; aca-py start --plugin webvh --inbound-transport http ''0.0.0.0'' ${AGENT_HTTP_PORT} --outbound-transport http --wallet-storage-config ''{"url":"${POSTGRESQL_WALLET_HOST}:${POSTGRESQL_WALLET_PORT}","max_connections":5}'' --wallet-storage-creds ''{"account":"${POSTGRESQL_WALLET_USER}","password":"${POSTGRESQL_WALLET_PASSWORD}","admin_account":"${POSTGRESQL_WALLET_USER}","admin_password":"${POSTGRESQL_WALLET_PASSWORD}"}'' --admin ''0.0.0.0'' ${AGENT_ADMIN_PORT} --${AGENT_ADMIN_MODE}', ] wallet-db: image: postgres:17-alpine environment: - POSTGRES_USER=${POSTGRESQL_WALLET_USER} - POSTGRES_PASSWORD=${POSTGRESQL_WALLET_PASSWORD} - POSTGRES_DB=${POSTGRESQL_WALLET_DATABASE} networks: - vc_auth ports: - 5433:5432 volumes: - agent-wallet-db:/var/lib/pgsql/data networks: vc_auth: driver: bridge volumes: controller-db-data: keycloak-db-data: agent-wallet-db: redis-data: