chore: freeze EventEnvelope v0 byte contract
This commit is contained in:
@@ -124,6 +124,7 @@ Nodes are keyed by `node_id`. Each heartbeat overwrites the previous entry for t
|
||||
- SSE broadcast of envelope events by their `kind` name.
|
||||
- Durable persistence to `events.jsonl` with replay on startup.
|
||||
- Memory-bounded in-memory store (500 most recent envelopes).
|
||||
- Canonicalization rules for audit-grade stability (see `docs/EVENT_ENVELOPE.md`).
|
||||
|
||||
### V0.7.1: Mission Console
|
||||
- NASA-style 3-panel dashboard at `GET /console`.
|
||||
|
||||
56
docs/EVENT_ENVELOPE.md
Normal file
56
docs/EVENT_ENVELOPE.md
Normal 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)`
|
||||
|
||||
Reference in New Issue
Block a user