# The swarm's message queue as every hive sees it: where it is reached and # what it is registered as, identical on every host. What the host running it # decides, and the container itself, are in ./swarm-nats.nix. { lib, config, ... }: let swarmDomain = config.services.hyperhive.swarm.domain; # Total on a null swarm domain, for the reason ./swarm-bao-service.nix gives. domainBase = if swarmDomain == null then "invalid" else swarmDomain; in { options.services.hyperhive.swarm.nats = { # `enable` is `services.hyperhive.deploy.nats.enable` — see # ./deploy.nix. What the queue IS to every hive: its domain, ports # and client id. # ⚠️ Deliberately NO `package` option for the NATS server, anywhere — # not here and not under `deploy.nats` either, where every other # service's build lives. `services.nats` upstream does not expose # one; it resolves `pkgs.nats-server` itself, so an option would be # ignored or need an overlay to mean anything, and an option that does # not control what it names is worse than its absence. Pin the build # with `nixpkgs.overlays` if you need to. (`deploy.nats.authPackage` # is a different thing: the callout responder, which IS ours.) domain = lib.mkOption { type = lib.types.str; default = "nats.${domainBase}"; defaultText = lib.literalExpression ''"nats.''${services.hyperhive.swarm.domain}"''; description = '' Name every client reaches the queue on, as `tls://:`, and the only name its certificate carries. A **sibling** of the swarm's other service names, for the reason {option}`services.hyperhive.swarm.bao.domain` gives. The host running the queue answers it through `gateway.localNames`. Every other hive resolves it through the operator's upstream DNS, which is where a multi-host swarm needs a record for it. Not in {option}`services.hyperhive.swarm.serviceDomains`. The gateway on the queue's host serves `https:///metrics` to the swarm collector, and in self-signed mode it serves the queue's own leaf there rather than the swarm-services one, so that host never waits for a services leaf (./swarm-nats.nix). ''; }; port = lib.mkOption { type = lib.types.port; default = 4222; description = '' TCP port the queue listens on. 4222 is upstream's default and sits outside hyperhive's claimed ranges (dashboard 7000, forge 3000, every agent in 8100..8999 via FNV-1a hash). Opened on the bridge interface, for agent containers, and on `wg-hive` when this host is on the mesh, for other hives. Never host-wide. TLS only: a client that does not speak it is refused. Unlike {option}`monitorPort` and {option}`metricsPort`, the address is not what bounds who may use this port: the queue's `auth_callout` refuses every client it cannot identify. ''; }; monitorPort = lib.mkOption { type = lib.types.port; default = 8222; description = '' Port NATS serves its **monitoring** endpoint on, bound to loopback. Not a metrics endpoint: the server has no Prometheus format of its own. This serves `/varz`, `/connz`, `/routez` as JSON, and the exporter below is what translates it — which is why enabling the exporter without this produces a process that starts cleanly and scrapes nothing. ⚠️ Loopback, and the exporter is the only intended reader. The endpoint is unauthenticated and `/connz` names every connected client, so the address it binds is the whole access control. Do not widen it, and do not put it behind a gateway vhost expecting that to add one. ''; }; metricsPort = lib.mkOption { type = lib.types.port; default = 7777; description = '' Port the Prometheus exporter serves NATS's metrics on, bound to loopback. The gateway's `= /metrics` location on {option}`services.hyperhive.swarm.nats.domain` proxies it, behind the swarm collector's authelia check. ⚠️ This and {option}`monitorPort` are two more claims on a port space every swarm container shares — they run in this container but `privateNetwork = false`, so a collision with any other hyperhive service is a runtime coin toss over which process gets the port, with nothing in any log saying so. Both defaults are upstream's own (`nats-server` 8222, `prometheus-nats-exporter` 7777) and neither is claimed elsewhere in this repo, checked when they were added. ''; }; otel.telemetryPort = lib.mkOption { type = lib.types.port; default = 8891; description = '' Loopback port the journal forwarder inside the queue's container serves its own metrics on (refused and dropped records, exporter failures). ⚠️ Not 8888, 8889 or 8890: the hive collector, the swarm collector and the store's forwarder bind those ({option}`services.hyperhive.otel.telemetryPort`, {option}`services.hyperhive.swarm.otel.telemetryPort`, {option}`services.hyperhive.swarm.bao.otel.telemetryPort`), and this container shares the host netns with them when co-located. The second collector to bind a port dies with `bind: address already in use`. ''; }; clientId = lib.mkOption { type = lib.types.str; default = "swarm-nats"; description = '' OAuth2 client id the queue's authentication path identifies itself with. The host running authelia registers it, whichever host runs the queue, and the queue's host reads its secret out of the swarm secret store. ''; }; }; }