# Glue: where the store and one of its readers share a host, the reader waits # for the unit that writes the cert-auth role it logs in with, and for any # other policy unit whose rules it relies on. # # ONE PAIRING PER FILE — the store's policy units ← the readers that rely # on them, and nothing else. Deleting this leaves every reader as it is on a # host whose store is remote: it may log in before its role exists, and its own # retries are what carry it past that. # # ⚠️ Gated on BOTH the store and that reader being here. Off the store's host # there is no local policy unit to order against. On the store's host without # the reader, setting `systemd.services..after` would define a unit # with no ExecStart, so each gate below restates the one the reader's own # module puts on it. A reader whose gate changes must change here too. # # Ordering, never a requirement: a policy unit that failed still counts as # done, and the reader's own retries carry it past that. `wants` as well as # `after`, so a reader started on its own pulls its policy unit into the same # transaction. { lib, config, ... }: let hyperhiveCfg = config.services.hyperhive; deployCfg = hyperhiveCfg.deploy; baoDeploy = deployCfg.bao; havePair = cert: key: cert != null && key != null; # Reader unit → the gate its own module defines it under. readersHere = { # ./glue-queue-agent-credential.nix swarm-bao-queue-agent = deployCfg.hive-controller.enable && havePair baoDeploy.queueAgentClientCertFile baoDeploy.queueAgentClientKeyFile; # ./swarm-grafana.nix swarm-bao-grafana-oidc = deployCfg.grafana.enable; # ./swarm-otel.nix swarm-bao-otel-oidc = deployCfg.swarm-otel.enable && havePair baoDeploy.otelOidcClientCertFile baoDeploy.otelOidcClientKeyFile; # ./swarm-bao.nix: its block is gated on the store, which `orderAfterPolicies` # already checks. swarm-bao-forwarder-oidc = true; # ./swarm-nats.nix: the queue's TLS leaf, not a secret, but the same wait. swarm-bao-nats-tls = deployCfg.nats.enable; # ./swarm-ci-runner.nix swarm-bao-ci-runner = deployCfg.ci-runner.enable; }; # Readers that log in under another unit's principal: reader unit → the # gate its own module defines it under, and the policy units it waits for. readersOfOtherPolicy = { # ./swarm-nats.nix, under the queue responder's leaf. Also after the # publisher's policy, which denies the publisher the callout seeds' paths: # until it is written the publisher may create them, and this unit keeps # a seed it finds there. swarm-bao-nats-callout-xkey = { here = deployCfg.nats.enable; policies = [ "swarm-bao-nats-auth-policy" "swarm-bao-secret-publisher-policy" ]; }; # ./swarm-grafana.nix, under Grafana's leaf, which may create the admin # password, the metrics bearer and the controller's Grafana token only # once that policy is written. swarm-bao-grafana-admin = { here = deployCfg.grafana.enable; policies = [ "swarm-bao-grafana-oidc-policy" ]; }; swarm-bao-grafana-metrics-bearer = { here = deployCfg.grafana.enable; policies = [ "swarm-bao-grafana-oidc-policy" ]; }; swarm-bao-grafana-controller-token = { here = deployCfg.grafana.enable; policies = [ "swarm-bao-grafana-oidc-policy" ]; }; # ./hive-matrix.nix, under matrix-ctl's leaf. swarm-bao-matrix-oidc = { here = deployCfg.matrix.enable; policies = [ "swarm-bao-matrix-ctl-policy" ]; }; # ./hive-forge/default.nix, under the forge publisher's leaf. swarm-bao-forge-oidc = { here = deployCfg.forgejo.enable; policies = [ "swarm-bao-forge-publisher-policy" ]; }; }; orderAfterPolicies = reader: here: policies: lib.mkIf (baoDeploy.enable && here) { systemd.services.${reader} = { after = map (p: "${p}.service") policies; wants = map (p: "${p}.service") policies; }; }; in { config = lib.mkMerge ( lib.mapAttrsToList (reader: here: orderAfterPolicies reader here [ "${reader}-policy" ]) readersHere ++ lib.mapAttrsToList (reader: r: orderAfterPolicies reader r.here r.policies) readersOfOtherPolicy ); }