{ config, ... }: # Headplane — web UI for headscale (services/vpn/headscale.nix; must be enabled # first). Runs as headscale's own OS user so it can restart headscale when # settings change from the UI. # # Served at vpn.mgaction.town/admin (path-routed alongside headscale itself, # see hosts/neptun/configuration.nix). base_url is the site root WITHOUT the # /admin prefix — Headplane appends that itself, including for the OIDC # callback. # # Auth is Zitadel (services/identity/zitadel.nix) via OIDC. client_id, client_secret, # and the headscale API key can't be known until Zitadel/headscale are # actually deployed, so they're placeholders below; direct API-key login # still works as a fallback until then. Once live: # 1. In Zitadel: create a project + a Web application for Headplane, with # redirect URI https://vpn.mgaction.town/admin/oidc/callback. Copy the # generated client ID into oidc.client_id below. # 2. `./scripts/edit_secrets secrets/neptun.yaml` and replace # headplane_oidc_client_secret with the app's client secret. # 3. `headscale apikeys create` on the box, and replace # headplane_headscale_api_key the same way. { services.headplane = { enable = true; settings.server = { cookie_secret_path = config.sops.secrets.headplane_cookie_secret.path; cookie_secure = true; # served over HTTPS via Caddy base_url = "https://vpn.mgaction.town"; }; settings.oidc = { issuer = "https://auth.mgaction.town"; client_id = "REPLACE_ME_zitadel_client_id"; # not secret, but not known until the app exists in Zitadel client_secret_path = config.sops.secrets.headplane_oidc_client_secret.path; headscale_api_key_path = config.sops.secrets.headplane_headscale_api_key.path; }; }; }