Multi-tenancy & isolation
Isolate tenants with group_id.
Every memory operation in pgmem is scoped by group_id — it is the tenant
boundary. Get it right on every call and tenants can't see each other's memory;
the session layer enforces it transitively so you don't have to thread it
everywhere.
Prerequisites
- A
group_idper tenant (a customer, an org, a workspace — whatever your isolation unit is).
Steps
Scope every entry point by group_id
Sessions, graph ingestion, and search all take it:
session = await mem.session.create(group_id="tenant-acme", user_id="user-1")
await mem.add_episode(content=..., group_id="tenant-acme")
results = await mem.search("...", group_ids=["tenant-acme"])Resume is group-scoped on purpose
resume requires the group_id and looks the session up scoped to it — a caller
who only knows a session_id cannot reach another tenant's session.
session = await mem.session.resume(session_id, "tenant-acme")The same applies to summarize_saga(saga_uuid, group_id): knowing a saga_uuid
is not enough without the owning tenant.
Delete per tenant
await mem.clear_group(["tenant-acme"])How isolation holds
Top-level rows (sessions, entities, edges, sagas) carry group_id and are filtered
on it. Session child data (turns, state nodes, facts, compactions) deliberately
has no group_id column — it's reached only through a session handle, which was
itself minted by a group-scoped lookup. So the boundary is transitive through the
handle: once you hold an authorised session, its turns and facts are already
scoped, and you never pass group_id to per-turn calls.
Gotchas
Never reuse a session handle across tenants, and never derive a handle from an
untrusted session_id without the tenant's group_id. The handle is the
authorisation — treat it like one.
clear_grouprequires explicitgroup_ids; there is no "all groups" sweep by design.user_idscopes within a group (e.g. per-user sagas); it is not a substitute forgroup_idas the isolation boundary.
Related
- Execution state vs memory — what a session handle owns
- Session API —
create/resumesignatures - Pitfalls & footguns — the forgotten-
group_idtrap