Skip to content

Latest commit

 

History

History
101 lines (75 loc) · 5.08 KB

File metadata and controls

101 lines (75 loc) · 5.08 KB

The .flight Trace Format — Specification v0.1.0

A .flight trace is a portable, tamper-evident, replayable record of an AI agent run. This document is the normative specification; it is language-neutral — any runtime can produce or consume a conforming trace. The Python reference implementation lives in this repository.

Normative keywords (MUST / SHOULD / MAY) follow RFC 2119. The full design rationale is in plan/02-trace-format.md; the JSON Schema for the envelope is schema/flight-event.schema.json.

1. Container

A trace is a directory named <name>.flight:

<name>.flight/
├── manifest.json     # sealed head hash + counts (written last)
├── events.jsonl      # append-only, hash-linked event stream (one JSON object per line)
└── blobs/            # OPTIONAL content-addressed sidecar: blobs/<hh>/<hh>/<sha256hex>

events.jsonl MUST be JSON Lines (one \n-terminated JSON object per line), UTF-8. It MUST be append-only: a writer MUST NOT rewrite a previously written line. manifest.json MUST be written after the last event.

2. Event envelope

Every line MUST be a JSON object with these fields (see the JSON Schema for exact types):

Field Type Meaning
v int ≥ 1 event-schema version
seq int ≥ 0 gap-free total order; the sole ordering authority
run_id string id of the run
id string run_id + "/" + seq
type string namespaced event type (§4)
t_wall string RFC3339 UTC timestamp — informational only, MUST NOT be used for ordering
t_mono_ns int nanoseconds since run.start
parents int[] causal/DAG edges, referencing seq numbers
body object type-specific content, already redacted
body_hash sha256:<hex> digest of the content (§3)
prev_hash sha256:<hex> hash of event seq-1 (or the genesis value for seq 0)
hash sha256:<hex> integrity digest of the whole envelope minus hash (§3)

3. Canonicalization & hashing

Before hashing, a value MUST be serialized with a canonical JSON scheme (RFC 8785 / JCS): object keys sorted by UTF-16 code units, no insignificant whitespace, NFC-normalized strings, and stable number formatting. NaN and Infinity MUST NOT appear.

  • body_hash = sha256(JCS({ "type", "v", "body" })), computed over the body with volatile fields removed (latency_ns, duration_ns, truncated, mono_origin_ns). This is the content-equality key used for dedup and diff — two otherwise-identical events MUST share a body_hash.
  • hash = sha256(JCS(envelope without "hash")). Because the envelope carries prev_hash, each hash transitively commits to every prior event (an append-only chain).
  • The genesis prev_hash for seq 0 is sha256: + 64 zeros.

The sha256:<hex> digests use lowercase hex.

4. Event types (v0.1.0)

run.start, run.end, http.request, http.response, tool.call, tool.result, clock.read, rng.draw, error. Types are namespaced (dot-delimited); vendors MAY add x-<vendor>.* types.

A reader encountering an unknown type MUST still order it by seq and MUST still be able to verify the chain (hashing is structural, not type-aware). A reader that re-emits an event MUST preserve fields it does not understand (or the hash breaks).

In v0.1 LLM traffic is recorded as http.request / http.response (the raw wire is what makes replay byte-faithful and provider-agnostic). Semantic llm.* events are a later enrichment.

Large body payloads MAY be spilled to the blobs/ sidecar and referenced inline as {"$blob": "sha256:<hex>", "media_type": "...", "size": <int>}. Non-JSON-serializable values MAY be represented as a lossy surrogate {"$unrepr": true, "lang_type": "...", "repr": "..."}.

5. Manifest

manifest.json MUST contain at least: spec_version, hash_alg ("sha256"), canonicalization, run_id, head_hash (the hash of the last event), and event_count. It MAY advertise optional features under capabilities.

6. Verification

A verifier re-walks events.jsonl in order, maintaining an expected prev_hash (starting at genesis), and for each event asserts: seq increments by 1; prev_hash equals the running value; the recomputed body_hash and hash match the stored values. Finally it asserts the last hash equals manifest.head_hash and the count matches manifest.event_count. Verification needs only the redacted canonical bytes — no secrets and no network — so any recipient can verify a shared trace offline.

7. Redaction

Redaction MUST run before canonicalization and hashing, so a secret is never hashed or written in plaintext and redaction can never break the chain. A redacted value SHOULD become a stable placeholder carrying a ref that is derived deterministically from the secret, so referential integrity is preserved without revealing the secret.

Versioning

spec_version is semver. Within a major version, fields are only added (optional); removal or retyping requires a major bump. This document specifies 0.1.0.