Skip to content
SHC Docs

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.

MethodPathPurpose
GET/api/method/shc.events.streamTenant-scoped SSE stream of all events
GET/api/method/shc.events.errors.streamErrors-only variant of the stream
POST/internal/eventsCross-node peer-broadcast receiver (private)

Live SSE stream of events for one tenant. Heartbeats every 30s; one SSE frame per event.

ParamRequiredDescription
tenantNoTenant override; honors X-SHC-Tenant header first, then ?tenant=, default "default".
stack_nameNoRestricts to one stack.
node_idNoRestricts to events originated by one node.
typeNoComma-separated whitelist of classified types (audit, docker, error, event).
last_event_idNoFallback resume cursor for clients that can’t set headers.
HeaderDirectionDescription
X-SHC-TenantRequestOverrides ?tenant= when both are present.
Last-Event-IDRequestRFC EventSource resume cursor. See Replay below.
Content-Type: text/event-streamResponseAlways set.
Cache-Control: no-cacheResponseAlways set.
id: 47
event: docker
data: {"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.

When the request carries Last-Event-ID: N (or ?last_event_id=N), the handler:

  1. Subscribes to the live channel first so events emitted during step 2 are queued.
  2. Reads the current MAX(id) from events_log for the tenant — the replay watermark W.
  3. 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).
  4. Switches to forwarding live subscription events, dropping any with Sequence <= W so 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: line is derived from the Event:

SourceClassified as
IsAudit == true or code prefix Aaudit
IsError == true or code prefix Eerror
Code prefix Ddocker
Anything elseevent

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.


Cross-node peer-broadcast receiver. Not for external use — the NodeBroadcaster calls this on every reachable peer when this node publishes an event.

HeaderRequiredDescription
X-SHC-Source-NodeYesOriginating node id. Missing → 400.
Content-Type: application/jsonYesBody is one Event.ToDict() envelope.

Body shape mirrors the JSON data: payload of the SSE frame format (see above) — same keys, same types.

  1. If X-SHC-Source-Node equals this node’s own SourceNode the request is dropped with 204 No Content (loop prevention per B12 Bug 5).
  2. The envelope is decoded into an Event; the receiver persists it to local events_log with events_pending_broadcast=false (the originating node owns the broadcast lifecycle).
  3. Local subscribers receive the event via the in-process bus; SSE replay for subsequent reconnects will include the persisted row.
StatusMeaning
200 OKEvent persisted + delivered to local subs.
204 No ContentSelf-loop — dropped without action.
400Missing X-SHC-Source-Node header or invalid body.
500Bus not running or persistence failure.

The broadcaster’s retry queue applies backoff to non-2xx responses; 2xx (including 204) is treated as acked.