The contracts and deterministic receipt assembler at the heart of Haia Trace.
Most users reach for the CLI or the x402
adapter instead — depend on trace-core directly when you’re
building your own capture adapter or embedding the assembler.
Zero runtime dependencies. ESM only, Node ≥ 20 (the assembler is also
runtime-agnostic; the file-backed store lives behind the /node subpath).
Install
The three contracts
- Event — the normalized shape every capture adapter emits: a namespaced
event_type, an occurred_at, an optional context_id grouping one
operation’s events, a seq, the observing role, and a redacted payload.
Written append-only as NDJSON.
- Template — a declarative description of an operation as a sequence of
milestone stages. Each stage’s
match is a match-set: any one of its events
closes the stage.
- Receipt — the verdict from
(events, template). See Operation
Receipt.
Assemble a receipt
The assembler is a pure, deterministic function: no LLM, no randomness. The
same events and template always yield the same Receipt, byte for byte. Events are
split into operations by context_id, and one Receipt is produced per operation.
assembleReceiptsProgressively(events, template) yields the same result as an
iterator of progress snapshots, for streaming a live count over a large run.
An OperationTemplate is plain data; validate an untrusted one (e.g. parsed
from YAML) with assertOperationTemplate before assembling against it.
Mint events (building an adapter)
createRecorder is the one place an adapter mints events — it owns the session’s
seq counter and stamps event_id, occurred_at, and adapter, so you supply
only the meaningful fields:
Persist events (the /node subpath)
The event sink is a runtime-agnostic contract in the root export (EventWriter /
EventReader, plus the NDJSON codec encodeEventLine / decodeEventLines). The
concrete file-backed implementation — the only place node:fs is imported —
lives behind the /node subpath: