Skip to content

Live events (SSE)

MeroKit streams a node’s real-time context events over Server-Sent Events. The task-oriented walkthrough is the live events guide; this page is the API surface.

nonisolated func events(
contextIds: [String], groupIds: [String] = []
) -> AsyncThrowingStream<ContextEvent, Error>

Opens GET /sse (bearer token in the Authorization header) and subscribes to the given context and group ids via POST /sse/subscription, then yields a ContextEvent per emission. It is nonisolated — callable without await — and reconnects automatically ~3s after a drop (the node persists session subscriptions). Consume it with for try await; cancel the enclosing Task to close the connection.

let task = Task {
for try await event in mero.events(contextIds: [contextId]) {
print(event.contextId, event.kind)
await reload()
}
}
// later:
task.cancel() // closes the SSE stream
public struct ContextEvent: Sendable {
public let contextId: String // "" for a group-keyed event
public let kind: String // "StateMutation", "SyncStatus", "Ephemeral", "MemberJoined", ...
public let payload: JSONValue // raw event JSON
public let groupId: String? // set for a group-keyed event (core rc.83)
public var presenceAccount: String? { get } // Ephemeral: the relayed account (rc.83)
public var presenceAuthor: String? { get } // Ephemeral: the author's signing key
}

Group events (core rc.83). Pass groupIds to also receive group-keyed events: membership (MemberJoined, MemberAdded, MemberRemoved, carrying memberAccount and maybe role) and migration (MigrationStarted, MigrationProgress, CascadeProgress, MigrationCompleted). They arrive with groupId set. groupIds is sent only when non-empty, so a context-only subscription is the same body an older node accepts. A client key can only subscribe to the contexts and groups it is bound to.

Presence. An Ephemeral event’s data.account names the account behind a presence update a relay carried; presenceAccount reads it.

For a state change (kind == "StateMutation") the encoded contract events live under payload’s data.events[].data.

mero.events(...) builds an SseClient for you; use it directly only if you need to manage the connection yourself.

public final class SseClient: @unchecked Sendable {
public init(
baseURL: URL,
token: @escaping @Sendable () async -> String?,
session: URLSession = .shared
)
public func events(
contextIds: [String], groupIds: [String] = []
) -> AsyncThrowingStream<ContextEvent, Error>
}