Skip to content

Owner-invites-only latch for workspaces that observed opted-in sensitive data - #523

Merged
Maximo-Guk merged 8 commits into
mainfrom
maximo/owner-invites-only
Sep 21, 2026
Merged

Maximo-Guk merged 8 commits into
mainfrom
maximo/owner-invites-only

Conversation

@Maximo-Guk

@Maximo-Guk Maximo-Guk commented Sep 17, 2026

Copy link
Copy Markdown
Member

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.

@github-actions github-actions Bot added workshop/frontend Changes to the Workshop frontend kernel Changes to the Workshop kernel workshop/shared Changes to shared Workshop APIs labels Sep 17, 2026
@github-actions

Copy link
Copy Markdown

Preview: pr523-maximo-owner-577bc511

https://pr523-maximo-owner-577bc511-router.cloudflare-os-previews.workers.dev

Dashboard · deleted when this PR closes

Comment thread packages/workshop-backend/src/sharing.ts
Comment thread packages/workshop-frontend/src/ShareModal.tsx Outdated
Comment thread packages/workshop-frontend/src/ShareModal.tsx Outdated
Comment thread packages/workshop-backend/src/sharing.ts Outdated
@ask-bonk

ask-bonk Bot commented Sep 17, 2026

Copy link
Copy Markdown

Submitted 4 actionable inline findings.

github run

@ask-bonk

ask-bonk Bot commented Sep 17, 2026

Copy link
Copy Markdown
  • [P2] WorkspaceOpenErrorPage.tsx:29: Make share-links-disabled retryable. After the owner directly adds the recipient, retry succeeds, but this page only offers “Go to workspaces,” where the workspace is absent until its first successful open.

  • [P3] api.ts:1411: The parenthetical incorrectly implies ownerInvitesOnly disables collaborator verification or sharing entirely. Owner-added collaborators remain supported and undergo normal gatekeeper verification.

github run

@Maximo-Guk
Maximo-Guk marked this pull request as ready for review September 17, 2026 20:53

@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.

Note

This report is out of date. Scroll down for Devin Review's latest report on this PR.

Devin Review found 1 potential issue.

Devin Review

Comment thread packages/workshop-backend/src/overseer.ts
@Maximo-Guk

Copy link
Copy Markdown
Member Author
  • [P2] WorkspaceOpenErrorPage.tsx:29: Make share-links-disabled retryable. After the owner directly adds the recipient, retry succeeds, but this page only offers “Go to workspaces,” where the workspace is absent until its first successful open.

This is fixed in 12c74ba, share links page now has retryable: true, so it shows a Try again button.

  • [P3] api.ts:1411: The parenthetical incorrectly implies ownerInvitesOnly disables collaborator verification or sharing entirely. Owner-added collaborators remain supported and undergo normal gatekeeper verification.

github run

This is also fixed in 12c74ba the containsRestrictedData doc in api.ts no longer suggests verification is skipped.

@ask-bonk

ask-bonk Bot commented Sep 17, 2026

Copy link
Copy Markdown
  • [P2] packages/workshop-backend/src/overseer.ts:10689: The latch is snapshotted before awaiting owner.whoami(), but subscribed afterward. If it flips during that RPC, the update is missed and collaborators receive stale ownerInvitesOnly: false indefinitely. Re-read the latch after the await before subscribing.

github run

@Maximo-Guk

Maximo-Guk commented Sep 17, 2026

Copy link
Copy Markdown
Member Author
  • [P2] packages/workshop-backend/src/overseer.ts:10689: The latch is snapshotted before awaiting owner.whoami(), but subscribed afterward. If it flips during that RPC, the update is missed and collaborators receive stale ownerInvitesOnly: false indefinitely. Re-read the latch after the await before subscribing.

github run

This was pre-existing on main, but fixed this in 0cdc075 by resolving the owner profile before building the snapshot

ask-bonk[bot]

This comment was marked as resolved.

@ask-bonk

ask-bonk Bot commented Sep 17, 2026

Copy link
Copy Markdown

Submitted 1 actionable inline finding.

github run

ask-bonk[bot]

This comment was marked as resolved.

@ask-bonk

ask-bonk Bot commented Sep 17, 2026

Copy link
Copy Markdown

Submitted 1 actionable inline finding.

github run

@ask-bonk

ask-bonk Bot commented Sep 17, 2026

Copy link
Copy Markdown

LGTM!

github run

@ask-bonk

ask-bonk Bot commented Sep 18, 2026

Copy link
Copy Markdown

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.

github run

Comment thread packages/workshop-backend/src/sharing.ts Outdated
Comment thread packages/workshop-backend/src/sharing.ts Outdated
Comment thread docs/sharing.md Outdated
@github-actions github-actions Bot added the gatekeeper Changes to a gatekeeper integration label Sep 18, 2026
Comment thread packages/workshop-backend/src/overseer.ts
@ask-bonk

ask-bonk Bot commented Sep 18, 2026

Copy link
Copy Markdown

Submitted 1 actionable inline finding.

github run

@ask-bonk

ask-bonk Bot commented Sep 18, 2026

Copy link
Copy Markdown

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.

github run

Maximo-Guk and others added 6 commits September 21, 2026 07:21
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>
Maximo-Guk and others added 2 commits September 21, 2026 07:21
…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>
@Maximo-Guk
Maximo-Guk force-pushed the maximo/owner-invites-only branch from bdd3798 to 83d8f0f Compare September 21, 2026 14:27

@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 found 1 new potential issue.

Devin Review

Comment thread packages/workshop-frontend/src/ShareModal.tsx
@ask-bonk

ask-bonk Bot commented Sep 21, 2026

Copy link
Copy Markdown
  • [P1] packages/workshop-backend/src/overseer.ts:5710: The restart preserves activeAgents, which #resumeAgent() resumes without reauthorizing the initiator. A collaborator revoked by ownerInvitesOnly can therefore have their agent continue reading and modifying the workspace after restart. Cancel affected turns or re-run authorizeCollaborator() before resuming them.

github run

@Maximo-Guk

Maximo-Guk commented Sep 21, 2026

Copy link
Copy Markdown
Member Author
  • [P1] packages/workshop-backend/src/overseer.ts:5710: The restart preserves activeAgents, which #resumeAgent() resumes without reauthorizing the initiator. A collaborator revoked by ownerInvitesOnly can therefore have their agent continue reading and modifying the workspace after restart. Cancel affected turns or re-run authorizeCollaborator() before resuming them.

github run

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

@ask-bonk

ask-bonk Bot commented Sep 21, 2026

Copy link
Copy Markdown

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.

github run

@Maximo-Guk
Maximo-Guk merged commit baa4f7c into main Sep 21, 2026
22 checks passed
@Maximo-Guk
Maximo-Guk deleted the maximo/owner-invites-only branch September 21, 2026 15:07
bashandbone added a commit to knitli/knitli-os that referenced this pull request Sep 22, 2026
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
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.

2 participants