Commit graph hyperhive/nix/host-modules/otel.nix
Author SHA1 Message Date
atlas
d2fb4bff79 feat(#3125): reshape the hive-to-swarm OTEL hop by domain
Drops swarm.otel.url (a loopback default an operator had to override on a
split host) in favor of swarm.otel.domain -- the same
gateway.localNames + nginx-vhost-through-the-gateway shape every other
swarm service (authelia, grafana, victoriametrics, ui) already uses. The
hive tier's exporter now reaches it as https://<domain> unconditionally,
resolved locally by dnsmasq on a co-located host and over the real network
otherwise, instead of a config knob nobody sets until they hit the silent
drop.

Costs CA trust on the hive tier: otel.nix wires
lib/hive-ca-trust.nix's trustBundle with hostUnit = true on the
opentelemetry-collector host unit, the same flag #3441/#3442 added for
swarm-controller and hive-c0re.

mara, #3125 comment 58363: "go c".
2026-08-18 21:02:18 +02:00
atlas
98ab0ad59f style: drop tracker tags from the collector modules' comments
The pre-push lint refuses them, and rightly: a comment that names an
issue number ages into a pointer at a closed thread. The constraint each
one carried is stated directly instead.
2026-08-18 21:02:18 +02:00
atlas
094a54e785 feat(#3125): the hive tier forwards to the swarm tier and holds nothing
The hive collector's only exporter becomes the swarm's collector, and
the upstream credential, the metrics-store exporter and the choice of
destination all move one tier up.

Its assertion goes with them: 'endpoint or a local store' was the right
rule while this tier picked the destination, and is the wrong one now.
A hive that runs no swarm services has neither, forwards to a swarm
collector elsewhere, and is correctly configured — the rule that
replaces it lives in swarm-otel.nix, where the destinations are.

The option descriptions here described a topology with one collector in
it: endpoint and protocol are not what agents are handed (they get the
derived first hop, see hive-c0re/environment.nix), and the credential is
not read by this tier.
2026-08-18 21:02:18 +02:00
atlas
16d578e692 docs(#3265): observability.md still said the endpoint was required
Review catch: this PR relaxed the `otel.endpoint` assertion and staled the
canonical OTEL reference in the same stroke — `docs/observability.md` is
what CLAUDE.md points readers at for "what OTEL options are available",
and it still said required-full-stop while the new swarm/services.md
section said a local store satisfies it.

Also corrects the option's own description in otel.nix, which said the
same thing and renders into the generated options doc. Grepping the
reviewer's phrasing did not find that one; grepping the claim did.

Records the second destination where the "endpoint is where telemetry
ultimately goes" paragraph makes its claim, rather than only in the new
section a reader may not reach.
2026-08-16 22:27:05 +02:00
atlas
c364d262e5 feat(#3265): feed the store from the collector, and derive the pair
Review feedback: the metrics pair had nothing writing into it, and it sat
outside the switch that turns on every other swarm-wide service.

The collector now exports to VictoriaMetrics as well as upstream — a
fan-out, not a choice: a local store is for looking at this swarm, an
upstream is for whoever aggregates across swarms.

That makes a local store a complete destination on its own, so
`otel.endpoint` is no longer required when it runs here; a hive with
neither is still refused. The assertion only ever relaxes, so every
config that evaluated before still does.

`enableRequiredServices` now derives both halves, alongside matrix,
authelia and nats. They derive together because a store with no UI is
unreadable and a UI with no store is empty.
2026-08-16 22:27:05 +02:00
atlas
c38778b885 docs(otel): validateConfigFile is a parser, not a wiring check
The comment said "It parses; it does not prove a sample arrives",
which understates the gap in the direction that matters: it reads as
if a green build proves the collector *starts* and only runtime
delivery is unverified.

Measured while probing ingest-auth options for #3283: `otelcol
validate` ACCEPTS a receiver naming an auth extension that is absent
from the build, and the collector then dies at startup with `Failed
to start component`. So the check does not prove this config starts
at all.

Comment-only; no evaluated config changes.

Refs #3283.
2026-08-15 12:13:48 +02:00
atlas
513eb5729d docs(otel): one path, and stop describing a forwarding that no longer happens
The headersCredential section still said hive-c0re forwards the file
into each agent container - that is exactly the behaviour this change
removes, so the doc contradicted the code rather than merely lagging
it. Same for the collector section, which presented the two paths the
change collapses.

Also drops the dangling collector.enable reference in the network
section.
2026-08-15 11:46:24 +02:00
atlas
9549cdf9bd feat(otel): one path out of the hive, not two
mara: 'there should only be the one via otel-collector'. Collapses
collector.enable away - enabling otel runs the collector, and agents
always export to it.

The argument for it is the same one the collector exists for: as long
as the direct path stays selectable, the credential-in-settings.json
hole stays selectable. An option that can reintroduce a hole is a hole
with extra steps. HYPERHIVE_OTEL_HEADERS_CREDENTIAL is now never
forwarded to containers at all rather than conditionally.

Two consequences, both deliberate:

- the enable option's own promise changes. It said each harness exports
  directly so telemetry survives anything host-side being down; that is
  now false for every hive rather than for opted-in ones. Stated in the
  option text rather than left for a reader to discover.
- the collector-requires-headersCredential assertion is gone. It was
  correct only under the shape it was written for: when the collector
  was opt-in FOR the credential, opting in without one was pure
  indirection. With one path it isn't - an upstream needing no auth
  header is legitimate, and the assertion would have rejected it. The
  upstream header block is now omitted entirely in that case, rather
  than rendering an ${env:...} reference nothing sets.
2026-08-15 11:46:24 +02:00
atlas
1fc267b880 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.
2026-08-15 11:46:24 +02:00
atlas
9ad4db842a feat(otel): the collector unit, off by default
Renders services.opentelemetry-collector on the host: OTLP/HTTP receiver
on the bridge address, otlphttp exporter to otel.endpoint, and the
upstream credential delivered as EnvironmentFile so the collector
interpolates it at runtime and nix never sees the value.

Three things worth knowing, each measured rather than assumed:

- network.exposeHostPorts already exists and is wired (it opens the port
  on the bridge interface only), so bridge reachability costs nothing.
- validateConfigFile defaults to isStorePath configFile, which is null on
  the settings path - so upstream's default is OFF for exactly the way
  this module configures it. Set true: it runs otelcol validate at build
  time. It parses, it does not prove delivery.
- headersCredential's file is already NAME=value, i.e. EnvironmentFile
  format, verified against a real settings.json rather than the doc.

An assertion refuses collector.enable with no headersCredential: the
collector exists to be the only holder of that token, and without one it
is indirection that reads as security.
2026-08-15 11:46:24 +02:00
atlas
b8af1525ff feat(otel): options for a host-side collector, off by default
Adds services.hyperhive.otel.collector.{enable,port}. No implementation
yet - this commit only declares the surface, so the option docs and the
default-off invariant can be reviewed on their own.

The reason the collector exists is the credential: without it every agent
needs headersCredential to reach the upstream, and the harness delivers
that token into the agent's own settings.json where the agent can read
it. The option text states the cost too - the collector becomes a
dependency in an export path that is currently direct.
2026-08-15 11:46:24 +02:00
müde
4a48ce5024 refactor: nix/host-modules + nix/agent-modules layout, update doc paths 2026-07-13 22:05:49 +02:00
Renamed from nix/modules/otel.nix (Browse further)