# `swarm-logs` on this agent's PATH, configured for this swarm's log store. # # The binary reads its coordinates from the environment and defaults nothing # (see `swarm-logs/README.md`), so it is installed **wrapped** rather than # bare — the same shape `swarm-controller.nix` installs `swarmctl` in, and for # the same reason: every value here is derived from an option a module owns, # and a default inside the binary would be an address we hope points at # something. # # ⚠️ Gated on the queue being configured as well as on the query URL, because # the identity this CLI presents IS the queue's: one per-hive machine client, # one credential pair, delivered once. A swarm with a log store and no agent # credential has nothing for this to authenticate as, and installing a binary # that can only fail would be worse than leaving it off PATH — an agent would # read the 401 as "no logs", which is the exact confusion the store's machine # route was added to end. { lib, pkgs, config, ... }: let cfg = config.hyperhive.logs; queueCfg = config.hyperhive.queue; configured = cfg.queryUrl != null && queueCfg.tokenEndpoint != null; # The four coordinates `swarm-logs` reads, all-or-none on its side. Bound # here as one attrset so the wrapper below cannot set three of them: a # half-set environment is the failure the binary reports as a deployment # bug, and it should not be reachable from the module that sets it. # # 🩸 Two of these are credential **paths**. Neither the id nor the secret # is read here — a value in `--set` lands in the wrapper script, which is # in the world-readable nix store. wrapperEnv = { HIVE_AGENT_LOGS_QUERY_URL = cfg.queryUrl; HIVE_AGENT_OIDC_TOKEN_ENDPOINT = queueCfg.tokenEndpoint; HIVE_AGENT_OIDC_CLIENT_ID_FILE = queueCfg.clientIdFile; HIVE_AGENT_OIDC_CLIENT_SECRET_FILE = queueCfg.clientSecretFile; }; # Same shape `swarm-controller.nix` wraps `swarmctl` in — `symlinkJoin` + # `wrapProgram`, not a `writeShellScriptBin` shim, because the wrapper has # to leave the binary's `--help`, its exit status and its streams intact. # The whole point of the CLI is that its stdout pipes into `grep`. swarmLogsConfigured = pkgs.symlinkJoin { name = "swarm-logs-configured"; paths = [ config.hyperhive.packages.swarm-logs ]; nativeBuildInputs = [ pkgs.makeWrapper ]; postBuild = '' wrapProgram $out/bin/swarm-logs ${ lib.concatStringsSep " " ( lib.mapAttrsToList (name: value: "--set ${name} ${lib.escapeShellArg value}") wrapperEnv ) } ''; }; in { options.hyperhive.logs = { queryUrl = lib.mkOption { type = lib.types.nullOr lib.types.str; default = null; example = "https://logs.example.com/select/logsql/query"; description = '' The swarm log store's LogsQL query endpoint, as this container reaches it — the machine route `swarm-victorialogs.nix` puts on the gateway, not the browser one at `/`. Set by the generated meta flake from the host's own {option}`services.hyperhive.swarm.victorialogs.domain`. ⚠️ The **whole URL**, not a host to build one from, because this same string is also the audience `swarm-logs` mints its token for. Two spellings of one address present as a valid token refused at the store — the rule `swarm-otel.nix` already states over its own push targets. `null` means this hive has not been given the log store's address for its agents, and `swarm-logs` is then not installed at all. Deliberately not defaulted: a guessed address resolves cleanly to the wrong place. ''; }; }; config = lib.mkIf configured { environment.systemPackages = [ swarmLogsConfigured ]; }; }