The contract
Use scoped storage for tenant/user partitions. Raw store access and application tools must follow the same host authorization model.
Scope hierarchy (Learnings & Procedures)
A record chooses one scope:user, agent, tenant, or global. Retrieval can union the caller’s matching scopes. Shared Agent labels or globally visible records require an intentional application policy; they are not automatically restricted to a tenant just because a different field contains a tenant ID.
Required identifiers per scope
User records requireuserId, Agent records require agentName, and tenant records require tenantId. Global records intentionally lack an owner. Use verified host values, not body fields supplied by an arbitrary client. Automatic extraction uses personal user scope; explicit promotion is a separate decision.
Visibility example
Within a tenant-scoped store, Alice can read her personal records and applicable Agent/tenant/global records. Bob’s personal records should not appear. Repeat the same test with Alice’s identifier in another tenant to verify the storage partition as well as the record filter.What the agent sees in the prompt
Memory context can include scope markers and selected records, but prompt labels are not access control. Some budgeted assembly paths format context differently. Enforce the boundary before text reaches the model and inspect the final context used in isolation tests.What it means for direct API callers
Direct methods receive owner identifiers from your code. Possessing a TypeScriptstring does not prove authorization. Graph-memory prompt retrieval applies user filtering, while raw GraphStore.search/traverse and the graph branch of MemoryManager.recall() query their configured backend directly. Do not expose those methods over shared data without an additional authorized backend/query boundary.
Administrative operations such as correction analytics and broad store listing may span users. Keep them behind the appropriate administrative permission instead of treating every public SDK method as an end-user API.
What stores share data intentionally
Explicitly shared learned/procedural/correction records can benefit several users. Store and retrieve them using documented scope fields, and test that narrowing identity cannot broaden access. Configure memory on each Team member explicitly; v4 does not implement automatic inheritance fromTeamConfig.memory. See sharing.
Right to be forgotten
Application deletion must enumerate the data you actually retain.curator.clearAll({ userId }) clears configured user facts, profiles, and entities; its Agent option clears decisions. It does not erase every session, procedure, correction, vector, graph record, backup, or external artifact. forget() has narrower store coverage still.
Build and test your own deletion workflow, including indexes and application-owned state. Do not promise universal erasure or a complete audit trail from one curator call. Maintenance guide.
Forgotten facts vs superseded facts
User facts can retain invalidation metadata.superseded means a later fact replaced the earlier statement; forgotten records a request not to repeat it in memory context. Invalidation and retained tombstones differ from physical data deletion. Use temporal methods to inspect current versus retained records.
Verify the boundary
Test owner reuse, another user, another tenant with the same user label, an unknown session, and explicit shared records. Inspect stored data, assembled context, and tool access—not just final model answers. Treat direct administrative methods as separate endpoints with separate authorization tests.Choose a persistent key encoding
In-memory storage keeps namespace/key pairs in separate maps. Persistent scope wrappers and Redis retainlegacy encoding by default so upgrading does not hide existing records. For a new shared key space, select keyEncoding: "tuple-v1" explicitly. Configure both the scope wrapper and Redis when both layers are used; one cannot repair collisions already introduced by the other.
Changing encoding creates a new physical key space. There is no automatic migration, legacy fallback scan or proof that ambiguous old keys identify the intended tenant. Stop writers, back up values/TTLs and the original identity mapping, verify collisions, migrate offline and validate reads before resuming writes. Missing IDs and empty strings remain distinct in tuple encoding.
ScopedStorage and TenantScopedStorage accept ownsInner: false for a borrowed driver. Initialization still forwards to the inner driver, which must make it idempotent. The host closes a shared backing client after all scopes settle. See 4.9 migration and storage API for exact options.