feat: add row-set subscriptions (ids: [UUID!]) and RLS-aware rowId masking#1108
Merged
pyramation merged 2 commits intomainfrom May 10, 2026
Merged
feat: add row-set subscriptions (ids: [UUID!]) and RLS-aware rowId masking#1108pyramation merged 2 commits intomainfrom
pyramation merged 2 commits intomainfrom
Conversation
… masking - Add ids: [UUID!] argument for sparse set subscription mode - Filter NOTIFY events by row ID intersection with subscribed set - Mask rowId (set to null) when RLS denies access to prevent metadata leaks - Support three subscription modes: single record, sparse set, full collection - Add 11 new tests covering sparse set filtering and RLS-aware delivery - Update docstrings to document security model and subscription modes
Contributor
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
Remove redundant id: UUID argument — a single-element array covers the single-record case. This simplifies the API surface to just two modes: specific rows (ids) and full collection (no args).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Extends
graphile-realtime-subscriptionswith two features:Row-set filtering (
ids: [UUID!]) — subscribers can pass an array of row IDs and only receive NOTIFY events whose row IDs intersect with that set. Events with no matching IDs are silently dropped before re-query.RLS-aware rowId masking — updated docstrings and type definitions to clarify that
rowIdis masked (set tonull) whenresource.get()returns null due to RLS denial, preventing metadata leaks.The subscription field signature changes from:
to:
Two subscription modes are supported:
onXxxChanged(ids: ["uuid-a", "uuid-b"])— pass a single-element array for one rowonXxxChanged(no args) — subscribe to any change on the tableThe previous
id: UUIDsingle-record argument was removed; a single-elementidsarray covers that case without the redundant API surface.Companion PR: constructive-io/constructive-db#1098 adds architecture documentation for the full realtime flow.
Review & Testing Checklist for Human
lambda([$payload, $ids], (pair) => ...)pattern assumespairis a tuple[raw, subscribedIds]. This is tested with mocks that return callback results directly — verify this matches actual Grafast behavior when the plan executes with a realpgSubscriber.subscribedIdscan benull,undefined, or[]. The guardsubscribedIds && subscribedIds.length > 0handles all three, but confirm this matches the GraphQL argument semantics (does omittingidsyieldnullorundefinedfromargs.get('ids')?).id:consumers: Theid: UUIDargument is removed. If any existing subscription queries useid:, they will fail at the GraphQL layer. Confirm no consumers exist yet (this plugin has not been wired into the server preset, so this should be safe).Suggested test plan: Wire the plugin into a PostGraphile server with a
@realtime-tagged table, open a WebSocket subscription withids: [...], and verify that NOTIFY events for non-subscribed rows are filtered out while subscribed rows are delivered.Notes
--noEmitrowIdfield description in the generated schema now explicitly states it is "masked when RLS denies access"subscribedId(singular) code path — all filtering goes throughsubscribedIds(array)Link to Devin session: https://app.devin.ai/sessions/19485cf5cc58416a9f86068563d512f5
Requested by: @pyramation