Skip to content
Agents: The Core LoopMemory — scope and privacy

Lead · Core path · 8 chapters

Memory — scope and privacy

Per-thread vs per-user vs per-organization, tenancy leaks, user-inspectable and user-deletable memory

Memory becomes a privacy and tenancy boundary as soon as it persists beyond one request; a single scope mistake can expose one customer’s history to another.

Read these first

What you will know

  • Scope memory per thread, per user, and per organisation

    Choose the owner and authorization boundary for each record, and enforce it in reads, writes, indexing, and background jobs.

  • Prove memory does not leak across tenants

    Use tenant-labeled fixtures and authorization assertions to show that retrieval, updates, and generated responses cannot cross the organization boundary.

  • Let a user see and delete what is remembered about them

    Expose the stored facts and their provenance through an authenticated control path, with deletion results that can be audited.

  • Trace deletion through caches, embeddings, logs, and backups, and document residual-retention limits

    A primary-row delete is insufficient when derived copies remain searchable or recoverable under a different retention policy.

  • Test the isolation adversarially

    Exercise forged identifiers, missing tenant filters, concurrency, stale caches, and tool or job paths rather than testing only the happy path.


Chapters

  1. 1

    Thread, user and organisation are three different owners

    A preferred language belongs to the person, an escalation policy belongs to the account, and 'we already tried restarting the router' belongs to this ticket and dies with it. This chapter assigns each record an owner and an authorisation boundary, then works both failure directions for each: what disappears when you scope too narrowly, and what leaks into the wrong conversation when you scope too wide.

  2. 2

    An account boundary is not a privacy boundary

    Sharing memory across an organisation so colleagues benefit from each other's conversations is sometimes correct and sometimes a disclosure incident, and the same schema supports both. Contracted support tier is a fact about the company; what an employee said about a grievance is not, even though both arrived through the same account. The line runs through who the fact is about and what they consented to, not through who pays the invoice.

  3. 3

    Tenancy filters go missing wherever the request path does not reach

    Query builders in the request path get reviewed; the nightly re-embedding job, the schema migration, and the summariser that compacts old memories do not. Each of those reads the table directly, and a scope filter that lives in application code never runs there. You will inventory every path that touches memory, including tools and background workers, and push enforcement down to where it cannot be forgotten.

  4. 4

    The scope key comes from the session, never from a tool argument

    A filter applied faithfully to a value the model chose is not an authorisation control. Derive tenant and user identity from the authenticated principal at the edge and carry it as ambient context, so that memory tools expose no user_id or org_id parameter at all: tool arguments are model-controlled, and untrusted content in the context can steer them, which makes any such parameter a bypass by construction rather than by bug. This is what makes a tenant A session carrying a tenant B identifier constructible at all, and what makes it fail closed. Attacker-supplied resource ids get the same treatment — resolve first, then check ownership against the principal.

  5. 5

    In a shared index, filtering after the search costs you the top-k

    One vector index for every tenant with a tenant field on each record is safe or unsafe depending on a detail no happy-path test reveals: whether the filter constrains the search or trims its output. Post-filtering lets a neighbouring tenant's records occupy the k nearest neighbours and hands back three results instead of ten, silently degrading answers long before anything leaks. This chapter covers pre-filtering, per-tenant partitions, and what each costs.

  6. 6

    Isolation is proved by labelled fixtures and hostile inputs

    Seed two tenants with recognisable records, then assert that no query, update, or generated response for one can ever surface the other's, on every path from the earlier chapters. Then make the suite hostile: a valid session for tenant A carrying tenant B's identifier in the payload, concurrent writes, a cache warmed under the wrong key, and a tool invoked with an attacker-supplied resource id — each of which is testing a control this topic has already specified.

  7. 7

    Provenance is what makes a memory inspection view worth shipping

    A list of bare sentences the system believes about you invites disbelief and nothing else. Attach when each record was written, from which conversation, and by which gate, and the same screen becomes something a user can act on and a support engineer can debug. This chapter also covers the authenticated control path itself, so that reading and deleting are themselves authorised and auditable events.

  8. 8

    Deleting the row leaves the vector, the cache, the trace and the backup

    The primary record goes, the confirmation is sent, and the same facts stay searchable in the embedding index, warm in a cache, quoted in trace payloads, and recoverable from a snapshot with its own retention schedule. Follow a deletion through every derived copy, decide which are purged and which are only aged out, and learn to tell a user honestly what remains and for how long.

External resources