Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.openmem.blog/llms.txt

Use this file to discover all available pages before exploring further.

Every memory in OMP can carry a scope — a hierarchical path that tells the system where that memory belongs and who is allowed to touch it. Scopes serve two purposes simultaneously: they organize memories into logical namespaces, and they form the basis of OMP’s access-control grammar so you can grant or restrict access at exactly the right level of granularity.

What is a scope?

A scope is a slash-delimited string that places a memory in a hierarchy. Think of it like a file path: the segments to the left are broader categories, and segments to the right are more specific. For example:
ScopeMeaning
coding/preferencesTooling and style choices for coding
coding/languagesProgramming language preferences
health/symptomsHealth-related notes
ui/preferencesInterface and display preferences
work/meetingsNotes from work meetings
Scopes are optional — if you omit scope on a memory, it has no namespace and can only be retrieved by direct ID or unscoped queries.

Wildcard matching

You can use a trailing * wildcard to match an entire subtree. The pattern coding/* matches coding/preferences, coding/languages, coding/tools, and any other path that begins with coding/. This is the same wildcard syntax used in both data queries and access-control grants.

Adding memories with a scope

Pass scope as a keyword argument to mem.add():
mem.add(
    content="User prefers pnpm over npm",
    user_id="u1",
    scope="coding/preferences",
    tags=["tooling", "nodejs"],
)

mem.add(
    content="User experiences migraines triggered by bright light",
    user_id="u1",
    scope="health/symptoms",
)

Filtering by scope in search and list

Both mem.search() and mem.list() accept a scope parameter. When you provide a scope, results are restricted to memories whose scope matches exactly or falls within the wildcard pattern.
# Exact match — only memories with scope="coding/preferences"
results = mem.search("package manager", user_id="u1", scope="coding/preferences")

# Wildcard — memories under any coding/ sub-scope
results = mem.search("editor setup", user_id="u1", scope="coding/*")

# List with exact scope filter
page = mem.list("u1", scope="coding/preferences")

# List with wildcard
page = mem.list("u1", scope="coding/*")

Access control with scopes

OMP’s authorization model uses a <verb>:<scope-path> grammar to express what an application is permitted to do. An app’s grant lists the specific verbs and scopes it may access.
read:coding/*          # read anything under coding/
write:coding/preferences  # write only to coding/preferences
delete:health/*        # delete anything under health/
read:*                 # read all scopes (full read)
A user sees a consent prompt like this before an app is granted access:
Cursor is requesting access to your memory:
  ✓ read:coding/*
  ✓ write:coding/*
  ✗ read:health
  ✗ read:finance
Duration: 90 days
[ Allow ]   [ Deny ]   [ Customize ]
When an app attempts to access a scope it was not granted, the SDK raises ScopeDeniedError with a clear message identifying the missing grant.

Best practices

Design your scope hierarchy with broad categories at the top level (coding, health, work) and progressively specific categories as you descend (coding/preferences, coding/languages/python). This lets you grant coarse access (read:coding/*) or fine-grained access (read:coding/preferences) without redesigning the hierarchy later.
A few guidelines:
  • One concept per leaf. Keep the final segment focused on a single topic rather than mixing unrelated data under the same path.
  • Match access boundaries. Draw scope boundaries where you expect to draw access boundaries — if health data should always require separate consent from work data, keep them as distinct top-level scopes.
  • Avoid deep nesting. More than three segments (a/b/c) rarely adds value and makes wildcard grants harder to reason about.
Not all providers support scopes natively. Some simulate scope filtering by mapping scope values to tags internally. Call mem.capabilities() and check caps.features.scopes — the value will be "native", "tags", or "none" — to understand what your current provider supports before designing a scope-heavy architecture.