host: vps services: node-agent: role: node-stability-monitor deployment_model: docker-compose exposure: local-only offline_required: true depends_on: local: [] external: [] runtime: config_path: /opt/homelab/config/node-agent data_path: /opt/homelab/state logs_path: /opt/homelab/events control-plane: role: management-and-orchestration deployment_model: docker-compose exposure: tailscale-internal offline_required: false depends_on: local: - node-agent external: - piha:redis ports: - name: http container_port: 18180 protocol: tcp runtime: config_path: /opt/homelab/config/control-plane data_path: /opt/homelab/data/control-plane logs_path: /opt/homelab/logs/control-plane node_exporter: role: metrics-exporter deployment_model: docker-compose exposure: local-only offline_required: true depends_on: local: [] external: [] fleet-prometheus: role: fleet-liveness-source deployment_model: docker-compose exposure: tailscale-internal offline_required: false depends_on: local: - node_exporter external: [] ports: - name: http container_port: 9090 protocol: tcp runtime: config_path: /opt/homelab/config/fleet-prometheus data_path: /opt/homelab/data/fleet-prometheus logs_path: /opt/homelab/logs/fleet-prometheus gokapi: role: public-file-share deployment_model: docker-compose exposure: public offline_required: false depends_on: local: [] external: [] ports: - name: http container_port: 53842 protocol: tcp runtime: config_path: /opt/homelab/config/gokapi data_path: /opt/homelab/data/gokapi logs_path: /opt/homelab/logs/gokapi stability-agent: role: node-watchdog # read-only docker.sock watchdog, emits filesystem events # Deploys via its own deploy-local.sh, outside the declarative pipeline # (recon B7) — entry added 2026-07 truth cleanup so desired state matches # what actually runs here. deployment_model: docker-compose exposure: local-only offline_required: true depends_on: local: [] external: [] runtime: data_path: /opt/homelab/state logs_path: /opt/homelab/events npm: role: reverse-proxy # public ingress on the VPS (separate from npm-piha) deployment_model: docker-compose exposure: public offline_required: false depends_on: local: [] external: [] ports: - name: http container_port: 80 protocol: tcp - name: https container_port: 443 protocol: tcp runtime: # Data stays at the pre-GitOps bind mounts (CLAUDE.md data migration rule): # /home/dockeruser/docker/npm/{data,letsencrypt} config_path: services/npm outline: # Runs on vps but has NO services/outline dir on master (recon F20.2) — the # containers predate GitOps management. Companion containers # outline-postgres-1 / outline-redis-1 are visible in world state but are # not separate desired-state entries. role: knowledge-base deployment_model: docker-compose exposure: public offline_required: false depends_on: local: [] external: [] joplin-server: # Keyed joplin-server (not "joplin") to match the compose service name the # world state tracks — an aggregate "joplin" entry would sit in # missing_service drift forever. No services/joplin dir on master (recon # F20.2). Companion joplin-db is tracked in world state, not desired here. role: notes-sync deployment_model: docker-compose exposure: public offline_required: false depends_on: local: [] external: [] umami: # Web analytics; compose-labelled but no services/umami dir in the repo — # pre-GitOps deployment (recon B5). Companion umami-db not desired here. role: web-analytics deployment_model: docker-compose exposure: public offline_required: false depends_on: local: [] external: [] # humanai-mailer / humanai-landing: hand-run containers with NO compose # labels (recon B5/B7). Documented here as unmanaged so the truth map is # complete. monitor: false keeps the supervisor from generating (dead-end # redeploy) actions for containers the pipeline cannot manage; `unmanaged` # itself is documentation only — no code reads it. humanai-mailer: unmanaged: true monitor: false role: mailer humanai-landing: unmanaged: true monitor: false role: landing-page # ai-cluster (openclaw, codex-worker, planner-worker, service-ops-worker, # redis, mosquitto): legacy stack, runs UNMANAGED on vps and is scheduled # for retirement — its codex/* bus has been idle since 2026-06-09. # Deliberately NO entry here: legacy is not pulled into desired state. # See kb/decisions/ai-cluster-legacy.md and # kb/subsystems/recon-multiagent.md (C9).