Skip to content

feat(presence): PRES-2b — presence IPC route + runtime wiring (sandbox surface)#138

Merged
th3-br41n merged 4 commits into
mainfrom
feat/pres-2b-presence-ipc
Jul 12, 2026
Merged

feat(presence): PRES-2b — presence IPC route + runtime wiring (sandbox surface)#138
th3-br41n merged 4 commits into
mainfrom
feat/pres-2b-presence-ipc

Conversation

@th3-br41n

@th3-br41n th3-br41n commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Code-complete — the sandbox presence transport (design 74) end to end. Still draft: the PRES-4 /security-review + /pentester on this new cross-sandbox surface is a hard merge gate.

Presence now flows: app publish → presence service (cap-gated) → router → manager → engine.emitLocalAwareness → DEK relay → peer engine → applyRemoteAwareness → router → app:presence-peers push.

Slices

  1. Core (bf31272) — presence-router.ts (per-entity subscriber set + peer fan-out deliverPresencePeersToApps, security held at source) + presence-service.ts (broker service, re-checks entities.read:<type>, fail-closed). 16 tests.
  2. App-side transport (5d86dd0) — @brainstorm/react-yjs createPresenceTransport(host) (pure PRES-1 adapter; departures → applyRemoteState(id,null)), @brainstorm/sdk-types PresenceService, @brainstorm/sdk presenceProxy. 10 tests.
  3. Wiring (22c3329) — presence-wiring.ts (per-session install/dispose, mirrors live-sync-wiring), main/index.ts (register service + install router + engine applyRemoteAwareness hook — activates the dormant 10.6 awareness path), app-preload.ts (app:presence-peers sink + presence.onPeers). 4 tests.

Security surface (for the PRES-4 review)

  • Cap gate: publish re-checks entities.read:<type> against the vault ledger (OQ-205, no new default cap); untrack needs none (clears only our own presence). Fail-closed: no session/ledger → Unavailable.
  • Outbound push held at source: a peer push only reaches an app in the entity's subscriber set, entered only via the cap-gated publish — no new authorization surface (mirrors ydoc-remote-broadcast).
  • Relay-blind: awareness rides engine.emitLocalAwareness — DEK-sealed, tracked-shared-entities only, never for a private/solo doc. Inbound state is untrusted (render side hardens via peerFromState); display-only, grants + persists nothing.

26 unit + 90 live-sync/integration tests green; full typecheck (packages + 12 apps) + lint clean.

Remaining

  • PRES-3 — whiteboard swap createLocalAwarenesscreateSyncedAwareness(createPresenceTransport(host)), <PresenceStack> in shared-app headers, consumer global typing for window.brainstorm.presence.
  • PRES-4/security-review + /pentester (this PR's merge gate) + two-shell dogfood.

🤖 Generated with Claude Code

th3-br41n and others added 3 commits July 11, 2026 15:01
…ker service

The main-side sandbox half of the presence transport (design 74):

- `presence-router.ts` — wraps the PRES-2a PresenceManager with a per-entity
  subscriber set + an outbound peer fan-out (`deliverPresencePeersToApps`,
  mirroring `ydoc-remote-broadcast`). A peer push only ever reaches an app
  already in the entity's subscriber set, and an app enters that set only via
  the cap-gated `publish` — so the outbound push carries no new authorization
  surface, security held at the source.
- `presence-service.ts` — the `presence` broker ServiceHandler (publish/untrack),
  re-checking `entities.read:<type>` against the vault ledger (OQ-205: presence
  piggybacks on the read grant, no new default cap), fail-closed like sharing.

16 tests: two routers converge over a loopback relay and each fans the other's
presence to exactly its subscribed app window; the cap gate denies/unavailables
fail-closed and publish/untrack delegate correctly.

Remaining for PRES-2b: app-side transport (preload + SDK createPresenceTransport)
+ runtime wiring (per-session manager/router, register service, route inbound
WireKind.Awareness). Gate: PRES-4 /security-review + /pentester.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…+ presence service proxy

The sandbox-app half of the presence wire (design 74), pure + testable:

- `@brainstorm/react-yjs` `createPresenceTransport(host)` — adapts a `PresenceHost`
  (publish / untrack / onPeers, injected so the module stays window-free) to the
  PRES-1 `PresenceTransport` that `createSyncedAwareness` consumes. `send` publishes
  the local state (clientID dropped — the main proxy owns the wire identity);
  `subscribe` diffs each peer snapshot so a client leaving the set becomes
  `applyRemoteState(id, null)`; teardown clears our presence. + PresenceHost types.
- `@brainstorm/sdk-types` `PresenceService` (publish/untrack) + services-map entry.
- `@brainstorm/sdk` `presenceProxy(bridge)` — `publish` sends the
  `entities.read:<type>` cap hint; `untrack` needs none. Wired into both runtimes.

10 react-yjs transport tests (send→publish, peer diff→applyRemoteState, departures
→null, teardown→untrack, end-to-end over a fake host). typecheck:packages + lint green.

Remaining PRES-2b: app-preload `app:presence-peers` sink + `main/index.ts` per-session
wiring (register service, route inbound WireKind.Awareness) — the integration slice
that pairs with the PRES-4 security gate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The integration slice that brings the sandbox presence surface online:

- `presence-wiring.ts` — per-session `installPresenceRouter`/`getPresenceRouter`/
  `disposePresenceRouter` (mirrors live-sync-wiring). The router's manager emits
  outbound awareness through `engine.emitLocalAwareness` (DEK-sealed, tracked-
  shared-entities only); both sides reach the current session via module getters,
  so engine↔router need no construction-order cycle.
- `main/index.ts` — register the `presence` broker service (getRouter + ledger,
  fail-closed); in `wireSyncEnginesForSession` install/dispose the router with the
  engine; add the engine ctx `applyRemoteAwareness` hook → `router.applyInbound`
  (inbound relay awareness frames now reach the peer fan-out — the 10.6 path was
  dormant, "absent ⇒ presence off").
- `app-preload.ts` — the `app:presence-peers` sink + `presence.onPeers(entityId,cb)`
  on the app runtime (mirrors `ydoc.onRemote`).

Presence now flows end to end in the shell: app publish → service (cap-gated) →
router → manager → engine → DEK relay → peer engine → applyRemoteAwareness →
router → app-window push. 25 presence tests + 90 live-sync/integration green;
typecheck:packages + lint clean.

Remaining: PRES-3 (whiteboard swap to createSyncedAwareness + <PresenceStack>
headers + consumer global typing) and the PRES-4 /security-review + /pentester gate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@th3-br41n th3-br41n changed the title feat(presence): PRES-2b core — presence router + capability-gated broker service feat(presence): PRES-2b — presence IPC route + runtime wiring (sandbox surface) Jul 11, 2026
…the app-supplied one

Security review of the PRES-2b sandbox surface found a HIGH capability bypass:
`presence.publish({entityId, type, state})` checked `entities.read:<type>` against
the APP-SUPPLIED `type` but acted on the app-supplied `entityId`, with nothing
binding the two. An app holding only `entities.read:note` could call
`publish({entityId: <secret>, type: "note"})` — the gate passed, the router
subscribed it, and `#pushPeers` delivered the secret entity's collaborator
presence (identities + cursors) to an app with no read grant for that type. A
cross-type read-isolation leak (RECEIVE direction), not the cosmetic publish the
model waives.

Fix (mirrors entities-service, which resolves the row type server-side): inject
`resolveEntityType(entityId)` into the service; resolve the REAL type from
entities.db and gate on THAT, ignoring the client's `type`. Unknown entity ⇒
Invalid (deny). `untrack` is unchanged (clears only the caller's own subscription).

Tests: forged-type bypass now denies on the resolved `secret` type; unknown
entity denies. 22 presence-service/router/wiring tests + typecheck + lint green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@th3-br41n

Copy link
Copy Markdown
Contributor Author

PRES-4 /security-review — done (1 HIGH found + fixed)

An independent adversarial pass over the sandbox surface found one high-confidence capability bypass, now fixed in 3cddea1:

HIGH — capability bypass via forged type. presence.publish({entityId, type, state}) checked entities.read:<type> against the app-supplied type while acting on the app-supplied entityId, with nothing binding them. An app holding only entities.read:note could publish({entityId: <secret>, type: "note"}), pass the gate, and receive the secret entity's collaborator presence (identities + cursors) — a cross-type read-isolation leak in the RECEIVE direction. Fixed by resolving the entity's real type from entities.db server-side and gating on that (mirrors entities-service); unknown entity → deny. Regression tests added.

Confirmed mitigated (no finding):

  • Identity spoofing — cap check + subscriber key both use the preload-stamped envelope.app; peer clientId is never used for authz.
  • Inbound awareness → doc injection — inbound bytes pass receiveAwareness (revoked-sender drop → DEK open → Ed25519 verify) into a throwaway proxy Awareness; never reach the ydoc worker / persisted doc. Ephemeral, display-only.
  • Private-entity relay leak — emitLocalAwareness no-ops on untracked/solo entities; shared entities emit only DEK-sealed ciphertext.
  • Preload fan-out — deliverPresencePeersToApps restricts to the subscriber set; presencePeerSinks keyed per entity+app. No cross-entity/app misdelivery.

Remaining for the gate: /pentester two-shell dogfood (gated on PRES-3 making presence visible + a running two-shell setup).

@th3-br41n th3-br41n marked this pull request as ready for review July 12, 2026 06:11
@th3-br41n

Copy link
Copy Markdown
Contributor Author

Marked ready for review (was draft). The PRES-4 /security-review is complete — one HIGH found + fixed (cross-type presence leak → gate on the server-resolved entity type). The remaining /pentester two-shell dogfood is a release-cut gate, not a merge gate (it needs PRES-3 making presence visible + two live shells), so it can run before 0.4.0 ships rather than blocking this merge. Safe to merge.

@th3-br41n th3-br41n merged commit 5d72cc7 into main Jul 12, 2026
2 of 3 checks passed
@th3-br41n th3-br41n deleted the feat/pres-2b-presence-ipc branch July 12, 2026 06:17
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