Events API
The events module exposes two SSE streams plus an internal peer-broadcast
ingest endpoint. The streams are tenant-scoped, support resume via
Last-Event-ID, and emit SSE frames with monotonic per-node id: values
backed by the events_log table.
| Method | Path | Purpose |
|---|---|---|
GET | /api/method/shc.events.stream | Tenant-scoped SSE stream of all events |
GET | /api/method/shc.events.errors.stream | Errors-only variant of the stream |
POST | /internal/events | Cross-node peer-broadcast receiver (private) |
GET /api/method/shc.events.stream
Section titled “GET /api/method/shc.events.stream”Live SSE stream of events for one tenant. Heartbeats every 30s; one SSE frame per event.
Query parameters
Section titled “Query parameters”| Param | Required | Description |
|---|---|---|
tenant | No | Tenant override; honors X-SHC-Tenant header first, then ?tenant=, default "default". |
stack_name | No | Restricts to one stack. |
node_id | No | Restricts to events originated by one node. |
type | No | Comma-separated whitelist of classified types (audit, docker, error, event). |
last_event_id | No | Fallback resume cursor for clients that can’t set headers. |
Headers
Section titled “Headers”| Header | Direction | Description |
|---|---|---|
X-SHC-Tenant | Request | Overrides ?tenant= when both are present. |
Last-Event-ID | Request | RFC EventSource resume cursor. See Replay below. |
Content-Type: text/event-stream | Response | Always set. |
Cache-Control: no-cache | Response | Always set. |
Frame shape
Section titled “Frame shape”id: 47event: dockerdata: {"code":"D100100CTRSTR","message":"container started",...,"sequence":47,"event_type":"docker"}The id: field carries the originating node’s events_log row id —
monotonic per node. Clients echo it on the next reconnect via
Last-Event-ID; the server replays anything newer.
Replay
Section titled “Replay”When the request carries Last-Event-ID: N (or ?last_event_id=N),
the handler:
- Subscribes to the live channel first so events emitted during step 2 are queued.
- Reads the current
MAX(id)fromevents_logfor the tenant — the replay watermarkW. - Streams rows where
id > N AND id <= W, in id order, as SSE frames (capped at 1000 to avoid pinning the connection on a long-gone client). - Switches to forwarding live subscription events, dropping any with
Sequence <= Wso a dupe between replay and live is impossible.
Limitations:
- The sequence space is per-node, not cluster-wide. A multi-node cluster’s replay covers only events the local node has persisted — cross-node events that arrived via the peer-broadcast path are included once the receive path persists them (see below); events the node never observed are not replayable.
- The 1000-frame cap means a client offline long enough to accumulate more than 1000 missed events will see a gap. Reconnect aggressively if your workload generates events faster than your clients consume them.
Event classification
Section titled “Event classification”event: line is derived from the Event:
| Source | Classified as |
|---|---|
IsAudit == true or code prefix A | audit |
IsError == true or code prefix E | error |
Code prefix D | docker |
| Anything else | event |
GET /api/method/shc.events.errors.stream
Section titled “GET /api/method/shc.events.errors.stream”Same wire shape as the main stream but filtered to IsError == true
events only. Useful for ops dashboards that only care about failure
signals.
The type= query parameter is ignored on this endpoint (the
errors-only filter overrides). All other query parameters work
identically.
POST /internal/events
Section titled “POST /internal/events”Cross-node peer-broadcast receiver. Not for external use — the
NodeBroadcaster calls this on every reachable peer when this node
publishes an event.
Request
Section titled “Request”| Header | Required | Description |
|---|---|---|
X-SHC-Source-Node | Yes | Originating node id. Missing → 400. |
Content-Type: application/json | Yes | Body is one Event.ToDict() envelope. |
Body shape mirrors the JSON data: payload of the SSE frame format
(see above) — same keys, same types.
Behavior
Section titled “Behavior”- If
X-SHC-Source-Nodeequals this node’s own SourceNode the request is dropped with204 No Content(loop prevention per B12 Bug 5). - The envelope is decoded into an
Event; the receiver persists it to localevents_logwithevents_pending_broadcast=false(the originating node owns the broadcast lifecycle). - Local subscribers receive the event via the in-process bus; SSE replay for subsequent reconnects will include the persisted row.
Responses
Section titled “Responses”| Status | Meaning |
|---|---|
200 OK | Event persisted + delivered to local subs. |
204 No Content | Self-loop — dropped without action. |
400 | Missing X-SHC-Source-Node header or invalid body. |
500 | Bus not running or persistence failure. |
The broadcaster’s retry queue applies backoff to non-2xx responses; 2xx (including 204) is treated as acked.