feat(service): seal the account's workspaces for following over Neon Realtime
Merged
CH
8d ago+398+398+012345678901234567890123456789-19-19-01234567890123456789
The service side of the live workspaces list, on the central live layer (liveRows and the sealed live procedures).
- New live procedure
live.workspaces(GET /api/live/workspaces, inlive-procedures.ts). It seals the bearer's workspaces as two statements,workspacesandinstallations, throughsealedParts. WithLUKE_REALTIMEoff, or when polled, it answers the rows inline as a held read. It's gated bysameUserlike every live read. - Statements (
workspacesLiveStatementsinworkspace-store.ts):- same joins and the same rule for who may read as
listWorkspaces - aliased with
liveSelection, with nonow()and noORDER BY - the photo digest is computed in SQL with immutable functions only:
decode(replace(logo, '\', '\\'), 'escape')instead ofconvert_to, giving the same sha256
- same joins and the same rule for who may read as
- Reader (
workspacesOfLiveRowsin the newworkspace-live.ts) orders the rows the way the held read does. roleOfmoves toworkspace-wire.tsso the desktop reads a role the same way the service does.- Export:
sealedLiveQuerySchemais exported again, now that a second module uses it.
Tests
- PGlite parity: the live rows equal
listWorkspacesfor an owner, a teammate and an outsider. - The procedure seals both parts for the bearer and refuses a missing bearer.
live-statements.test.tspasses the statements throughmutableFunctionsOfand the Drizzle adapter.
The live read refuses a forUser other than the bearer (sameUser, 401). Its photo check is the same as workspacePhotoOfDataUrl: a whole base64 payload and a real PNG, JPEG or WEBP signature.
Made with Luke