krply/docs/event-schema/event-schema.md
lakshit verma 9f4b6c2c5a
fix(audit,web,deploy): audit ids, web dry-run view, hardened chart, docs
Audit: correlation event_ids include stage and response code so the
multi-stage lines of one request no longer collapse under dedup; match
scans are bounded to a time window and a page instead of the object's full
history.

Web: dry-run results are read from dry_run_result (nested) so the verdict is
rendered correctly and conflicts/errors/skipped are shown; the plans view no
longer POSTs an unsolicited plan on page load; coverage and streams surface
API errors instead of showing a misleading empty state and follow cursor
pagination so they are not stuck on the oldest page; the diff path tokenizer
handles backslash-escaped dotted keys.

Deploy/CI: the chart no longer grants the query server a cluster-wide read
ClusterRole, runs as non-root with a read-only root filesystem, adds
liveness/readiness probes, wires the ConfigMap as env (STORE_PATH,
LISTEN_ADDR), defaults the journal to a PVC instead of an ephemeral
emptyDir, and adds imagePullSecrets; the replay ClusterRole drops the
unused update verb; a Dockerfile builds a static distroless image; GitHub
actions are pinned by commit SHA, jobs set least-privilege permissions, the
Vercel deploy skips fork PRs, CI passes the Makefile test timeouts, and
make lint runs a real web syntax check.

Docs: event-schema/consistency no longer describe an ingest_sequence field,
an observed-time-based event_id, or restart-from-checkpoint; the threat
model documents the unauthenticated HTTP API surface and the chart's RBAC
change; replay-safety matches the enforced dry-run gate.
2026-08-06 06:57:11 +05:30

3.8 KiB

Event schema

Every journal record is an immutable entry. The wire format is api/event/v1. This document describes the fields, the record types, and how keys and hashes are derived.

Record fields

Field Purpose
cluster_id Separates resource version and UID domains; one per physical cluster
stream_id Identifies group, version, resource, namespace, and selector
event_id Deterministic deduplication key
ingest_seq Local storage order (ascending); returned in this order
observed_at Collector observation time
watch_type Original event type (ADDED, MODIFIED, DELETED, BOOKMARK, ERROR)
synthetic True for baseline events
resource Group, version, kind, namespace, name, UID, resource version
object_hash Fast equality and deduplication
object Raw or faithfully preserved payload (JSON)
provenance Optional audit correlation

The raw watch payload is kept separate from normalized fields. This preserves unknown CRD fields. Never re-serialize the object into a lossy normalized shape.

Record types

The journal stores event records plus these special records:

Type Meaning
event A watch event (ADDED, MODIFIED, DELETED)
baseline A list result (initial or post-relist)
gap Continuity was lost (for example, a 410 Gone response)
coverage_change A resource became unavailable or was newly discovered
audit_correlation Optional request provenance matched from audit logs
snapshot A materialized set of stream boundaries
checkpoint Progress marker advanced by a bookmark

The corresponding record type constants in internal/event are: TypeEvent, TypeBaseline, TypeGap, TypeCoverageChange, TypeAuditCorrelation, TypeSnapshot, and TypeCheckpoint. The watch types are: WatchAdded, WatchModified, WatchDeleted, WatchBookmark, and WatchError.

event_id derivation

The field event_id is the deterministic deduplication key. The derivation is:

EventID(stream, resource, watchType, observedAt) -> string

The inputs are the stream identity, the resource reference, and the watch type. For live watch events the observed time is deliberately excluded: the same underlying API event must always produce the same key, so a duplicate delivery after a reconnect is idempotent. Collector-generated synthetic baselines pass the list observation time, so an unchanged object re-listed after a gap is treated as a new observation rather than a duplicate and survives deduplication. See ../consistency/consistency.md for the ingestion guarantee.

object_hash

The function ObjectHash(objectJSON) returns a fast equality hash over the raw object payload. It lets consumers detect that a MODIFIED event did not change the object. It is not a cryptographic commitment. Treat it as a performance and equality helper only. Deduplication is by event_id, not by object content.

Provenance

The field provenance is optional. When audit logs are available, an audit event is correlated to a stored event by cluster, namespace, name, UID, and resource version. It is recorded as a TypeAuditCorrelation record that references the field event_id. Audit identity is available only through this optional correlation. A watch event itself never contains the actor who made the change.

Versioning

The package api/event/v1 is the public wire format. Internal packages stay private until the schema and replay contract stabilize. The manifest used by the object-storage segment layout records a schema version alongside the redaction policy version. Do not change api/event/v1 without a coordinated schema bump. Consumers of the journal and replay contract depend on it.

See also

  • Ingestion and deduplication: ../consistency/consistency.md.
  • Storage layout and segment manifests: ../architecture/architecture.md.
  • Sanitization of the object payload before replay: ../replay-safety/replay-safety.md.