Skip to content

feat: add ReBAC event relays and live document updates - #1594

Closed
JohnChantz wants to merge 6 commits into
mainfrom
feat/event-relays
Closed

feat: add ReBAC event relays and live document updates#1594
JohnChantz wants to merge 6 commits into
mainfrom
feat/event-relays

Conversation

@JohnChantz

Copy link
Copy Markdown
Contributor

What kind of change does this PR introduce?

  • Bugfix
  • Feature
  • Code style update
  • Refactor
  • Build-related changes
  • Other (please describe)

Does this PR introduce a breaking change?

  • Yes
  • No

Local Compose Mongo now runs as a single-node replica set (rs0) so change streams work. Existing deployments are unchanged; local DB_CONN_URI must include replicaSet=rs0.

The PR fulfills these requirements:

  • It's submitted to the main branch
  • When resolving a specific issue, it's referenced in the PR's description (e.g. fix #xxx, where "xxx" is the issue number)

If adding a new feature, the PR's description includes:

  • A convincing reason for adding this feature

Other information:

Apps need two live-update paths that REST polling cannot cover: forwarding exact Redis bus events to Socket.io, and watching opted-in MongoDB collections for document changes. Both stay authorization-aware (ReBAC on relays; schema/document checks on live updates) and keep secrets out of browser handshakes.

Summary

  • Event relays (router): Admin-configured, ephemeral mappings from an exact Redis bus channel to a templated Socket.io event on /events/. Clients subscribe with relayId + resourceId; rooms are derived server-side. Subscribe is allowed only when User:<id> has the relay permission on <resourceType>:<resourceId>. Admin CRUD is at /router/event-relays.
  • Live document updates (database): Opt-in MongoDB change streams (realtime.enabled plus modelOptions.conduit.realtime.enabled). Clients connect to /database/ (path /realtime) and emit subscribe / unsubscribe. Events are change metadata only (no document fields); consumers refetch through authorized REST. Admin clients use POST /realtime/ticket for a 30-second handshake token.
  • Local Mongo replica set: Compose initializes a single-node rs0 (docker/mongo/init-replica.sh + keyfile) because change streams require a replica set or sharded cluster.

Test plan

  • Router event-relay unit tests (modules/router/src/event-relays/*.test.ts): channel matching, template rendering, ReBAC authorize, subscribe/unsubscribe rooms
  • Database realtime unit tests (modules/database/src/realtime/__tests__/): coordinator, authorize, topology, subscriptions, rooms, normalize, status
  • Core admin realtime tests (packages/core/src/admin/realtime/*.test.ts): ticket, handshake, namespace
  • docker compose --profile mongodb up: Mongo becomes replica set rs0; DB_CONN_URI includes replicaSet=rs0
  • Manual: create an event relay, connect to /events/ with a user token, confirm subscribe is denied without the ReBAC grant and allowed with it
  • Manual: enable realtime.enabled, opt a schema in, subscribe on /database/, mutate a document, confirm a change event with no document payload
  • Manual: admin socket via POST /realtime/ticket (session JWT / masterkey not sent from the browser)

function extractDocumentId(id: unknown): string | null {
if (id === undefined || id === null) return null;
if (typeof id === 'string' || typeof id === 'number') return String(id);
if (typeof id === 'object' && id !== null && 'toHexString' in id) {
const hex = (id as { toHexString: () => string }).toHexString();
return typeof hex === 'string' && hex.length > 0 ? hex : null;
}
if (typeof id === 'object' && id !== null && 'toString' in id) {
Forward exact Redis bus events as templated Socket.io messages on
/events/, with resource-scoped rooms and local-only HA emission.
MongoDB change streams require a replica set, so local Compose now
initializes a single-node rs0. Schemas can opt in via realtime.enabled.
Mongo 4.4 keyfiles reject hyphens, and rs.status() returns ok:0 instead of
throwing, so Compose never elected a primary for replicaSet URIs.
Stop duplicate admin/router deliveries and apply handshake auth on connect.
Retry change-stream leadership after lock races.
Extend realtime beyond MongoDB so PostgreSQL, MySQL, MariaDB, and SQLite
emit the same metadata-only change events using table triggers and a
durable internal log.
@JohnChantz

Copy link
Copy Markdown
Contributor Author

Split into three stacked PRs for review:

  1. feat(router): add ReBAC-protected event-to-socket relays #1600feat(router): add ReBAC-protected event-to-socket relays
  2. feat(database): stream live document updates over admin sockets #1601feat(database): stream live document updates over admin sockets (stacked on feat(router): add ReBAC-protected event-to-socket relays #1600)
  3. feat(database): add SQL live updates via triggers and change-log #1602feat(database): add SQL live updates via triggers and change-log (stacked on feat(database): stream live document updates over admin sockets #1601)

Tree is identical to this branch (feat/event-relays left in place as backup). Closing this combined draft in favor of the split.

@JohnChantz JohnChantz closed this Sep 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant