Skip to main content
CoreSDK
Multi-Tenancy

Tenant Isolation

Hard multi-tenancy with per-tenant policy namespaces and audit streams.

Tenant Isolation

CoreSDK provides hard tenant isolation at the policy, audit log, and rate-limit layers.

How isolation works

Every request carries a tenant_id — extracted from the JWT claim tenant_id. CoreSDK scopes all policy evaluations, audit writes, and trace attributes to that tenant.

Tenant resolution

The tenant_id is always read from the verified JWT claim. It is never trusted from a query parameter or arbitrary header.

use coresdk_engine::Engine;

// Engine reads CORESDK_* env vars; tenant_id comes from JWT claim
let engine = Engine::from_env().await?;
from coresdk import CoreSDKClient, SDKConfig
from fastapi import Request

_sdk = CoreSDKClient(SDKConfig(
    sidecar_addr="[::1]:50051",
    tenant_id="acme-corp",
    fail_mode="open",
))

# Tenant comes from JWT claim tenant_id — always from token, never query param
def get_tenant(request: Request) -> str:
    user = getattr(request.state, "coresdk_user", None) or {}
    return user.get("tenant_id") or "default"

# Scope all DB queries by tenant
@app.get("/products")
async def list_products(request: Request):
    tenant = get_tenant(request)
    return db.products.for_tenant(tenant)
// Go SDK ships Phase 2
// tenant_id is resolved from the verified JWT claim automatically
// TypeScript SDK ships Phase 2
// tenant_id is resolved from the verified JWT claim automatically

Policy namespaces

Each tenant can have its own policy file that overrides the global policy:

policies/
  global.rego           # applies to all tenants
  tenants/
    acme.rego           # overrides for tenant "acme"
    beta-corp.rego      # overrides for tenant "beta-corp"
# policies/tenants/acme.rego
package coresdk.authz

# Acme gets extra analytics access
allow {
    input.action == "analytics:read"
    input.user.tenant_id == "acme"
}

Per-tenant rate limits

Phase 2. Per-tenant rate limit configuration ships Phase 2. Phase 1 enforces a single global rate limit via the sidecar.

Configure per-tenant limits in coresdk.toml:

[sdk]
sidecar_addr = "[::1]:50051"
tenant_id    = "acme-corp"
service_name = "my-api"
fail_mode    = "open"

[[tenants]]
acme-corp = { display_name = "Acme Corp" }
globex    = { display_name = "Globex" }

Tenant provisioning API

Phase 2. The programmatic tenant provisioning API ships Phase 2. In Phase 1, tenants are declared in coresdk.toml under [[tenants]].

Cross-tenant access

Phase 2. Cross-tenant service tokens ship Phase 2.

Cross-tenant access is always logged separately in the audit stream.

Audit stream per tenant

Phase 2. Per-tenant audit stream export (S3, GCS, HTTP) ships Phase 2. Phase 1 writes audit events to the local OTel pipeline.

On this page