Skip to content

Restricted data: govern restricted reads by observer verification - #382

Draft
Maximo-Guk wants to merge 5 commits into
restricted-data-flag-renamefrom
restricted-data
Draft

Restricted data: govern restricted reads by observer verification#382
Maximo-Guk wants to merge 5 commits into
restricted-data-flag-renamefrom
restricted-data

Conversation

@Maximo-Guk

Copy link
Copy Markdown
Member

WIP

Maximo-Guk and others added 5 commits August 28, 2026 14:45
Restate what `ObservationDescription.containsRestrictedData` means now that the
enforcement is per-collaborator observer verification rather than an
all-or-nothing sharing lockdown, and state the two limits of the model plainly:
verification is held to the collaborator's role scope, and enforcement is at
admission rather than at each read.

No functional change; the implementation follows.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ion.

Reading restricted data no longer locks the workspace down. The old model
blocked the observation outright if the workspace was shared and then refused
all future sharing, which made every sensitive data source unusable the moment
a workspace had a single collaborator. The observer verification machinery
already answers the real question -- does this collaborator have access to the
same data? -- at every open, and a widening of that scope now restarts every
live session, so admission is a sound enforcement point.

So: drop the `hasAnyShares()` block in `authorizeObservation` and the three
guards on the sharing mutators. Keep the two guards that are about leaking data
back out rather than about who may see it -- no actions and no public web
fetches once the latch is set.

What replaces them is narrower. A producer nobody can ever be verified against
(a vendorless connection, or a legacy record with no `creationSpec`) is still
refused while the workspace is shared, because `#inScopeGatekeepers` skips it
and so admission cannot see it at all. Removing a producer's record is blocked
while the workspace is shared, since that record is what verification runs
against. And a new grant -- a collaborator, a share link, another key for one,
or a redemption -- is refused if some producer can no longer verify anyone.
Each of those checks runs in the same synchronous block as the write it gates,
after every await, so a concurrent change cannot slip between check and write.

`sharing.ts` loses `hasAnyShares()` and gains an optional `assertGrantAllowed`
on each grant-writing method, invoked at that write.

Two smaller things fall out. `getSharingManager()` moves inside the
`containsRestrictedData` branch, so an ordinary observation on a cold DO no
longer pays for an owner User DO round trip; the producer record is then read
after that await, since latching against a stale record would permanently brick
sharing. And the restart on a terminal re-verification failure is hoisted ahead
of the best-effort rollback, taking a gatekeeper RPC fan-out off the path
between determining the denial and the abort.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Covers the latch (what sets it, and the cases that must refuse the read rather
than latch), the producer-removal guard and its exemptions, the grant checks on
each sharing mutator, and the tolerance for action records written before the
flag's rename.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Drives the model end to end through the test gatekeeper: a restricted read on a
shared workspace, the unverifiable-producer refusal, the removal guard, the
action and web-fetch blocks, and the restart that forces re-verification when
scope widens.

`TestSession.readThing()` takes an optional `restricted` flag so a test can trip
the latch through the same `ApprovalQueue` funnel a shipping gatekeeper uses.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Rewrites the observer document's model section around admission-time
enforcement, states the two limits (role-scoped verification, and enforcement
at admission rather than at each read) as edge cases with their reasoning, and
records the design under plans/restricted-data-sharing.md -- including the
known risk of a producer no gadget binds.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-actions github-actions Bot added kernel Changes to the Workshop kernel workshop/shared Changes to shared Workshop APIs labels Aug 28, 2026
@github-actions

Copy link
Copy Markdown

Preview: pr382-restricted-data

https://pr382-restricted-data-router.cloudflare-os-previews.workers.dev

Dashboard · deleted when this PR closes

@ask-bonk

ask-bonk Bot commented Aug 28, 2026

Copy link
Copy Markdown
  • High: packages/workshop-backend/src/overseer.ts:8028 excludes unbound producers from use verification. Restricted data can be copied into gadget state/UI, then exposed to a use collaborator who never passes that producer’s addObserver(). Include all restricted producers in use scope or prevent their data reaching gadget-visible state.

github run

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

kernel Changes to the Workshop kernel workshop/shared Changes to shared Workshop APIs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant