diff --git a/nix/host-modules/hive-gateway/default.nix b/nix/host-modules/hive-gateway/default.nix index 13df618c..c15f5816 100644 --- a/nix/host-modules/hive-gateway/default.nix +++ b/nix/host-modules/hive-gateway/default.nix @@ -289,31 +289,20 @@ in # Import the hive-CA leaf into nginx's state dir before nginx starts. # - # 🚨 DO NOT "simplify" this into pointing nginx at the CA dir. It - # does TWO jobs: + # 🚨 DO NOT "simplify" this into pointing nginx at the CA dir. It does + # TWO jobs: it re-modes the leaf to the 0640 root:nginx copy below (a + # 0600 key fails nginx's pre-start config test and blocks the unit), + # and it guarantees every cert path the config names exists — which is + # what the swarm-services fallback at the bottom of the script is for. + # A leaf that never issues therefore takes the WHOLE gateway down, not + # one vhost. Both mechanisms, and what it cost the time this was + # collapsed: `docs/networking/gateway.md::Self-signed TLS (default)`. # - # (1) It re-modes the leaf. `hive-tls-ca` writes the key 0600 - # root:root; nginx's pre-start `nginx -t` runs as the nginx - # *user*, so a 0600 key fails the config test with - # `BIO_new_file() … Permission denied` and blocks the unit — - # hence the 0640 root:nginx copy below. - # (2) It guarantees that **every cert path the nginx config names - # exists** — which is what the swarm-services fallback at the - # bottom of the script is for. nginx refuses to load a config - # naming a missing cert file, so a leaf that never issues takes - # the whole gateway down rather than one vhost; that has already - # happened once and it took the forge, dashboard and matrix with - # it. Removing this unit re-creates it exactly. - # - # nginx - # `Requires=` this via `requiredBy`, so it refuses to start until - # the copy succeeds. ALWAYS runs (no ConditionPathExists) and is - # idempotent — necessary to reconcile broken state from prior - # failed boots (a 0700 dir from a stale UMask, a truncated copy - # from an interrupted oneshot, etc.). The leaf covers the bare - # hive domain plus `forge.`, `matrix.` and `*.${hyperhiveDomain}` - # so all sub-domains validate under the same cert + the hive CA. - # See `docs/networking/gateway.md` ("Self-signed TLS"). + # ALWAYS runs (no ConditionPathExists) and is idempotent, because it + # has to reconcile broken state from earlier failed boots — a 0700 dir + # from a stale UMask, a truncated copy from an interrupted oneshot. + # nginx `Requires=` it via `requiredBy`, so nginx refuses to start + # until the copy succeeds. systemd.services.hive-gateway-self-signed-cert = lib.mkIf useSelfSigned { description = "Import host-generated TLS leaf for hive-gateway"; wantedBy = [ "multi-user.target" ]; @@ -383,14 +372,11 @@ in ''; }; - # nginx reload is triggered from the HOST side by hive-c0re - # after each agents.conf write, through hive-priv (c0re is - # unprivileged and cannot act on a system unit). - # - # It stays an explicit trigger rather than a systemd path unit - # watching the file: the write and the reload belong in one causal - # chain c0re can retry and report on (see RELOAD_PENDING), not two - # independent units racing on an inotify event. + # Nothing here reloads nginx: hive-c0re drives that from the host side + # through hive-priv after each agents.conf write, deliberately as an + # explicit trigger rather than a path unit. Why, and what hive-priv + # dispatches per unit state: + # `docs/networking/gateway.md::Per-agent unix-socket upstream`. services.nginx = { enable = true;