You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
2.0: Scope event subscriptions by client interest #36443
/api/event currently subscribes every connected client to the process-global EventV2 bus. Every TUI receives events for unrelated locations, workspaces, and sessions, so server and aggregate client work scale with all connected TUIs rather than interested TUIs.
Goal
Introduce explicit event subscription scope so clients receive only events they need, while preserving a small, well-defined channel for truly global events.
Requirements
Define subscription identity in terms of project/location, optional workspace, and optional session interest.
Filter before events enter subscriber queues and before schema encoding/JSON serialization.
Define how a client updates its interest as the active route/session changes.
Avoid overlapping streams or duplicate delivery during scope changes.
Preserve global events such as server lifecycle/update notifications through an explicit global scope.
Document reconnect and resume semantics.
Acceptance criteria
A client scoped to session A does not receive session B events.
A client scoped to one location does not receive unrelated location events.
One event is delivered exactly once to each eligible client.
Opening unrelated TUIs does not increase serialization count for an active session.
Tests cover 11 concurrent clients with disjoint and overlapping scopes.
A fan-out benchmark demonstrates cost proportional to interested subscribers, not total subscribers.
A current next-15834 capture confirms this failure mode with nine TUIs and eight running sessions: unrelated clients received identical stream-byte deltas while each consumed sustained CPU, and the TUI data reducer created/reactively mutated foreign-session message state. Full process, network, database, and origin/v2 source evidence is in #36441: #36441 (comment). The server now encodes each event once, but queue/write, client parsing/dispatch, and projection still scale with total subscribers.
#37792 tracks the client-side complement to scoped delivery. At the pinned next-15834 revision, DataProvider.handleEvent accepts every emitted event, and message.update(sessionID) creates a transcript array plus retained message-index Map for an unseen event-owned Session. Other handlers create foreign active, pending, input, permission, and form state or trigger location refreshes.
Server-side scoping remains the primary fix here. The separate TUI issue defines the defense-in-depth rule and tests: an event for Session A must not create keys or HTTP side effects in a client following only Session B, while explicitly followed cross-location child Sessions must continue to work.
I'm going to assess a little bit deeper what I ended up with, but I've burned a lot a lot a lot of tokens designing this out, just eager to share. This is a decent exploraiton that I am going to try to figure out how to deliver incrementally.
@thdxr research from a contributor workspace confirms the gap: GET /api/event takes no query parameters (packages/protocol/src/groups/event.ts:44-47) and its description says "across all server locations". This means every remote bot receives every event from every session on the host and filters client-side — a privacy and scaling problem. Already labeled perf/core/2.0 and part of epic #36441. The workspace finds this the single most impactful infrastructural gap for any chat-bot integration. Proposing to take the PR since it's pure infrastructure (no UI, perf-labelled, skips design-review gate).
Thanks for the write-up. I checked the current event pipeline on dev (77a0fd6f). It works the way you describe, but by design:
The /api/event subscribe query only takes an optional types filter, and its schema says "When omitted, all events are sent" (packages/protocol/src/groups/event.ts).
EventFeed.make (packages/server/src/event-feed.ts) listens on the process-wide bus. It encodes each event once and pushes the shared string onto every connected subscriber whose types set matches. Nothing scopes delivery by location, workspace or session.
This is documented behaviour, not a defect I can reproduce, so I'm treating it as feature/design work under #36441. The scoping, update and reconnect semantics, plus the 11-client tests and fan-out benchmark from the acceptance criteria, would all be new work in those two files.
Activity
kitlangton commentedon Jul 19, 2026
A current
next-15834capture confirms this failure mode with nine TUIs and eight running sessions: unrelated clients received identical stream-byte deltas while each consumed sustained CPU, and the TUI data reducer created/reactively mutated foreign-session message state. Full process, network, database, andorigin/v2source evidence is in #36441: #36441 (comment). The server now encodes each event once, but queue/write, client parsing/dispatch, and projection still scale with total subscribers.kitlangton commentedon Jul 19, 2026
#37792 tracks the client-side complement to scoped delivery. At the pinned
next-15834revision,DataProvider.handleEventaccepts every emitted event, andmessage.update(sessionID)creates a transcript array plus retained message-indexMapfor an unseen event-owned Session. Other handlers create foreign active, pending, input, permission, and form state or trigger location refreshes.Server-side scoping remains the primary fix here. The separate TUI issue defines the defense-in-depth rule and tests: an event for Session A must not create keys or HTTP side effects in a client following only Session B, while explicitly followed cross-location child Sessions must continue to work.
rektide commentedon Sep 5, 2026
I'm going to assess a little bit deeper what I ended up with, but I've burned a lot a lot a lot of tokens designing this out, just eager to share. This is a decent exploraiton that I am going to try to figure out how to deliver incrementally.
https://github.com/rektide/opencode/tree/bus-smart
raihan-js commentedon Oct 12, 2026
@thdxr research from a contributor workspace confirms the gap:
GET /api/eventtakes no query parameters (packages/protocol/src/groups/event.ts:44-47) and its description says "across all server locations". This means every remote bot receives every event from every session on the host and filters client-side — a privacy and scaling problem. Already labeledperf/core/2.0and part of epic #36441. The workspace finds this the single most impactful infrastructural gap for any chat-bot integration. Proposing to take the PR since it's pure infrastructure (no UI,perf-labelled, skips design-review gate).opencodejr commentedon Oct 28, 2026
Thanks for the write-up. I checked the current event pipeline on
dev(77a0fd6f). It works the way you describe, but by design:/api/eventsubscribe query only takes an optionaltypesfilter, and its schema says "When omitted, all events are sent" (packages/protocol/src/groups/event.ts).EventFeed.make(packages/server/src/event-feed.ts) listens on the process-wide bus. It encodes each event once and pushes the shared string onto every connected subscriber whosetypesset matches. Nothing scopes delivery by location, workspace or session.This is documented behaviour, not a defect I can reproduce, so I'm treating it as feature/design work under #36441. The scoping, update and reconnect semantics, plus the 11-client tests and fan-out benchmark from the acceptance criteria, would all be new work in those two files.