Skip to content

Multi-Tenancy

Delete One Tenant's Data for GDPR, Not the Rest

Delete one tenant's data for GDPR right-to-erasure and you inherit a deceptively hard requirement: remove exactly that customer's records from the semantic cache, the vector store, and long-term memory — and nothing belonging to anyone else. On a shared multi-tenant agent, "their data" is smeared across at least three stores, each keyed differently, and a right-to-erasure request gives you a legal deadline to prove you got all of it. This post shows how Promptise Foundry turns that into one call per surface — cache.purge_user("alice", tenant_id="acme") and provider.purge_user(caller.isolation_key) — where the tenant is baked into the key, so the purge matches exactly what was stored.

Migrate a Single-Tenant Agent to Multi-Tenant

To migrate a single-tenant agent to multi-tenant, you don't have to rewrite it — you have to find the one place where identity enters the system and make the tenant part of it. The trap is believing there are many such places. In a typical agent, per-customer state lives in at least three stores — long-term memory (a vector index), a semantic cache, and conversation history — and the naive migration path is to hunt down every read and write against all three and splice in a tenant filter. That's the retrofit tax, and it's where cross-tenant leaks come from: not the filters you add, but the one you forget. This how-to walks the Promptise Foundry migration seam end to end, where adding a tenant means setting a single field.

Multi-Tenant RAG: Isolate Customer Data in a Shared Store

Multi-tenant RAG almost always ships as one shared vector store plus a hand-written metadata filter bolted onto every query — and the first query that forgets the filter quietly leaks Acme's documents into Globex's answer. There is no exception, no stack trace, no failing test. The retrieval just returns a neighbor from the wrong customer, the model summarizes it, and you find out when a support ticket arrives. This post is about the retrieval path specifically: why per-query filters are the wrong place to enforce a security boundary, what other frameworks hand you today, and how Promptise Foundry threads the tenant into retrieval automatically so a forgotten filter is structurally impossible.

Per-Customer Chat History: Enforcing Session Ownership

Per-customer conversation isolation is the difference between persisting a chat thread and actually keeping one customer from reading another's — and the second is the part most stacks quietly skip. Wiring up a database to store messages by session_id is a solved problem: pick SQLite, Postgres, or Redis and you have durable history. The hard problem starts the moment a second customer exists, because a session id is just a string, and any authenticated caller who can produce that string can ask for the thread behind it. This post shows how Promptise Foundry turns "who is allowed to read this thread" from a check you have to remember into an invariant enforced on every read, write, and delete — keyed on the tenant::user isolation key so an enumerated session id from another customer is denied, not served.

Noisy Neighbors: Per-Tenant Rate Limits for AI Agents

Per-tenant rate limiting AI agent platforms need is the difference between a shared deployment that degrades gracefully and one where a single customer's runaway loop pages your on-call at 2 a.m. because every other tenant's requests are suddenly getting refused. In a multi-customer platform, every tenant's agents hit the same MCP tool servers. Most of the time that is fine. Then one tenant ships a buggy prompt, an agent gets stuck reformulating the same generate_report call forty times a minute, and your token bucket drains — for everyone. This is the noisy-neighbor problem, and the uncomfortable truth is that a per-client or per-tool limiter alone does not solve it.

Carry a User's Tenant Across Agent Delegation

To propagate tenant across agent delegation is to keep one promise: when your orchestrator agent hands work to a peer mid-request, the peer's cache, memory, and conversation history stay scoped to the original end-user's tenant — not to the process, and not to whatever principal the peer happens to think it is serving. This is the data-isolation side of delegation, and it is distinct from attribution (knowing which agent asked). Attribution answers "who delegated?"; isolation answers "whose data can the delegate touch?". Get attribution right and you still have a leak if the peer reads a shared vector store under the wrong tenant. This post shows how Promptise Foundry makes the tenant ride along automatically, so ask_agent_<peer> and broadcast_to_agents never silently widen the blast radius.

Enforce Tenant Isolation with require_tenant=True

A require_tenant mcp server turns tenant isolation from a check you remember to add into an invariant the framework enforces at build time. Instead of adding a tenant guard to each tool by hand — and hoping the next tool, the next router, and the mounted third-party server all get one too — you set a single flag on the constructor, and every tool the server exposes is forced to authenticate and carry a tenant guard before you write a line of handler code. This post is about the exact mechanics of that flag: what MCPServer(require_tenant=True) does at build time, how to prove it with a runnable unit test, why it covers registration paths you didn't write yourself, and where it is honestly stronger than the opt-in checks other frameworks hand you.

Same user_id, Two Tenants: Why That Isn't Isolation

The problem with the same user_id across two tenants is that it looks fine right up until it doesn't: two of your customers, Acme and Globex, each have a user literally named alice, and the day both are active, whatever you key memory, cache, and conversation history on had better distinguish them. If that key is user_id alone, it doesn't — and the failure is silent. No exception, no log line, just Acme's alice occasionally reading Globex's alice's data. This post is about why a per-user key is not a per-tenant key, what an injective isolation key is, and the subtler risk most treatments skip: not just accidental collision, but deliberate forgery of another tenant's namespace.

Where Does tenant_id Come From? JWT Claim vs API Key

Every multi-tenant tutorial hands you a tenant_id and moves on, but the honest question is where that value comes from — and the two answers Promptise Foundry supports are a tenant_id from jwt claim and a tenant bound to each API key. Get that sourcing step right and the rest of your isolation story (tenant-qualified rate limits, per-tenant audit, cache scoping) follows for free. Get it wrong — a claim you forgot to read, a number where you expected a string, a request with no tenant at all — and a claim-less caller slips through as tenant-less, which is the one outcome a multi-customer platform can never afford.

Tenant-Scoped Audit Logs for AI Agent Tool Calls

A tenant-scoped audit log for an AI agent's tool calls exists to answer two questions a compliance auditor puts to you in the same breath: show me every action your system took for Acme, and prove that record wasn't edited after the fact. Both are easy to wave at and hard to actually satisfy. A general trace store can usually show you something about Acme, but "every action" means the tenant has to be a field you can filter on cleanly — not a tag one call site set and another forgot. And "prove it wasn't edited" means the trail needs integrity you can hand to a third party, not your assurance that the database looks fine. This post is about the per-tenant forensics angle specifically: how to produce a slice of the trail that belongs to exactly one customer, and hand it over with a proof it wasn't altered.