Skip to content

Isolate multi-tenant memory ​

Level: Pattern · Estimated time: 40 minutes

Outcome ​

Bind every conversation to an authenticated tenant and user before Anvia loads or appends memory.

When to use it ​

Use this in SaaS applications where session identifiers can collide across tenants or users may belong to several organizations.

Request flow ​

session middleware → tenant membership check → application conversation lookup → opaque memory scope → agent.generate({ prompt, session }). Tenant and user values come from trusted server state.

Resolve an owned conversation ​

ts
type Principal = {
    tenantId: string;
    userId: string;
};
async function promptConversation(principal: Principal, publicConversationId: string, question: string) {
    const conversation = await conversations.findOwned({
        tenantId: principal.tenantId,
        userId: principal.userId,
        publicId: publicConversationId,
    });
    if (conversation === undefined)
        throw new Error("Conversation not found");
    // `memoryKey` is application-generated and never accepted directly from the caller.
    const session = { sessionId: conversation.memoryKey, userId: principal.userId, metadata: { tenantId: principal.tenantId } };
    return agent.generate({
        prompt: question,
        session,
    });
}

The repository lookup is as important as the memory adapter. Return the same not-found response for unknown and unauthorized conversations to reduce identifier disclosure.

Scope design ​

Prefer random opaque conversation IDs plus tenant/user columns and database constraints. If your adapter scope options support tenant-aware prefixes, configure them consistently, but keep an application authorization lookup in front of the store. Do not concatenate unvalidated header values into table names or SQL identifiers.

Expected behavior ​

The same public conversation ID in two tenants resolves to distinct durable memory scopes. A user removed from a tenant loses access immediately even though retained messages still exist according to policy.

Failure cases ​

Tenant switching, stale session claims, membership removal, conversation transfer, duplicate IDs, and background deletion all create race conditions. Resolve membership on every sensitive request or use appropriately short-lived authoritative claims.

Security and ownership ​

Anvia stores messages under the context you provide; it does not authenticate tenants. The application owns membership, row policies, encryption, retention, export, deletion, and support access. Consider database row-level security as defense in depth.

Production changes and tests ​

Add composite uniqueness, foreign keys, tenant quotas, audit events, deletion jobs, and support impersonation controls. Test cross-tenant and cross-user canaries, tenant switch, revoked members, guessed IDs, concurrent turns, conversation deletion, and database-policy enforcement.

Runnable reference ​

The cookbook demonstrates Anvia session mechanics; tenant repositories and identity checks here are suggested application architecture.

Extensions ​

Add organization-wide shared conversations with explicit roles, customer-managed encryption keys, retention tiers, and a compliance export pipeline.

Built for Anvia.