Skip to content
Ironflow is pre-1.0: APIs can change between releases. Pin a version. What is ready →

Subscriptions

Subscription patterns determine which events are delivered to a subscriber. Ironflow uses NATS-style wildcards for flexible pattern matching.

TokenDescriptionExample
*Matches one segment.system.run.*.created
>Matches all remaining segments.events:>

For real-time subscriptions, the SDK maintains a persistent connection (WebSocket or ConnectRPC stream).

import { ironflow } from "@ironflow/browser";
// 1. Connect to the stream
await ironflow.connect();
// 2. Monitor connection state
const off = ironflow.onConnectionChange((state) => {
console.log(`Connection state: ${state}`); // 'connected', 'reconnecting', etc.
});
// 3. Disconnect when finished
ironflow.disconnect();
off();

Browser (Singleton):

import { ironflow } from "@ironflow/browser";
await ironflow.subscribe("events:order.*", { onEvent: (e) => {} });

Node.js (Instance):

import { createSubscriptionClient } from "@ironflow/node";
const subClient = createSubscriptionClient({
serverUrl: "http://localhost:9123",
apiKey: "...",
});
await subClient.connect();
const sub = await subClient.subscribe("system.run.>", { onEvent: (e) => {} });

Set replay to receive the last N events of the pattern first. The same subscription then delivers new events as they arrive. replay is a count, not a position. To resume after a known event, use startAfterSequence (see below).

await ironflow.subscribe("topic:orders", {
replay: 10,
onEvent: (event) => console.log(event.topic, event.data),
});

Regular subscriptions are ephemeral and stored in-memory on the server.

  1. Disconnect: If a client disconnects, the subscription is removed from the server.
  2. Reconnect: SDKs restore the pattern, filter, consumer group, metadata, acknowledgment, backpressure, and namespace options.
  3. Position: The start position depends on how you subscribed.

replay applies only to the initial subscription. Reapplying the same count on every reconnect would repeat an arbitrary historical window. On reconnect:

  • A consumer group resumes from its server-owned durable position.
  • A fan-out subscription with startAfterSequence resumes after the last sequence delivered to the caller. Delivery remains at least once.
  • An unpositioned fan-out subscription starts at the current tail and may miss events published while it was offline.

Use startAfterSequence for positioned fan-out delivery. Do not combine it with replay or a consumer group.

onEvent is not awaited, so advance the cursor only after your own work completes. An async handler runs unsupervised: the next event can be delivered before it settles, and a rejection becomes an unhandled rejection rather than reaching onError. Serialize the handler yourself if the cursor must never run ahead of the work it stands for.

await ironflow.subscribe("events:>", {
startAfterSequence: lastDeliveredSequence,
onEvent: (event) => console.log(event),
});

Two server-side caps apply to subscriptions. Both are fixed defaults — there is no flag, env var or YAML key that changes them.

LimitValueOn breach
Subscriptions per WebSocket connection, plain subscriptions and consumer-group joins together100The subscribe is rejected with LIMIT_EXCEEDED. A ConnectRPC Subscribe call is its own stream and is not counted.
replay count, on WebSocket and ConnectRPC1000The subscribe is rejected with the message replay limit exceeded. ConnectRPC returns invalid_argument. WebSocket maps no code for this one, so it arrives as internal.

Use startAfterSequence rather than a large replay when you need to resume a long gap — it is a cursor, not a bounded backlog request.