# Phase 2A full-stack local dev/test — Postgres + egress-proxy + Provisioner + a persistent # "identity" instance + the Gateway, all on one machine (the documented Phase 1/2A dev # exception; production is the two-VM Proxmox split, see docs/LAB_ARCHITECTURE.md §1). # # "identity" is a plain, non-ephemeral instance of the existing, unmodified rootnode-academy # image — it's what holds the canonical roster/curriculum/progress SQLite and serves the # platform-shell frontend. It is NOT managed by the Provisioner and never gets destroyed/reset; # it's infrastructure, the same way Postgres or the egress proxy are. See # docs/PHASE2A_GATEWAY_AND_STUDENT_FLOW.md for why this role exists and its Phase 2B # optimization opportunity (it currently boots a full unused k3s cluster just to serve as the # identity backend — wasteful but zero-new-code, matching "don't redesign the Lab image"). # # gateway and provisioner both run with network_mode: host — both need to reach ephemeral # per-student Lab containers by their internal Docker-network IP, the same requirement that # already applies to the Provisioner's own firewall management (see # platform/provisioner/docker-compose.dev.yml). services: postgres: image: postgres:16-alpine environment: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres POSTGRES_DB: rootnode_platform ports: - "5433:5432" healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres"] interval: 5s timeout: 3s retries: 10 egress-proxy: build: ./egress-proxy network_mode: host provisioner: build: ./provisioner network_mode: host cap_add: - NET_ADMIN - NET_RAW volumes: - /var/run/docker.sock:/var/run/docker.sock environment: PLATFORM_DB_URL: postgres://postgres:postgres@localhost:5433/rootnode_platform PROVISIONER_SHARED_SECRET: ${PROVISIONER_SHARED_SECRET:?set a real secret, do not use a default} PROVISIONER_PORT: "4100" LAB_IMAGE_REF: ${LAB_IMAGE_REF:-rootnode-academy:test} EGRESS_PROXY_PORT: "3128" DNS_RESOLVER: "127.0.0.11" NO_PROXY_CIDRS: "10.42.0.0/16,10.43.0.0/16" depends_on: postgres: condition: service_healthy identity: image: ${LAB_IMAGE_REF:-rootnode-academy:test} privileged: true network_mode: bridge ports: - "7554:80" volumes: - identity-data:/data gateway: build: ./gateway network_mode: host environment: GATEWAY_PORT: "4200" PROVISIONER_URL: "http://localhost:4100" PROVISIONER_SHARED_SECRET: ${PROVISIONER_SHARED_SECRET:?set a real secret, do not use a default} IDENTITY_BACKEND_URL: "http://localhost:7554" depends_on: - provisioner - identity volumes: identity-data: