From 1fc267b880cfb2731610a3bc3e38f7bc06a0d10a Mon Sep 17 00:00:00 2001 From: atlas Date: Sat, 15 Aug 2026 09:33:11 +0200 Subject: [PATCH] feat(otel): the collector's upstream exporter honours otel.protocol The module hardcoded otlphttp, so a hive with protocol = "grpc" would have had its agents' protocol respected end to end before the collector and silently rewritten to HTTP after it. Splitting the path in two makes the upstream half the one that has to keep honouring the option; the agent half is pinned to the receiver's protocol and derived, not configured. --- nix/host-modules/otel.nix | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/nix/host-modules/otel.nix b/nix/host-modules/otel.nix index 66d89855..75e3ec49 100644 --- a/nix/host-modules/otel.nix +++ b/nix/host-modules/otel.nix @@ -181,6 +181,23 @@ let otel = config.services.hyperhive.otel; listen = "${config.services.hyperhive.network.bridgeIp}:${toString otel.collector.port}"; + # `otel.protocol` describes the UPSTREAM link and always did. + # Inserting a collector splits the path in two, and the + # upstream half is the one that has to keep honouring it — so + # the exporter is chosen by it, rather than the option quietly + # becoming "how agents talk to the collector". The agent half + # is pinned to OTLP/HTTP by the receiver below (derived in + # hive-c0re/environment.nix). + grpcUpstream = otel.protocol == "grpc"; + upstreamName = if grpcUpstream then "otlp" else "otlphttp"; + upstream = { + endpoint = otel.endpoint; + # The value is interpolated by the collector at runtime from + # its environment, never by nix. `EnvironmentFile` below is + # what puts it there. + headers.${otel.collector.upstreamHeaderName} = "\${env:${otel.collector.upstreamHeaderName}}"; + } + // lib.optionalAttrs (otel.protocol == "http/json") { encoding = "json"; }; in { assertions = [ @@ -215,16 +232,10 @@ validateConfigFile = true; settings = { receivers.otlp.protocols.http.endpoint = listen; - exporters.otlphttp = { - endpoint = otel.endpoint; - # The value is interpolated by the collector at runtime - # from its environment, never by nix. `EnvironmentFile` - # below is what puts it there. - headers.${otel.collector.upstreamHeaderName} = "\${env:${otel.collector.upstreamHeaderName}}"; - }; + exporters.${upstreamName} = upstream; service.pipelines.metrics = { receivers = [ "otlp" ]; - exporters = [ "otlphttp" ]; + exporters = [ upstreamName ]; }; }; };