name: mcp-vps-agent x-default-logging: &default-logging driver: json-file options: max-size: "${VPS_AGENT_LOG_MAX_SIZE:-10m}" max-file: "${VPS_AGENT_LOG_MAX_FILES:-5}" services: broker: build: context: . target: broker image: ${VPS_AGENT_BROKER_IMAGE:-mcp-vps-agent-broker:local} restart: unless-stopped logging: *default-logging user: "0:65532" privileged: true pid: host network_mode: host environment: VPS_AGENT_POLICY: /etc/vps-agent/policy.yaml VPS_AGENT_BROKER_SOCKET: /run/vps-agent/broker.sock VPS_AGENT_STATE_DB: /var/lib/vps-agent/state.db VPS_AGENT_ADMIN_TOKEN: ${VPS_AGENT_ADMIN_TOKEN:?set VPS_AGENT_ADMIN_TOKEN in .env} VPS_AGENT_OPERATOR_APPROVAL_TOKEN: ${VPS_AGENT_OPERATOR_APPROVAL_TOKEN:-} VPS_AGENT_OPERATOR_APPROVAL_SOCKET: ${VPS_AGENT_OPERATOR_APPROVAL_SOCKET:-} VPS_AGENT_OPERATOR_ID: ${PORTICO_OPERATOR_ID:-local-owner} VPS_AGENT_EXPECTED_SUBJECT: ${VPS_AGENT_SUBJECT:-operator} VPS_AGENT_HOST_ROOT: /host VPS_AGENT_HOST_NSENTER: "1" VPS_AGENT_PHYSICAL_SCOPE_ROOT: ${VPS_AGENT_SCOPE_ROOT:-/opt/my-app} VPS_AGENT_WHOLE_HOST: ${VPS_AGENT_WHOLE_HOST:-0} VPS_AGENT_INSTANCE_ID: ${VPS_AGENT_INSTANCE_ID:-} VPS_AGENT_INSTANCE_NAME: ${VPS_AGENT_INSTANCE_NAME:-vps-agent} VPS_AGENT_MAX_CONCURRENT_REQUESTS: ${VPS_AGENT_MAX_CONCURRENT_REQUESTS:-64} volumes: - type: bind source: ${VPS_AGENT_SCOPE_ROOT:-/opt/my-app} target: /host${VPS_AGENT_SCOPE_ROOT:-/opt/my-app} bind: propagation: rslave create_host_path: false - type: bind source: ./config/policy.yaml target: /etc/vps-agent/policy.yaml read_only: true - type: bind source: ./state target: /var/lib/vps-agent - broker-run:/run/vps-agent - operator-run:/run/portico-operator healthcheck: test: ["CMD", "/usr/local/bin/vps-agent", "audit-status", "--socket", "/run/vps-agent/broker.sock"] interval: 10s timeout: 5s retries: 6 start_period: 5s security_opt: - no-new-privileges:false gateway: build: context: . target: gateway image: ${VPS_AGENT_GATEWAY_IMAGE:-mcp-vps-agent-gateway:local} restart: unless-stopped logging: *default-logging depends_on: broker: condition: service_healthy environment: VPS_AGENT_BROKER_SOCKET: /run/vps-agent/broker.sock VPS_AGENT_LISTEN: 0.0.0.0:8080 VPS_AGENT_AUTH_MODE: ${VPS_AGENT_AUTH_MODE:-static} VPS_AGENT_STATIC_TOKEN: ${VPS_AGENT_STATIC_TOKEN:-} VPS_AGENT_STATIC_SUBJECT: ${VPS_AGENT_SUBJECT:-operator} VPS_AGENT_OIDC_ISSUER: ${VPS_AGENT_OIDC_ISSUER:-} VPS_AGENT_INTEGRATED_INTROSPECTION_URL: ${VPS_AGENT_INTEGRATED_INTROSPECTION_URL:-} VPS_AGENT_INTEGRATED_INTROSPECTION_HOST: ${VPS_AGENT_INTEGRATED_INTROSPECTION_HOST:-} VPS_AGENT_INTEGRATED_INTROSPECTION_CLIENT_ID: ${VPS_AGENT_INTEGRATED_INTROSPECTION_CLIENT_ID:-} VPS_AGENT_INTEGRATED_INTROSPECTION_CLIENT_SECRET: ${VPS_AGENT_INTEGRATED_INTROSPECTION_CLIENT_SECRET:-} VPS_AGENT_INTEGRATED_AUDIENCE_PROJECT_ID: ${VPS_AGENT_INTEGRATED_AUDIENCE_PROJECT_ID:-} VPS_AGENT_REQUIRED_SCOPES: ${VPS_AGENT_REQUIRED_SCOPES:-} VPS_AGENT_PUBLIC_URL: ${VPS_AGENT_PUBLIC_URL:-} VPS_AGENT_OPERATOR_PORTAL_URL: ${VPS_AGENT_OPERATOR_PORTAL_URL:-} VPS_AGENT_MCP_APPS: ${VPS_AGENT_MCP_APPS:-0} PORTICO_APPROVAL_SURFACE: ${PORTICO_APPROVAL_SURFACE:-external} PORTICO_OPERATOR_FRAME_ANCESTORS: ${PORTICO_OPERATOR_FRAME_ANCESTORS:-} VPS_AGENT_OAUTH_RESOURCE: ${VPS_AGENT_OAUTH_RESOURCE:-} VPS_AGENT_RESOURCE_METADATA_URL: ${VPS_AGENT_RESOURCE_METADATA_URL:-} VPS_AGENT_INSTANCE_ID: ${VPS_AGENT_INSTANCE_ID:-} VPS_AGENT_INSTANCE_NAME: ${VPS_AGENT_INSTANCE_NAME:-vps-agent} VPS_AGENT_LANG: ${VPS_AGENT_LANG:-en} volumes: - broker-run:/run/vps-agent ports: - "${VPS_AGENT_BIND_ADDRESS:-127.0.0.1}:${VPS_AGENT_PORT:-8080}:8080" healthcheck: test: ["CMD", "curl", "-fsS", "http://127.0.0.1:8080/healthz"] interval: 10s timeout: 5s retries: 6 start_period: 5s read_only: true tmpfs: - /tmp:size=16m,mode=1777 security_opt: - no-new-privileges:true cap_drop: - ALL operator-portal: profiles: ["operator-portal"] image: python:3.12-alpine working_dir: /srv/portico entrypoint: ["python3", "web/operator-approval/server.py"] user: "65532:65532" read_only: true restart: unless-stopped security_opt: - no-new-privileges:true cap_drop: - ALL pids_limit: 80 mem_limit: 128m environment: PORTICO_OPERATOR_CONTAINER: "1" PORTICO_OPERATOR_SOCKET: /run/portico-operator/operator.sock PORTICO_OPERATOR_APPROVAL_TOKEN: ${VPS_AGENT_OPERATOR_APPROVAL_TOKEN:-} PORTICO_OPERATOR_PASSWORD_SCRYPT: ${PORTICO_OPERATOR_PASSWORD_SCRYPT:-} PORTICO_OPERATOR_PUBLIC_ORIGIN: ${PORTICO_OPERATOR_PUBLIC_ORIGIN:-} PORTICO_OPERATOR_NODE_LABEL: ${VPS_AGENT_INSTANCE_NAME:-vps-agent} PORTICO_OPERATOR_PHYSICAL_CEILING: ${VPS_AGENT_SCOPE_ROOT:-/opt/my-app} PORTICO_OPERATOR_ID: ${PORTICO_OPERATOR_ID:-local-owner} PORTICO_OPERATOR_NODE_ID: ${VPS_AGENT_INSTANCE_ID:-} PORTICO_OPERATOR_FRAME_ANCESTORS: ${PORTICO_OPERATOR_FRAME_ANCESTORS:-} volumes: - operator-run:/run/portico-operator - ./web/operator-approval:/srv/portico/web/operator-approval:ro - ./scripts/operator-approvals.py:/srv/portico/scripts/operator-approvals.py:ro ports: - "127.0.0.1:8765:8765" depends_on: broker: condition: service_healthy volumes: broker-run: operator-run: