# Environment of the hive-c0re daemon unit — a plain function file # (not a module) returning the env attrset, imported by ./default.nix. # Everything meta.rs forwards into agent containers or reads for the # meta-flake render is assembled here. { lib, config, pkgs, }: let cfg = config.services.hyperhive.c0re; in { # nix (the prebuild `nix build`, flake-check, and meta eval) writes # its fetcher/eval cache under $HOME/.cache. As a system user # hive-core has no home, so HOME defaults to the unwritable # /var/empty and Lix fails to initialise its cache. Point HOME at # the writable StateDirectory. HOME = "/var/lib/hyperhive"; HYPERHIVE_GIT = "${pkgs.git}/bin/git"; # Never let git block on an interactive credential prompt. hive-core is a # TTY-less system user, so a prompt (e.g. the forge credential helper # returns nothing because the forge isn't reachable yet on cold boot) # would hang forever — this is what froze the whole daemon during startup # migration's `nix flake lock` of the forge-hosted config inputs. With # this set, git fails fast instead of prompting. Paired with the git http # low-speed abort in `safeDirGitconfig` (bounds a stalled transfer) and # the 120s migration shellout timeout in migrate.rs. GIT_TERMINAL_PROMPT = "0"; # No HIVE_STATIC_DIR: the gateway static-serves the dashboard dist # (see the hive-gateway module); this router is API-only. # Path to the base agent frontend dist. hive-c0re's # gateway_nginx.rs uses this to generate split location # blocks in agents.conf — static HTML/CSS/JS served from the # nix store directly; dynamic API paths still proxied to the # agent daemon. The nix store is shared across nspawn # containers, so this path is reachable from inside the # gateway container's nginx. HIVE_AGENT_FRONTEND_DIR = "${cfg.servedFrontend}/agent"; # Path to the static runtime asset tree (branding + claude # prompts). `hive_sh4re::assets::*` reads paths underneath. # `forge.rs` reads the avatar PNGs from here on startup. HIVE_ASSETS_DIR = "${cfg.assets}/share/hyperhive"; # Whether this hive runs ruthless — no root/manager agent at all # (`auto_update::ensure_root_agent`). Default false = root # auto-managed; true makes the sweep a no-op. HYPERHIVE_RUTHLESS = lib.boolToString config.services.hyperhive.ruthless; } // { # Identity env vars threaded into c0re's own service env and # forwarded by meta.rs into every sub-agent's harness env — # full chain in docs/conventions.md::Hive identity. `domain` is # required (asserted in hive-network.nix), so it's always set. HYPERHIVE_HIVE_DOMAIN = config.services.hyperhive.domain; } // lib.optionalAttrs (config.services.hyperhive.hiveName != null) { HYPERHIVE_HIVE_NAME = config.services.hyperhive.hiveName; } // lib.optionalAttrs (config.services.hyperhive.swarmName != null) { HYPERHIVE_SWARM_NAME = config.services.hyperhive.swarmName; } // lib.optionalAttrs (!config.services.hyperhive.github.enable) { # GitHub integration is on by default; only signal the OFF override to # meta.rs, which then injects `hyperhive.github.enable = false` into # every agent. See services.hyperhive.github.enable. HYPERHIVE_GITHUB_DISABLED = "1"; } // lib.optionalAttrs config.services.hyperhive.otel.enable ( # Hive-wide OTEL config -> read by meta.rs::otel_config and # injected as build-time `hyperhive.otel.*` into every agent. # Endpoint presence is the enable signal on the meta side; the # optional fields are only emitted when set so absent values # don't render no-op env lines. let otel = config.services.hyperhive.otel; in { HYPERHIVE_OTEL_ENDPOINT = otel.endpoint; HYPERHIVE_OTEL_PROTOCOL = otel.protocol; } // lib.optionalAttrs (otel.extraResourceAttributes != "") { HYPERHIVE_OTEL_EXTRA_RESOURCE_ATTRIBUTES = otel.extraResourceAttributes; } // lib.optionalAttrs (otel.headersCredential != null) { HYPERHIVE_OTEL_HEADERS_CREDENTIAL = otel.headersCredential; } // lib.optionalAttrs (otel.metricIntervalMs != null) { HYPERHIVE_OTEL_METRIC_INTERVAL_MS = toString otel.metricIntervalMs; } // lib.optionalAttrs otel.debug { HYPERHIVE_OTEL_DEBUG = "1"; } ) // { # In-cluster forge URL — the gateway vhost (`forge.`), which # nginx proxies to forgejo. Used both for internal API calls in # hive-c0re (forge/mod.rs `forge_http_base()`) and forwarded to # agents via meta.rs for their forge-notify client. The forge is # mandatory, so this is unconditional (the whole env block is already # gated on hyperhive being enabled). See `docs/gateway.md::HIVE_FORGE_URL`. HIVE_FORGE_URL = "http://${config.services.hyperhive.forge.domain}"; } // lib.optionalAttrs config.services.hyperhive.matrix.enable { # In-cluster matrix homeserver URL for each agent's # hive-matrix-daemon — the gateway vhost (`matrix.`). The # gatewayHost null-guard falls back to loopback so a domain-less # config still evals. Forwarded to agents by meta.rs alongside # HIVE_FORGE_URL; shares the same env-forwarding ordering caveat # (value baked at config-generation time). HIVE_MATRIX_URL = if config.services.hyperhive.matrix.gatewayHost != null then "http://${config.services.hyperhive.matrix.gatewayHost}" else "http://127.0.0.1:${toString config.services.hyperhive.matrix.httpPort}"; } // lib.optionalAttrs config.services.hyperhive.matrix.gui.enable { # Availability flags read by the dashboard's `/api/state`. # Matrix GUI lives entirely on the gateway nginx (matrix tab # only shows when both flags are on). Gateway routing detail: # docs/gateway.md::Vhost map. HIVE_MATRIX_GUI_ENABLED = "1"; } // { # The gateway always runs, so the dashboard always builds # same-origin `/agent//` links (never the direct # `:` TCP fallback). Kept as an env flag so the # dashboard doesn't need to learn the gateway is unconditional. HIVE_GATEWAY_ENABLED = "1"; } // lib.optionalAttrs config.services.hyperhive.forge.behindGateway { # Public URL of the forge vhost served by hive-gateway. The # dashboard uses this to build browser-facing forge links # instead of hardcoding `:3000`, which breaks when # the operator accesses the dashboard through the gateway # (forge sub-domain has no port; direct port URL would be # wrong). Absent when `behindGateway = false` — dashboard # falls back to `:3000`. HIVE_FORGE_PUBLIC_URL = "https://${config.services.hyperhive.forge.domain}"; } // lib.optionalAttrs ( config.services.hyperhive.matrix.gui.enable && config.services.hyperhive.matrix.gatewayHost != null ) { # Browser-facing matrix GUI (fluffychat) URL — the gateway # vhost (`matrix.`). Surfaced via the daemon's `Urls` # request for `hivectl open matrix`. Absent when the GUI is off # or no gatewayHost is set (no browser-reachable matrix vhost). HIVE_MATRIX_PUBLIC_URL = "https://${config.services.hyperhive.matrix.gatewayHost}/"; } // lib.optionalAttrs (config.services.hyperhive.swarm.peers != { }) { # Peer hives serialised as a JSON array of {domain, cert_fingerprint, # wireguard_address?} objects. Consumed by hive-agent::identity::peers() # + the dashboard's peer_hives StateSnapshot field (P33RS tab). Domain # is the attrset key; cert_fingerprint is null for CA-trusted peers; # wireguard_address is omitted when not part of the mesh. HYPERHIVE_PEERS = builtins.toJSON ( lib.mapAttrsToList ( domain: p: { inherit domain; cert_fingerprint = p.certFingerprint; } // lib.optionalAttrs (p.wireguardAddress != null) { wireguard_address = p.wireguardAddress; } ) config.services.hyperhive.swarm.peers ); } // lib.optionalAttrs (lib.any (p: p.caCert != null) (lib.attrValues config.services.hyperhive.swarm.peers)) { # Peer-hive root CA file paths (colon-joined), one per peer that # declares `swarm.peers..caCert`. hive-c0re's meta-flake # renderer (meta.rs) embeds each next to every agent's flake and # adds it to `security.pki.certificateFiles`, so a peer CA is # trusted everywhere the hive's own internal CA (`hive-ca.pem`) # is — i.e. by every agent. The matrix container trusts the same # CAs separately for federation TLS. The `caCert` files are # copied into the nix store at build, so these are store paths — # nothing mutable lives on the host. HIVE_PEER_CA_PATHS = lib.concatStringsSep ":" ( lib.filter (c: c != null) ( lib.mapAttrsToList (_domain: p: p.caCert) config.services.hyperhive.swarm.peers ) ); }