Runtime Evidence Schema Reference

Overview

Runtime evidence is serialized as NDJSON (Newline-Delimited JSON), one event per line. NDJSON is chosen so chunks can be streamed, appended, and hashed incrementally without buffering a whole document, and so that a single corrupt line never invalidates the rest of a chunk. Output is canonicalized (see Determinism Invariants) so the same observed events produce byte-identical files across runs — the property that makes evidence chains reproducible and attestable.

This page is an altitude reference: it describes the record shape, the event taxonomy, and the invariants. It does not enumerate every field — exact field names, types, requiredness, and discriminator values live in the authoritative sources below and are validated against them. When this page and the schema disagree, the schema and the C# records win.

Authoritative sources

WhatWhere
JSON Schema (field-by-field, the validation authority)docs/schemas/runtime-evidence-v1.json
C# record definitionssrc/Signals/__Libraries/StellaOps.Signals.Ebpf/Schema/ (RuntimeEvidence.cs, SyscallEvents.cs, RuntimeCallEvent.cs)
Canonical NDJSON writer (ordering, chunking)src/Signals/__Libraries/StellaOps.Signals.Ebpf/Output/RuntimeEvidenceNdjsonWriter.cs
DSSE chunk predicate modelsrc/Signals/__Libraries/StellaOps.Signals.Ebpf/Signing/RuntimeEvidencePredicate.cs
Worked exampledocs/modules/scanner/runtime-evidence.example.ndjson

Schema version: runtime-evidence/v1.

Record shape

Every line is one RuntimeEvidenceRecord (Schema/RuntimeEvidence.cs): a common envelope plus a polymorphic event payload.

{ "ts_ns": <ns since boot>, "src": "<probe name>", "pid": <int>,
  "cgroup_id": <int>, "comm": "<process name>",
  "event": { "type": "<event type>", ... } }

The exact required-vs-optional split and field types are in the JSON Schema and the RuntimeEvidenceRecord record — do not re-document them here.

Event taxonomy

Six event types exist. Each maps to a type discriminator value and an originating probe; the payload fields are defined in the JSON Schema $defs of the same name and the corresponding C# record.

typeCapturesOriginating probe
file_openFile open + derived access mode (read/write/read_write)sys_enter_openat tracepoint
process_execExecuted binary, parent pid, argv[0]sched_process_exec tracepoint
tcp_stateTCP state transition, peer addr/port, familyinet_sock_set_state tracepoint
net_connectconnect/accept to a remote addr/port + resultlibc uprobes
ssl_opPlaintext-side SSL read/write byte counts + session ptrOpenSSL uprobes
function_callSymbol/library/runtime + optional call stack + reachability node_hashgeneric function uprobe

function_call is the join point with static reachability: its node_hash is the canonical hash used to match a runtime-observed call against a graph node.

Note on names: the discriminator values are file_open, net_connect, and function_call (not file_access, network_op, symbol_call) — verify against the JSON Schema $defs before coding to them.

Representative record

A single representative line (a tcp_state event); see the JSON Schema examples and the worked NDJSON file for the others:

{"ts_ns":1234567890123456789,"src":"inet_sock_set_state","pid":1234,"cgroup_id":5678,"container_id":"containerd://abc123...","image_digest":"sha256:...","comm":"curl","event":{"type":"tcp_state","oldstate":"SYN_SENT","newstate":"ESTABLISHED","daddr":"93.184.216.34","dport":443,"family":"inet"}}

TCP state strings and address-family values (inet/inet6) are enumerated in the schema and produced by the TcpStateHelper/IpAddressHelper mappers in Schema/RuntimeEvidence.cs.

Determinism invariants

These are design contracts the writer guarantees so chunk hashes are stable; the mechanics live in Output/RuntimeEvidenceNdjsonWriter.cs.

  1. Key ordering — JSON keys are emitted in a fixed (alphabetical) order.
  2. No floating point — all numerics are integers; no float formatting variance.
  3. String encoding — UTF-8, NFC-normalized.
  4. Null omission — null/default optional fields are dropped, not written as null.
  5. Whitespace — compact (WriteIndented = false), exactly one newline per record, no trailing whitespace.

Chunk metadata & attestation

Evidence is written in rotating chunks. Each chunk is sealed as a DSSE-signed in-toto statement with predicate type stella.ops/runtime-evidence@v1. The predicate links chunks into a hash chain and records collection provenance — shape:

{ "predicateType": "stella.ops/runtime-evidence@v1",
  "predicate": { "chunk_id": "sha256:...", "chunk_sequence": <n>,
                 "previous_chunk_id": "sha256:...", "event_count": <n>,
                 "time_range": { "start": "...", "end": "..." },
                 "collector_version": "...", "kernel_version": "...",
                 "host_id": "...", "container_ids": [ ... ] } }

previous_chunk_id is what makes the sequence tamper-evident — each chunk commits to its predecessor. The full predicate field set is defined in Signing/RuntimeEvidencePredicate.cs; the statement/DSSE assembly and signing flow are in Signing/AttestorEvidenceChunkSigner.cs.

Validation

Validate evidence against docs/schemas/runtime-evidence-v1.json with any JSON Schema (draft 2020-12) validator. The schema is the validation authority; the C# records are kept in sync with it.

For evidence handling at the chunk/attestation level the CLI exposes stella evidence export and stella evidence verify (plus card, reindex, verify-continuity, migrate); see src/Cli/StellaOps.Cli/Commands/EvidenceCommandGroup.cs.

Migration from v0 schemas

Earlier per-language schemas migrate to the unified v1 record by: renaming fields to snake_case, wrapping type-specific fields inside the event object with a type discriminator, adding the src probe identifier, and converting timestamps to ts_ns (nanoseconds since boot). The v1 field names and discriminator values are those in the JSON Schema.