Owner-invites-only latch for workspaces that observed opted-in sensitive data - #523
Conversation
Preview:
|
|
Submitted 4 actionable inline findings. |
|
This is fixed in 12c74ba, share links page now has retryable: true, so it shows a Try again button.
This is also fixed in 12c74ba the |
|
This was pre-existing on main, but fixed this in 0cdc075 by resolving the owner profile before building the snapshot |
|
Submitted 1 actionable inline finding. |
|
Submitted 1 actionable inline finding. |
|
LGTM! |
|
Performance is inconclusive because the 10-trial cohorts used different models and are not comparable. Pass rates shifted from 90%/100%/90% to 0%/0%/100%, while mean durations rose from 39/88/14s to 684/648/56s. Candidate appointment and expense runs averaged zero tool calls and about one turn, producing no required gadget, versus 5.8 and 22.4 baseline tool calls. Model differences and stochastic behavior prevent attributing this apparent regression to the PR. |
|
Submitted 1 actionable inline finding. |
|
Performance is inconclusive because all 10-trial cohorts used different models and are non-comparable. Pass rates shifted from 90%/100%/90% to 0%/0%/100%, while mean durations rose from 39/88/14s to 766/686/101s. Appointment and expense candidates averaged zero tool calls and under one turn, creating no gadget, versus 5.8/22.4 baseline tool calls. Model differences and stochastic behavior prevent attributing these shifts to the PR. |
A gatekeeper can now mark an observation `ownerInvitesOnly`. Once any such observation is authorized, the Overseer permanently latches the workspace: share links can no longer be created, copied, or redeemed by anyone new, and only the owner can add collaborators directly. People who already joined keep access and are still re-verified on every open; the owner can still list, rename, and revoke links. The policy is supplied to `SharingManager` as a hook and checked after each grant's last await, right before the storage write, so an observation that latches mid-call cannot slip a grant through. `GadgetMetadata` reports the latch, the Share modal hides link controls (and the invite box for non-owners), and a refused redemption shows the server's message on open. Co-Authored-By: Claude Code <noreply@anthropic.com>
The latched Share modal banner now keeps the restricted-data caveats (per-level verification, already-saved output is visible) and adds the link restriction in front of them, instead of replacing them. Redemption refusals under the latch carry a stable SHARE_LINKS_DISABLED open-gadget code, and the frontend shows a dedicated open error page for it rather than matching message text. Link management keeps throwing the same message uncoded. Document and test that re-adding a collaborator under the latch still restores the grants they made before it (the lazy undo). Co-Authored-By: Claude Code <noreply@anthropic.com>
The share key is consumed on the first attempt, so a retry reopens without it and succeeds once the owner adds the person directly. The workspace isn't listed for them until that first successful open, so "Go to workspaces" alone was a dead end. Also reword the containsRestrictedData doc so it no longer implies ownerInvitesOnly skips collaborator verification. Co-Authored-By: Claude Code <noreply@anthropic.com>
subscribeToMetadata() read storage into the snapshot, awaited owner.whoami(), then subscribed. An update landing during that RPC was never delivered, so a collaborator could keep a stale ownerInvitesOnly (or title, totalCost, containsRestrictedData) until reopening. Await whoami() first so the read and subscribe run with no await in between, in both the build and use interfaces. Co-Authored-By: Claude Code <noreply@anthropic.com>
…tions test. The test awaited newGatekeeper() and then called getId() as a second round trip on the owner's connection. The scope-widening restart aborts the DO ~100ms after newGatekeeper() returns, which drops that connection, so on a loaded CI runner getId() failed with "Peer closed WebSocket: 3000 RPC session was shut down by disposing the main stub". Pipeline getId() onto the creation so the server answers it in the same batch. Co-Authored-By: Claude Code <noreply@anthropic.com>
The dialog description now follows the capability flags ("Invite people." for
a latched owner, "Manage access." for a latched collaborator), and the
directory results count as open only while the invite box exists. Otherwise a
live metadata update that latched the workspace with results open unmounted
the search field without blurring it, leaving the dialog body scroll-locked.
Co-Authored-By: Claude Code <noreply@anthropic.com>
…hem latches. Kenton pointed out that "latch" was used for several different things. Docs, comments, test names and fixtures now name the flag they mean. No behavior change. Co-Authored-By: Claude Code <noreply@anthropic.com>
Once ownerInvitesOnly is set, computeEffectiveRoles skips share-link edges and user edges from anyone but the owner, so open(), the listing and the previews agree. People who joined through a link or another collaborator lose access, and setting the flag restarts the workspace if anyone did. Re-adding an intermediary no longer brings back the people they had added. Redeeming a link under the flag lets direct collaborators through and refuses everyone else with shareLinksDisabled. Per review, the grants check the flag only after their last await. Co-Authored-By: Claude Code <noreply@anthropic.com>
bdd3798 to
83d8f0f
Compare
|
Pre-existing and not specific to ownerInvitesOnly, removeCollaborator and revokeShareLink use the same restart and resume the removed user's turn the same way currently. This is a known issue/accepted risk at the moment |
|
Performance is inconclusive because all three 10-trial cohorts had changed eval definitions and are non-comparable. Pass rates stayed 100% and 90% for appointment/project, but expense fell from 80% to 40%; mean durations rose from 35/77/11s to 42/211/14s. Expense runs used fewer turns and tool calls, dropping from 18.5/22.5 to 11.2/15.9 despite taking much longer. Eval changes and stochastic behavior prevent attributing this apparent expense regression to the PR. |
Upstream `2e37d67f..87e09fe` (21 commits), merged per `docs/fork-maintenance.md`. ## Resolution highlights - **cloudflare#523 owner-invites latch**: adopted, but routed through the gated `runRevocationCleanup` instead of upstream's ungated `scheduleAccessRestart` (new optional reason param). Upstream's latch test re-stubbed to the fork seam, same intent. - **cloudflare#493/cloudflare#513 agent + worktree rewrites**: turn options thread through a new trailing `RunAgentOptions` on `runAgent`/`runAgentPass`; blindfold re-applied to the new replay loop; `knitli-*` harnesses migrated to `loadChatHistory`. - **cloudflare#474 admin normalize**: re-pointed at the fork's opt-in `enabledResources`, preserving per-path case behavior (KV parse lowercases, AdminSettings read preserves). - **cloudflare#466 bundled-blueprints rename**: reconciled; backend `wrangler.jsonc` takes the v3 migration, formatting normalized to upstream. - **Add/add** `GatekeeperModal.test.tsx`: upstream's file kept byte-identical, fork tests moved to `GatekeeperModal.fork.test.tsx` (new Tier-1 entry). ## Fork tooling - `fork-boundary.json`: +35 Tier-1 entries for previously undeclared fork files; `reviewedSurvivors` pruned (22 stale) + 9 new acks, all false-positive class. - Inventory: documented two previously unrecorded divergences (opt-in resources, full-SHA commit capabilities) plus this sync's fallout. ## Verification - `fork:sync --verify`: exit 0 (survivors, uncached typecheck, audit clean — no dropped hunks, no format drift, no collisions) - `pnpm lint`: green; full uncached suite on Node 24: 43/43 tasks green
This PR lets a gatekeeper mark an observation
ownerInvitesOnly. It's meant for data sources whose own sharing model grants access one person at a time, like for a gatekeeper who doesn't want to allow sharing links period.Once any such observation is authorized, the workspace is permanently latched, share links stop admitting anyone new, and only the owner can add people directly.