Skip to content

Realtime Events

Polling the REST API for live state is wasteful. For anything realtime — console output, power transitions, metric ticks — subscribe to a stream. Browser-facing realtime is Server-Sent Events (SSE); the true WebSocket gateway carries the agent control plane (node traffic), not browser clients.

Channel Transport Endpoint
Console output SSE GET /api/servers/:serverId/console/stream (25 s heartbeats)
Console input REST POST /api/servers/:serverId/console/command
Server events SSE GET /api/servers/:serverId/events (state, backups, alerts)
Metrics stream SSE/WebSocket metrics endpoints CPU/memory/disk ticks
Node updates Panel WebSocket gateway Node health, agent update progress

Authenticate stream requests the same way as REST (session or API key). Unauthenticated or unauthorized subscribers are rejected before any data flows.

Frames are JSON with a type discriminator (console, node_updated, resource_stats, error, …). Always switch on type and ignore unknown types — new event types are added over time and must not break your client.

  1. Reconnect with backoff and resume from the last seen marker where the channel offers one (resume_console and friends).
  2. Handle error frames (auth_failed, auth_lockout with retryAfterSeconds, connection limits) instead of reconnect-spinning.
  3. One connection per view, not per widget — fan out locally.
  4. Fall back to REST polling only when streams are unavailable, and say so in your UI.
const src = new EventSource(
`/api/servers/${serverId}/console/stream`,
{ withCredentials: true },
);
src.addEventListener('console', ({ data }) => {
appendOutput(JSON.parse(data).line);
});
src.addEventListener('error', handleStreamError);
// Sending input is a normal POST, not a socket frame:
await fetch(`/api/servers/${serverId}/console/command`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${key}` },
body: JSON.stringify({ command: 'say Hello' }),
});

Exact paths, parameters, and event types are pinned in the API reference — treat this page as concepts, the reference as contract.