Docs/Serverv1.0.0-rc.1

Server-Sent Events

Package: @tagna/udiot/server API: createEventStream() Added: v0.16.0

createEventStream() provides a small Express-compatible Server-Sent Events helper for browser live updates. It is designed for read-only streams such as agent progress, build logs, notifications, and telemetry tails. Client input should arrive through normal HTTP requests.

Basic Usage

import express from 'express';
import { createEventStream } from '@tagna/udiot/server';

const app = express();
const runs = createEventStream({
  channel: 'runs',
  heartbeatMs: 15000,
});

app.get('/events/runs', runs.handler);

app.post('/runs/:id/start', async (req, res) => {
  await runs.publish({ runId: req.params.id, status: 'started' }, 'run');
  res.status(202).json({ ok: true });
});

Browser client:

const source = new EventSource('/events/runs');

source.addEventListener('run', (event) => {
  const payload = JSON.parse(event.data);
  console.log(payload.runId, payload.status);
});

Options

Option Default Description
channel required Logical stream channel name
heartbeatMs 15000 Sends SSE comments to keep proxies and browsers connected. Set 0 to disable
authorize none Optional async request guard before the stream opens
backend 'memory' 'memory' or 'redis'
redis none Required for Redis backend, with separate publisher and subscriber clients
corsOrigin none Optional Access-Control-Allow-Origin value
replayBufferSize 100 Events kept per instance and replayed after the browser's Last-Event-ID when it reconnects. 0 disables event ids and replay
maxSubscribers unlimited Concurrent connections per instance; extra clients get 503
maxBufferedBytes 1 MiB A client whose unsent output grows past this is disconnected; it reconnects and replays what it missed

Payloads are framed safely: CR, LF and CRLF in data all start a new data: line, so text cannot inject event:, id: or retry: fields, and publish() rejects event types that contain line breaks.

Auth Gate

Supply a real application-owned verifyRunAccess(req) function that checks credentials and authorization for the requested tenant/channel. A header being present is not authentication. The basic public-stream example above has no authentication; do not use it for private run data. Browser EventSource cannot set an arbitrary Authorization header: design secure session/cookie-based access or an appropriate client integration, and do not put long-lived tokens in URLs.

const stream = createEventStream({
  channel: 'org-runs',
  // Application-owned: verify credentials AND access to this stream/channel.
  authorize: async (req) => verifyRunAccess(req),
});

When authorize returns false, the handler responds with 401 and does not subscribe the connection.

Redis Backend

Use Redis when more than one process must publish to the same channel.

import Redis from 'ioredis';
import { createEventStream } from '@tagna/udiot/server';

const stream = createEventStream({
  channel: 'runs',
  backend: 'redis',
  redis: {
    publisher: new Redis(process.env.REDIS_URL),
    subscriber: new Redis(process.env.REDIS_URL),
  },
});

The subscriber connection is dedicated to Redis subscribe mode. Do not reuse it for regular Redis commands.

Shutdown

Call close() during graceful shutdown to end open responses and unsubscribe from Redis channels.

process.on('SIGTERM', async () => {
  await stream.close();
  process.exit(0);
});

Relationship To Run Events

createEventStream() is generic. To stream agent progress to a browser, bridge RunEventStream into SSE:

import { getDefaultRunEventStream } from '@tagna/udiot/telemetry';

const sse = createEventStream({ channel: 'run-events' });
const runEvents = getDefaultRunEventStream();

for await (const event of runEvents.subscribe('run_123')) {
  await sse.publish(event, 'run-event');
}

The SSE helper handles browser transport; RunEventStream handles run-specific history, replay, and telemetry semantics.

From the canonical UDIOT public documentation.Try it in the playground ↗