fix(#2172): gate CLAUDE_CODE_OTEL_DIAG_STDERR on hyperhive.otel.debug

CLAUDE_CODE_OTEL_DIAG_STDERR was always set when OTEL is enabled, making
OTEL SDK errors (e.g. 404 from a misconfigured collector endpoint) appear
in every agent's stderr unconditionally. Move it behind a new opt-in flag.

Changes:
- nix/modules/hive-c0re.nix: add services.hyperhive.otel.debug (bool,
  default false); wire to HYPERHIVE_OTEL_DEBUG env on hive-c0re unit.
- hive-c0re/src/meta.rs: add debug field to OtelConfig; read
  HYPERHIVE_OTEL_DEBUG; emit hyperhive.otel.debug = true when set.
- nix/templates/harness-base.nix: add hyperhive.otel.debug internal
  option; move CLAUDE_CODE_OTEL_DIAG_STDERR out of otelSettingsEnv into
  a debug-gated lib.optionalAttrs block.

Default behaviour: OTEL exports silently (no stderr noise). Operators
troubleshooting collector connectivity set services.hyperhive.otel.debug
= true to re-enable the diagnostic output.
This commit is contained in:
atlas 2026-07-03 22:10:22 +02:00 committed by mara
commit cb0a66147a
3 changed files with 42 additions and 3 deletions

View file

@ -666,6 +666,9 @@ struct OtelConfig {
extra_resource_attributes: Option<String>,
headers_credential: Option<String>,
metric_interval_ms: Option<u64>,
/// `HYPERHIVE_OTEL_DEBUG=1` → `hyperhive.otel.debug = true` →
/// `CLAUDE_CODE_OTEL_DIAG_STDERR=1` in every agent's env.
debug: bool,
}
/// Read the hive-wide OTEL config from env, or `None` when OTEL is off.
@ -692,12 +695,16 @@ fn otel_config() -> Option<OtelConfig> {
.ok()
.and_then(|v| v.parse::<u64>().ok())
.filter(|v| *v > 0);
let debug = std::env::var("HYPERHIVE_OTEL_DEBUG")
.ok()
.is_some_and(|v| v == "1");
Some(OtelConfig {
endpoint,
protocol,
extra_resource_attributes,
headers_credential,
metric_interval_ms,
debug,
})
}
@ -982,6 +989,9 @@ where
// parsed u64, so it can't inject anything into the rendered nix.
let _ = writeln!(out, " hyperhive.otel.metricIntervalMs = {ms};");
}
if otel.debug {
out.push_str(" hyperhive.otel.debug = true;\n");
}
}
out.push_str(
r#" # The harness service inside the container runs as a

View file

@ -279,6 +279,18 @@ in
'';
};
debug = lib.mkOption {
type = lib.types.bool;
default = false;
description = ''
Emit OTEL SDK diagnostic messages to every agent's stderr by
setting `CLAUDE_CODE_OTEL_DIAG_STDERR=1`. Useful when
troubleshooting collector connectivity or endpoint config;
leave off in normal operation to avoid noise in agent logs.
Only meaningful when `enable` is true.
'';
};
metricIntervalMs = lib.mkOption {
type = lib.types.nullOr lib.types.ints.positive;
default = null;
@ -923,6 +935,9 @@ in
// 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.<domain>`), which

View file

@ -67,8 +67,6 @@ let
CLAUDE_CODE_ENABLE_TELEMETRY = "1";
# Attach feedback-survey data to the OTEL pipeline.
CLAUDE_CODE_ENABLE_FEEDBACK_SURVEY_FOR_OTEL = "1";
# Emit OTEL SDK diagnostics to stderr for easier log capture.
CLAUDE_CODE_OTEL_DIAG_STDERR = "1";
OTEL_METRICS_EXPORTER = "otlp";
OTEL_LOGS_EXPORTER = "otlp";
OTEL_TRACES_EXPORTER = "otlp";
@ -357,6 +355,16 @@ in
default. Host-driven via `services.hyperhive.otel.metricIntervalMs`.
'';
};
debug = lib.mkOption {
type = lib.types.bool;
default = false;
internal = true;
description = ''
Emit OTEL SDK diagnostics to stderr (`CLAUDE_CODE_OTEL_DIAG_STDERR=1`).
Host-driven via `services.hyperhive.otel.debug`.
'';
};
};
options.hyperhive.allowedRecipients = lib.mkOption {
@ -1222,7 +1230,13 @@ in
# run — `baseClaudeEnv` contains per-agent values (e.g.
# CLAUDE_REMOTE_CONTROL_SESSION_NAME_PREFIX) that can't live in the
# static store asset.
allEnv = baseClaudeEnv // lib.optionalAttrs otelCfg.enable otelSettingsEnv;
allEnv =
baseClaudeEnv
// lib.optionalAttrs otelCfg.enable otelSettingsEnv
// lib.optionalAttrs (otelCfg.enable && otelCfg.debug) {
# SDK diagnostics — noisy; only on when services.hyperhive.otel.debug = true.
CLAUDE_CODE_OTEL_DIAG_STDERR = "1";
};
in
pkgs.runCommand "managed-settings.json" { nativeBuildInputs = [ pkgs.jq ]; } ''
jq --argjson env ${lib.escapeShellArg (builtins.toJSON allEnv)} \