Node telemetry
The Effect v4-native observability facade for Maia Node. It replaces the
previous @vllnt/logger adapter; Maia does not retain that external dependency.
It was the separate @maiaos/telemetry package until Maia Node became its only
consumer; it is no longer published.
layer.ts provides one scoped layer that records:
- structured Effect logs;
- named Effect spans, including outcome (
success,interruptedwhen an interruption alone ended the span, such as a graceful shutdown, orfailure), duration, hierarchy, attributes, and bounded events; - periodic and final Effect metric snapshots.
All records are versioned NDJSON, tagged with a component, bounded, and redacted by key. Logs and traces pass through byte- and count-bounded dropping Effect queues; saturation is summarized, writer calls are time-bounded, and a sink defect cannot fail the product scope. A writer is injected so the same instrumentation can target a local file, stderr, a desktop live view, or a future consented exporter without changing this scope.
Exporter delivery runs with Effect tracing disabled to prevent recursive self-observation. The Node file adapter registers each accepted batch with its six-batch physical writer before awaiting it; three slots are reserved for final log, trace, and metric delivery. It opens one validated private file handle for each batch, checks the rotation bound per record, and writes the records that fit one file with a single write: a write per record would hold the writer long enough under load to fill its normal slots and drop records.
Expandable structured log fields use a Map<string, unknown>. Map iteration,
arrays, metric snapshots, trace attributes, and span events are capped before
encoding. One record also shares a 512-value traversal budget, a 64 KiB string
budget, a seven-level depth bound, and a 64 KiB encoded bound. Span attributes
and events share a per-span traversal budget and are normalized when received,
not only when a span ends. Their drop counters also count redacted or truncated
inputs. Link payloads are counted and discarded because Maia does not serialize
them, so raw link graphs are never retained.
Arbitrary objects are emitted as [UNSTRUCTURED_OBJECT]; their keys, accessors,
and custom serializers are never enumerated on the logging path. This keeps
hostile, nested, or accidentally high-cardinality values from turning local
observability into an unbounded synchronous workload.
Each telemetry scope allocates a fresh Effect metric registry. Metric descriptors must come from a per-registry factory or scoped service because Effect v4 descriptors cache the hooks resolved from their first registry. Metric names and attributes are static, and frequency inputs are finite typed enums with preregistered words rather than user-controlled strings.
layer.ts does not reference Node globals. rotating-file-writer.ts
supplies the Node stderr adapter and a serialized, rotating file writer capped
at five 5 MiB files per profile. It enforces mode 0600 on POSIX and uses
no-follow where Node exposes that flag.
Effect v4's core Logger is the logging API; Maia does not add a parallel
PlatformLogger facade. Platform adapters use Effect FileSystem and Stdio
services where those contracts retain Maia's security and backpressure
guarantees, with narrowly documented native code for guarantees the generic
API cannot express.
Target Node streaming integration
This scope supplies diagnostic emitters; it is not the authoritative event store or application streaming server. Domain transitions, receipts, effects, final messages, and usage are committed through Maia Node's operation/event transaction. Telemetry cannot downgrade those records into a dropping log queue or claim that a log line proves a transition.
The target Node adapter offers redacted diagnostic records to
ObservationIngress in batches. Each process owns one bounded queue, coalesces
adjacent log/span/text-delta records under pressure, and returns immediately
after local acceptance. It never awaits a TUI, mobile client, exporter,
projection processor, or Swarm consumer. Saturation produces counts that Node
turns into an authoritative observation.gap; silent loss is forbidden.
Node performs classification, correlation, partitioning, persistence policy, and fan-out once. A hundred subscribers therefore do not produce a hundred logger sinks or additional worker writes. Optional exporters consume the same checkpointed observation partitions and cannot sit on the product execution path.
Remote telemetry is deliberately not implemented. See the target live observability contract for the mandatory unsampled evidence edges, live projections, retention, health, and qualification gates. This scope is the instrumentation and sink boundary; Maia Node remains the authority for correlated lifecycle evidence.
Rules
- Keep
layer.tsfree of Node built-ins; Node filesystem behavior belongs inrotating-file-writer.tsand its private writers. - Use Effect v4 logger, tracer, metric, scope, batching, and concurrency APIs.
Do not add
@vllnt/logger, another logging facade, or an unbounded queue. - Local collection is the default authority. Remote export is absent until an explicit consent, redaction, revocation, retry, and cost contract exists.
- Redact secret-like keys; bound ingestion, each container, total traversal, total string data, and encoded bytes; keep record schemas versioned; and never record credential values, prompts, file contents, or command output by default.
- Allocate a fresh Effect metric registry for each telemetry scope. Create metric descriptors through a per-registry factory or scoped service because Effect v4 descriptors cache their first registry hooks; do not reuse a module-scoped descriptor across runtime scopes. Metric names and attributes must be static; frequency inputs must be finite typed enums with preregistered words, never arbitrary or user-controlled strings.
- File writers must be private, serialized, rotated, bounded, and non-fatal to the product workflow. Tests must prove scope-final flushing and redaction.
Imported from Maia OS docs at a2175bb