What are memory scopes?

Short answer: A scope is the identifying context on a memory that decides which searches may find it, such as project, user, or a custom property.

Every memory has a scope whether designed deliberately or left to defaults. Project scope shares institutional knowledge; user scope strictly isolates personal data; property scopes add finer cuts like conversation or tenant. Multiple dimensions can layer on one memory. Engram lets systems define these levels precisely.

The previous chapter argued that memory needs enforced boundaries, not just careful code. This chapter looks at the specific mechanism that actually draws those boundaries: the scope a memory belongs to. A scope determines who a memory is about and who’s allowed to retrieve it, and getting this right is the difference between a system that isolates data reliably and one that merely hopes it does.

What Actually Is a Scope, and Why Does Every Memory Need One?

A scope is the identifying context attached to a memory that determines which searches are allowed to find it, a project, a specific user, or a specific custom identifier like a conversation or a tenant. Every memory belongs to some scope whether a system designer thinks about it deliberately or not, the only real choice is whether that scope gets defined intentionally, matching the actual boundary the data needs, or left to default in a way that quietly grants broader access than anyone actually intended.

What Does It Mean for a Memory to Be Scoped at the Project Level Rather Than to Any Individual?

A project-level memory is shared across everyone operating within that project, with no additional identifier narrowing who it belongs to. This is the right shape for information that genuinely isn’t about any one person, a lesson an agent learned about how to do its job correctly, a general procedure, a piece of institutional knowledge that should benefit every user equally rather than staying locked to whoever happened to be involved when it was first captured. Scoping something at the project level is a deliberate choice to share, not an accidental default that happened because nothing narrower was specified.

What Does It Mean for a Memory to Be User-Scoped, and Why Does That Isolation Need to Be Strict Rather Than Optional?

A user-scoped memory carries a specific user identifier and is isolated so strictly that a search running as one user structurally cannot retrieve a memory scoped to a different user, regardless of how closely the two queries might resemble each other in wording. This strictness isn’t a configurable nicety, it’s the whole point of the scope, a personal preference, a private detail, or an individual history genuinely should never bleed across users, and treating that isolation as anything less than absolute defeats the purpose of scoping the memory to a user in the first place.

What Do Custom Property Scopes Add Beyond the Basic Project and User Distinction?

A custom property scope attaches an additional identifier to a memory, a conversation, a session, a specific deal or case, letting a system isolate memories more finely than user identity alone could manage. This kind of scoping tends to work a little differently from strict user isolation, since a caller can often choose whether to filter by that additional property or search across every value of it at once, useful when a system wants a user’s memories from one specific conversation in one moment and that same user’s memories across every conversation they’ve ever had in another.

How Do These Different Kinds of Scopes Actually Combine When a Single Memory Needs More Than One Layer of Isolation?

A memory can carry more than one scope dimension simultaneously, a user identifier alongside a custom property, layering isolation rather than forcing a choice between them. A per-conversation summary, for instance, naturally needs both a user identifier, so one person’s conversations never surface for someone else, and a conversation identifier, so a summary from one specific conversation doesn’t blend into a summary from a different one the same user had last week. Each additional scope dimension narrows the boundary further, and a system should add exactly as many as the actual data genuinely requires, no more and no fewer.

How Does Weaviate Engram Let a System Define These Different Scope Levels Precisely?

Weaviate Engram supports project-wide, user-scoped, and custom property scoping directly, enforcing whichever combination a given topic actually requires both when memories are stored and when they’re searched. Consider a language-exchange platform matching learners with native-speaking conversation partners, where some memories genuinely belong to one learner personally, while others describe a shared best practice the platform’s matching algorithm should apply to everyone:

from engram import EngramClient

client = EngramClient(api_key=os.environ["ENGRAM_API_KEY"])

client.memories.add(
    "Learner is working toward conversational fluency in Portuguese and prefers partners who correct mistakes gently rather than mid-sentence.",
    user_id="learner-langexchange-5563",
)

client.memories.add(
    "Matching pairs with a larger gap in proficiency level tends to produce shorter, less engaged conversation sessions.",
)

learner_preferences = client.memories.search(
    query="What kind of correction style does this learner prefer?",
    user_id="learner-langexchange-5563",
)

The learner’s own personal preference is scoped strictly to their own user identifier, guaranteeing it never surfaces for a different learner’s matching session, while the general observation about proficiency gaps carries no user identifier at all, deliberately shared project-wide so the platform’s matching algorithm can apply that lesson to every learner it serves. This is exactly the value clearly chosen scopes deliver for a use case like a language-exchange platform, where personal preferences genuinely need to stay private to the person who shared them, while general lessons about what makes a good match genuinely benefit from being available to everyone the system serves.

Scopes give a system the actual mechanism for deciding who a memory belongs to, turning the abstract need for access control into a concrete, enforceable property attached to every stored memory. A closely related question sits just beyond this one: when a system actually should share memory broadly across everyone versus keep it locked to a single individual. Our next chapter, When should memory be project-wide vs user-scoped?, takes up exactly that choice.