Files
git.stella-ops.org/docs/reachability/evidence-schema.md
2026-01-28 02:30:48 +02:00

7.3 KiB

Runtime Evidence Schema Reference

Overview

Runtime evidence is serialized as NDJSON (Newline-Delimited JSON), with one event per line. The schema ensures deterministic output for reproducible evidence chains.

Schema Location

  • JSON Schema: docs/schemas/runtime-evidence-v1.json
  • C# Models: src/Signals/__Libraries/StellaOps.Signals.Ebpf/Schema/

Common Fields

Every evidence record includes these base fields:

Field Type Required Description
ts_ns integer Yes Nanoseconds since system boot
src string Yes Event source identifier
pid integer Yes Process ID
tid integer No Thread ID (if available)
cgroup_id integer Yes Kernel cgroup ID
container_id string No Container ID (enriched)
image_digest string No Image digest (enriched)
comm string No Process command name (max 16 chars)
event object Yes Type-specific event data

Event Types

File Access (file_access)

Captured from sys_enter_openat tracepoint.

{
  "ts_ns": 1234567890123456789,
  "src": "tracepoint:syscalls:sys_enter_openat",
  "pid": 1234,
  "cgroup_id": 5678,
  "container_id": "abc123def456",
  "image_digest": "sha256:...",
  "comm": "nginx",
  "event": {
    "type": "file_access",
    "path": "/etc/nginx/nginx.conf",
    "flags": 0,
    "mode": 0,
    "access": "read"
  }
}
Field Type Description
path string File path (max 256 chars)
flags integer Open flags (O_RDONLY, O_WRONLY, etc.)
mode integer File mode (for creation)
access string Derived access type: read, write, read_write

Process Execution (process_exec)

Captured from sched_process_exec tracepoint.

{
  "ts_ns": 1234567890123456789,
  "src": "tracepoint:sched:sched_process_exec",
  "pid": 1234,
  "cgroup_id": 5678,
  "container_id": "abc123def456",
  "image_digest": "sha256:...",
  "comm": "python3",
  "event": {
    "type": "process_exec",
    "filename": "/usr/bin/python3",
    "ppid": 1000,
    "argv": ["python3", "script.py", "--config", "/etc/app.conf"]
  }
}
Field Type Description
filename string Executed binary path
ppid integer Parent process ID
argv string[] Command arguments (limited to first 4)

TCP State Change (tcp_state)

Captured from inet_sock_set_state tracepoint.

{
  "ts_ns": 1234567890123456789,
  "src": "tracepoint:sock:inet_sock_set_state",
  "pid": 1234,
  "cgroup_id": 5678,
  "container_id": "abc123def456",
  "image_digest": "sha256:...",
  "comm": "curl",
  "event": {
    "type": "tcp_state",
    "family": "ipv4",
    "old_state": "SYN_SENT",
    "new_state": "ESTABLISHED",
    "src_addr": "10.0.0.5",
    "src_port": 45678,
    "dst_addr": "93.184.216.34",
    "dst_port": 443
  }
}
Field Type Description
family string Address family: ipv4 or ipv6
old_state string Previous TCP state
new_state string New TCP state
src_addr string Source IP address
src_port integer Source port
dst_addr string Destination IP address
dst_port integer Destination port

TCP States: CLOSED, LISTEN, SYN_SENT, SYN_RECV, ESTABLISHED, FIN_WAIT1, FIN_WAIT2, CLOSE_WAIT, CLOSING, LAST_ACK, TIME_WAIT

Network Operation (network_op)

Captured from libc connect/accept uprobes.

{
  "ts_ns": 1234567890123456789,
  "src": "uprobe:libc.so.6:connect",
  "pid": 1234,
  "cgroup_id": 5678,
  "container_id": "abc123def456",
  "image_digest": "sha256:...",
  "comm": "app",
  "event": {
    "type": "network_op",
    "operation": "connect",
    "family": "ipv4",
    "addr": "10.0.1.100",
    "port": 5432,
    "result": 0
  }
}
Field Type Description
operation string connect or accept
family string Address family
addr string Remote address
port integer Remote port
result integer Return value (0 = success)

SSL Operation (ssl_op)

Captured from OpenSSL SSL_read/SSL_write uprobes.

{
  "ts_ns": 1234567890123456789,
  "src": "uprobe:libssl.so.3:SSL_write",
  "pid": 1234,
  "cgroup_id": 5678,
  "container_id": "abc123def456",
  "image_digest": "sha256:...",
  "comm": "nginx",
  "event": {
    "type": "ssl_op",
    "operation": "write",
    "requested_bytes": 1024,
    "actual_bytes": 1024,
    "ssl_ptr": 140234567890
  }
}
Field Type Description
operation string read or write
requested_bytes integer Bytes requested
actual_bytes integer Bytes actually transferred
ssl_ptr integer SSL context pointer (for correlation)

Symbol Call (symbol_call)

Captured from function uprobes.

{
  "ts_ns": 1234567890123456789,
  "src": "uprobe:app:vulnerable_parse_json",
  "pid": 1234,
  "cgroup_id": 5678,
  "container_id": "abc123def456",
  "image_digest": "sha256:...",
  "comm": "app",
  "event": {
    "type": "symbol_call",
    "symbol": "vulnerable_parse_json",
    "library": "/usr/lib/libapp.so",
    "offset": 4096,
    "address": 140234571986
  }
}
Field Type Description
symbol string Function symbol name
library string Library/binary path
offset integer Offset within library
address integer Runtime address

Determinism Requirements

For byte-identical output across runs:

  1. Field Ordering: All JSON keys sorted alphabetically
  2. Number Format: Integers as-is, no floating point variance
  3. String Encoding: UTF-8 with NFC normalization
  4. Null Handling: Null fields omitted (not "field": null)
  5. Whitespace: No trailing whitespace, single newline per record

Chunk Metadata

Each evidence chunk includes metadata in its DSSE attestation:

{
  "predicateType": "stella.ops/runtime-evidence@v1",
  "predicate": {
    "chunk_id": "sha256:abc123...",
    "chunk_sequence": 42,
    "previous_chunk_id": "sha256:def456...",
    "event_count": 150000,
    "time_range": {
      "start": "2026-01-27T10:00:00Z",
      "end": "2026-01-27T11:00:00Z"
    },
    "collector_version": "1.0.0",
    "kernel_version": "5.15.0-generic",
    "compression": null,
    "host_id": "node-01.cluster.local",
    "container_ids": ["abc123", "def456"]
  }
}

Validation

Evidence can be validated against the JSON Schema:

# Validate single file
stella evidence validate evidence-chunk-001.ndjson

# Validate and show statistics
stella evidence validate --stats evidence-chunk-001.ndjson

Migration from v0 Schemas

If using earlier per-language schemas, migrate to v1 unified schema:

  1. Update field names to snake_case
  2. Wrap type-specific fields in event object
  3. Add src field with probe identifier
  4. Ensure ts_ns uses nanoseconds since boot

Example migration:

// v0 (old)
{"timestamp": 1234567890, "type": "file", "path": "/etc/config"}

// v1 (new)
{"ts_ns": 1234567890000000000, "src": "tracepoint:syscalls:sys_enter_openat", "pid": 1234, "cgroup_id": 5678, "event": {"type": "file_access", "path": "/etc/config", "flags": 0, "mode": 0, "access": "read"}}