chore: freeze EventEnvelope v0 byte contract

This commit is contained in:
sovereign
2025-12-18 00:14:03 +00:00
parent d361122ec0
commit 1830e0f673
9 changed files with 282 additions and 40 deletions

56
docs/EVENT_ENVELOPE.md Normal file
View File

@@ -0,0 +1,56 @@
# VaultMesh EventEnvelope (v0) Canonical Spec
This document defines the stable, audit-friendly contract for `EventEnvelope` as used by the Command Center:
- HTTP API: `POST /api/events`, `GET /api/events`
- SSE stream: `GET /events` (event name = `kind`)
- Durable log: `$VAULTMESH_LOG_DIR/events.jsonl` (one envelope per line)
## Envelope Shape
`EventEnvelope` is a single JSON object with the following fields:
Required:
- `format`: string, must be `"vm-event-envelope-v0"`
- `schema`: object, must be `{ "envelope": 0, "payload": 0 }`
- `id`: UUID string (server-assigned)
- `ts`: RFC3339 UTC timestamp with **seconds precision** (server-assigned), e.g. `"2025-12-17T23:07:10Z"`
- `kind`: string (e.g. `"note"`, `"incident"`, `"ack"`, `"tag"`, `"resolve"`)
- `author`: string (e.g. `"operator"`, `"system"`, `"vm-copilot"`)
- `payload`: JSON value (kind-specific; usually an object)
Optional:
- `node_id`: UUID string (omit if global event)
Compatibility:
- Incoming requests/log lines may use `body` instead of `payload`; Command Center treats `body` as an alias for `payload`.
## Timestamp Rules
- Canonical timestamps are **UTC `Z`**, **seconds precision only**.
- If a timestamp contains fractional seconds, Command Center truncates to seconds during canonicalization.
## Canonical JSON Ordering
To keep bytes stable forever (for hashing, Merkle roots, and diffability), Command Center canonicalizes envelopes before persistence and broadcast:
- Top-level field order is fixed by the envelope struct definition.
- `payload` is recursively normalized by sorting **object keys** lexicographically.
- Arrays preserve order (arrays are never sorted).
- Optional fields are omitted when absent (no `field: null` unless semantically meaningful).
## Canonical Bytes + Newline
The canonical byte representation of an event is:
- UTF-8 bytes of the canonical JSON serialization of the envelope
- followed by a single LF newline byte (`0x0A`)
`events.jsonl` is the concatenation of these canonical envelope line bytes in file order.
## Hashing (v0)
When hashing a canonical event line (leaf hashing), use:
- `SHA-256(canonical_event_line_bytes)`