Skip to main content
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: