Identity & Scope
MemorySync has two related but distinct entities: a Tenant (the hard isolation boundary) and an Organization (the SaaS team). One organization may own one or more tenants. This page explains the model fields, how isolation is enforced, and how header-driven tenant switching works.
Tenant vs Organization at a glance
| Aspect | Tenant | Organization |
|---|---|---|
| Purpose | B2B isolation boundary. | SaaS team entity. |
| Primary id | tenant_id (string, opaque). | Internal id + owner_user_id. |
| Members | Resolved through Organization. | Membership rows with roles. |
| Owns | Projects, users, memories, vectors. | One or more Tenants. |
Tenant model fields
| Field | Notes |
|---|---|
tenant_id | String, unique, indexed. Opaque id (e.g. org_123). |
name, description, slug | Display metadata. |
plan, status | Billing plan and lifecycle status. |
quota_gb, max_users, max_api_keys | Hard limits. |
usage_bytes, user_count, memory_count, api_call_count | Counters maintained by the platform. |
organization_id | Optional FK to Organization. |
How isolation is enforced
- FK chain —
Memory.user_id → User.id → User.tenant_id;Project.tenant_id → Tenant.tenant_id. - Query filters — every read in the memory service adds
WHEREclauses on tenant and project before vector recall. - API-key check — service-auth requests carry an org context on the key; mismatched
X-Tenant-IDreturns 403.
The <code>X-Tenant-ID</code> header
X-Tenant-ID is validated against the caller's memberships before the request state is updated. If the user belongs to multiple tenants, this header chooses which one this request runs against. Without the header, the user's default tenant is used.
Limits and counters
| Limit | Counter that drives it |
|---|---|
quota_gb | usage_bytes |
max_users | user_count |
max_api_keys | Number of active API keys for the tenant. |
Quota enforcement runs in front of /memory/add and /memory/query; in silent quota mode, an exceeded quota returns a normal-shaped success body with empty data.
One organization, many tenants
Because Tenant.organization_id is optional, a single Organization can own several Tenants — useful for staging vs production isolation, or for a parent company managing per-customer tenants. Cross-tenant data access still goes through the same checks: there is no implicit shortcut just because two tenants share an organization.
What tenants do not do
- Tenants are not a billing subject by themselves — billing rolls up to the Organization plus its tenants.
- Tenants do not contain other tenants — there is no nesting.
- Tenants are not deleted in place — a deactivated tenant has its
statuschanged and its data is purged through the lifecycle path.