Skip to content

Deleting a private Context collection permanently locks collaborators out of every workspace that observed it #326

Description

@pol-cc

Summary

When a workspace's agent reads the Context library, the ambient Context gatekeeper records every enabled collection as observed (observedCollection:<id> keys written by ContextObserverTracker, packages/gatekeeper-context/src/context-observers.ts) — including the owner's private collections, since getAgentCatalog() authorizes an observation over all catalog entries. Collaborator verification (ContextObserverTracker.addObserver) then requires each observed collection to pass ContextVerifier.hasCollectionAccess, i.e. owns || isPublic.

While the private collection exists, a blocked collaborator can be fixed by sharing the collection. But if the owner deletes the collection (deleteSelf()storage.deleteAll()), the recorded observation stays behind in every workspace's Context gatekeeper DO, and hasCollectionAccess now returns false for everyone, forever: nobody owns the id anymore and it isn't public. Every collaborator of every workspace that ever observed the collection is permanently locked out with:

Context — This collaborator does not have access to a Context collection whose data this workspace has read, so they cannot be allowed to observe it.

There is no recovery path in the UI or API: collection ids are server-minted so the collection can't be recreated under the same id, and the observedCollection: keys are never pruned.

Steps to reproduce

  1. User A creates a private Context collection.
  2. User A opens a workspace shared with user B and runs any agent turn (the catalog read records an observation of the private collection in that workspace).
  3. User A deletes the collection.
  4. User B opens the workspace → the "Verify your access again" dialog fails, permanently.

Expected

Deleting a collection should not permanently brick collaborator access to workspaces. Once the collection's data no longer exists, there is nothing left to protect beyond what already entered chat history (which collaborators can see regardless).

Possible fix

Treat a collection whose DO has no metadata (id is the "" storage default — the state deleteSelf() leaves behind) as accessible in ContextVerifier.hasCollectionAccess:

if (owns || isPublic) return true;
let collections = this.ctx.exports.ContextCollectionDurableObject;
let meta = await collections.get(collections.idFromName(
    domainName(sharingDomain, collectionId))).getMetadata();
return !meta.id;

A live private collection owned by someone else still has meta.id set and keeps blocking as today; an RPC error still fails closed. Alternatives: tombstone deleted ids somewhere, or prune observedCollection: entries when the collection disappears.

We hit this in production (a private "roles" collection deleted after a trial) and are running the clause above as a local patch; it unblocks affected workspaces at the next verification.

Related observation

getAgentCatalog() records an observation over every enabled collection even when the agent never reads their content, which is what makes the blast radius "every workspace the owner has used since creating the collection". Scoping the catalog observation more narrowly (e.g. only collections actually read) would shrink the footprint of this failure mode.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions