feat(#1930): move otel stats export to host-level services.hyperhive.otel

This commit is contained in:
damocles 2026-06-23 20:42:15 +02:00 committed by mara
commit 838cc9af9a
3 changed files with 303 additions and 38 deletions

View file

@ -174,23 +174,34 @@ in
visible = false;
};
# OTEL stats export is configured ONCE at host level via
# `services.hyperhive.otel.*` (see nix/modules/hive-c0re.nix) and
# injected into every agent's build by the meta-flake renderer
# (`hive-c0re/src/meta.rs::otel_config`). These per-agent options are
# the build-time implementation surface that injection writes into;
# they are not meant to be set directly in an agent.nix. Marked
# `internal` so the host option is the only documented operator knob.
options.hyperhive.otel = {
enable = lib.mkEnableOption ''
exporting this agent's Claude Code stats (token usage, cost, tool
calls) to an OTLP endpoint via Claude Code's built-in OpenTelemetry.
Each agent's harness exports its own stats directly to the collector,
so it keeps working even when hive-c0re is down. Meant to be enabled
hive-wide (one switch for every agent) - there is no per-agent
opt-in flag beyond this option
'';
enable = lib.mkOption {
type = lib.types.bool;
default = false;
internal = true;
description = ''
Export this agent's Claude Code stats (token usage, cost, tool
calls) to an OTLP endpoint via Claude Code's built-in
OpenTelemetry. Each agent's harness exports directly to the
collector, so it keeps working even when hive-c0re is down.
Host-driven: set `services.hyperhive.otel.enable` instead.
'';
};
endpoint = lib.mkOption {
type = lib.types.str;
default = "";
example = "https://collector.example.com/otel";
internal = true;
description = ''
OTLP collector endpoint, set as `OTEL_EXPORTER_OTLP_ENDPOINT`.
Required when `enable` is true.
Host-driven via `services.hyperhive.otel.endpoint`.
'';
};
@ -201,8 +212,10 @@ in
"grpc"
];
default = "http/protobuf";
internal = true;
description = ''
OTLP wire protocol, set as `OTEL_EXPORTER_OTLP_PROTOCOL`.
Host-driven via `services.hyperhive.otel.protocol`.
'';
};
@ -214,27 +227,27 @@ in
# is only ever read from disk by systemd at start, never nix-stored.
type = lib.types.nullOr lib.types.str;
default = null;
example = "/run/secrets/otel-headers";
internal = true;
description = ''
Absolute path to an operator-provided secret file whose contents
become `OTEL_EXPORTER_OTLP_HEADERS` (e.g.
`Authorization=Bearer <token>`). Loaded via systemd
`LoadCredential` into the unit-private credential store at
runtime, so the token is never copied into the nix store or
exposed in the process argv. Must be an absolute path (systemd
`LoadCredential` requires one). Leave null if the endpoint needs
no auth header.
exposed in the process argv. Host-driven via
`services.hyperhive.otel.headersCredential`.
'';
};
extraResourceAttributes = lib.mkOption {
type = lib.types.str;
default = "";
example = "deployment.environment=prod";
internal = true;
description = ''
Extra comma-separated entries appended to
`OTEL_RESOURCE_ATTRIBUTES` after the built-in
`service.name` / `agent` / `hive` / `swarm` labels.
Host-driven via `services.hyperhive.otel.extraResourceAttributes`.
'';
};
};
@ -787,11 +800,6 @@ in
'';
assertions = [
# OTEL export needs an endpoint to point at.
{
assertion = !config.hyperhive.otel.enable || config.hyperhive.otel.endpoint != "";
message = "hyperhive.otel.enable is true but hyperhive.otel.endpoint is empty.";
}
# Guard the inputs-routed-as-output pattern: the agent flake.nix is
# expected to set `_module.args.flakeInputs = builtins.removeAttrs inputs ["self"]`.
# If `self` leaks into flakeInputs the agent gets a spurious attrset