Skip to content

Govern sharing of restricted data by observer verification - #340

Draft
Maximo-Guk wants to merge 8 commits into
mainfrom
restricted-data
Draft

Govern sharing of restricted data by observer verification#340
Maximo-Guk wants to merge 8 commits into
mainfrom
restricted-data

Conversation

@Maximo-Guk

@Maximo-Guk Maximo-Guk commented Aug 25, 2026

Copy link
Copy Markdown
Member

WIP

Maximo-Guk and others added 8 commits August 25, 2026 17:14
The flag's real meaning is "this observation contains restricted data". What
the platform does about that is policy, which shouldn't be baked into the name
-- the next commits replace the all-or-nothing lockdown with per-collaborator
observer verification.

ObservationDescription.prohibitAllSharing and GadgetMetadata.sharingProhibited
both become containsRestrictedData. No alias: this is a hard rename, so the
gatekeeper call sites move in the same commit.

The overseer's durable singleton keeps its historical storage key, since
typed-storage keys are property names and renaming one would silently unlatch
every workspace that already carries it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
PermissionEdge gains a `pending` flag plus per-attempt `pendingAttempts`
claims, so a redeemed share key can be recorded without granting anything
until its recipient is verified -- and concurrent redemptions of one link can
each withdraw only their own claim on failure.

Also restates what containsRestrictedData means now: sharing is governed by
observer verification rather than banned outright. The server still implements
the old behavior; that changes in the next commit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sharing a workspace that has read restricted data is no longer refused
outright. Instead authorizeObservation admits the observation only when every
current collaborator is already verified as an observer of the producing
gatekeeper, held to each collaborator's role scope.

Share-key redemption becomes two-phase: redeemShareKey writes a pending edge
that grants nothing, the redeeming open() verifies the recipient at the role
that edge would grant, and only then is it confirmed -- capped at the verified
role, rolled back on refusal. This also stops a refused recipient persisting
in the sharing graph, which the previous flow left behind.

receiveExternalMessage now routes through authorizeCollaborator too -- the
single gate both non-owner entry points share -- verifying external callers
(non-interactively) where the old code checked only the role.

Known races in the surrounding observer machinery -- most of them preexisting
-- are deliberately not fixed here: each is marked with a TODO naming the
PR #306 (observer-verification-fixes) commit that addresses it, so this PR
stays reviewable as the model change alone.

Tests follow in a later commit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Carried from PR #306 (observer-verification-fixes, commits ddbbbb5 and
5924efa): the coverage guard (#assertSensitiveObservationCoverage) reads the
*persisted* observer record from other turns, so a collaborator whose live
re-verification just failed must not keep a persisted entry saying they are
covered -- until now a revoked collaborator stayed "verified" for restricted
reads until their next successful open.

fail() now drops the failed gatekeeper from the persisted accountChoices
synchronously with the failure determination, getVerifier moves inside the
per-gatekeeper try so a verifier-acquisition rejection scrubs like any other
refusal (and surfaces the descriptive denial rather than the raw RPC error,
with no mid-flight Promise.all rejection to stale the rollback snapshot), and
the terminal catch de-registers invalidated registrations alongside
newly-added ones.

Carried into this PR because it is a stated precondition of the coverage
guard; the guard's other soundness precondition (the out-of-scope prune)
follows in the next commit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Carried from PR #306 (observer-verification-fixes, commit e1b36c3): the
coverage guard's key-presence check rests on the invariant "entry present =>
verified at this collaborator's most recent open". Without the prune, a "use"
collaborator opening while a connection is unbound from every gadget verifies
nothing against it, yet their stale entry survives to be trusted by the guard
the moment the connection is rebound -- an entry their last open never
verified.

ensureObserver's step 2 now drops every account choice for a gatekeeper
outside the collaborator's live verification scope, even when the remaining
scope is empty (that is exactly the everything-unbound open). The
gatekeeper-side registration is deliberately kept: it preserves forward
exclusion via byObserverId, and the next successful open's addObserver
overwrites the verifier.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
restricted-data.test.ts covers the legacy-name reader; sharing.test.ts gains
the pending-redemption model cases (adoption, claim-counted reverts,
assertGrantAllowed on every grant vector) and loses the hasAnyShares cases
with the method; restricted-producer-removal.test.ts covers the removal guard
and ambient reconciliation; restricted-observation-latch.test.ts pins the
synchronous coverage check and the removed-connection refusal;
verification-scope.test.ts pins the #scopeGeneration topology detection
(commit-gate rollback cases stay with PR #306, whose fix they pin).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
sensitive-observations.test.ts drives the whole model against real DOs and
the extended test gatekeeper (per-resource restricted flag, controllable
verify outcomes): the latch, the coverage guard, pending redemptions and
their concurrent/mid-topology/mid-revocation windows, the producer-removal
guard, and ambient reconciliation. The mid-revocation case pins only the
denial and the inert edge -- the denied open's observer-record residue is
the deferred commit-gate fix (PR #306, commit 0f39a79).

external-message-verification.test.ts proves receiveExternalMessage holds
collaborators to live observer verification (with the gatekeeper's own
refusal reason surfaced) and denies a "use" collaborator by role before
verification runs. observer-role-scope.test.ts covers role-scoped
verification needs.

The fixture gains an external control surface, real per-account sessions,
and empty required-secrets hardening so local .dev.vars never leak into
suites.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
docs/observers.md gains the coverage rules, the residuals, and the
per-collaborator verification flow; docs/sharing.md documents pending
redemption, the policy hooks, and the revocation interplay. Passages that
describe the deferred observer-machinery hardening (the verification commit
gate, per-profile serialization, the revocation-restart fail-closed window,
the exclusion-gate pending map, the external path's commit-time re-assertion)
are written as "Known limitation, see PR #306" notes matching the code's TODO
ledger. The plan doc's commit sequence is updated to reflect this split.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions github-actions Bot added workshop/frontend Changes to the Workshop frontend kernel Changes to the Workshop kernel gatekeeper Changes to a gatekeeper integration workshop/shared Changes to shared Workshop APIs labels Aug 25, 2026

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 1 additional finding.

Open in Devin Review

@Maximo-Guk
Maximo-Guk marked this pull request as draft August 25, 2026 23:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gatekeeper Changes to a gatekeeper integration kernel Changes to the Workshop kernel workshop/frontend Changes to the Workshop frontend workshop/shared Changes to shared Workshop APIs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant