Generic registry of SSE streams with channel-based filtering and identity-keyed disconnection.
Subscribers connect with optional channel filters, a capped scope, and
uncapped groups. Broadcasts go to a specific channel and reach only
matching subscribers. close_by_identity force-closes all subscribers
whose scope or groups contain the given key — use for auth revocation.
Two-phase registration
A stream is authorized once, at open, so a revocation has to be able to
reach it from the moment its credential is read. A route's gates run before
the stream exists, and each revocation closes only the streams *registered*
under what it revoked — registering after the gates leaves a window in which
a revocation closes nothing and the stream then opens on a dead session, or
under a role its actor no longer holds.
So a route that authenticated a revocable credential registers in two
phases: subscribe_pending registers the subscription un-admitted —
closeable by every close_by_identity, and otherwise inert: it has no
stream, so no broadcast reaches it, the cap neither counts nor evicts for
it, and count does not see it (pending_count does). The route then
re-reads the credential and the role, and admit attaches the stream and
opens the feed — or refuses when a revocation closed the registration in
between, evicting nothing. The cap's eviction happens at admission.
subscribe stays as the one-step form for a stream with nothing to revoke
behind it. The twin of the Rust fuz_realtime SseRegistry
(subscribe_pending / admit / subscribe).
Shutdown
close_all — the close AppServer.close runs — closes every stream,
pending or admitted, and leaves the registry closing for good: every
later registration is born closed. subscribe_pending registers nothing,
so admit refuses the handle and the route's stream ends after its connect
comment; subscribe closes the stream it is given at once. A stream still
opening when the server shuts down therefore ends rather than outliving it.
The flag is set in the same synchronous step that closes the live streams.
examples
const registry = new SseRegistry<SseNotification>();
const unsubscribe = registry.subscribe(stream, {channels: ['runs']});
registry.broadcast('runs', {method: 'run_created', params: {run}});
unsubscribe();
const unsubscribe = registry.subscribe(stream, {
channels: ['audit_log'],
scope: session_hash,
groups: [account_id],
});
registry.close_by_identity(account_id);
registry.close_by_identity(session_hash);
const pending = registry.subscribe_pending({scope: session_hash, groups: [account_id]});
if (!(await credential_is_still_live())) {
pending.unsubscribe();
return unauthorized();
}
const {response, stream} = create_sse_response(c, log);
stream.on_close(pending.unsubscribe);
if (!registry.admit(pending, stream)) {
stream.close();
} else {
stream.on_close(
schedule_credential_deadline(get_resolved_auth(c)?.credential_deadline ?? null, () =>
stream.close()
)
);
}
return response;