Architecture navigation+

Architecture · Multi-tenant

Multi-tenant isolation

How Cognitive Server enforces hard tenant boundaries across every layer of the stack — from the JWT to the vector store.

JWT & Tenant-ID

Every request to Cognitive Server carries a JWT issued and signed by Shield. The token payload contains three mandatory claims:

  • tenant_id (UUID v7): the unique identifier of the operating tenant. Every component downstream uses this claim to scope reads and writes.
  • scope[]: the exact set of permissions active for this request. No implicit scopes exist; if a scope is absent from the token, the corresponding resource or action is unavailable.
  • jti (JWT ID): a unique token identifier used for anti-replay validation. Shield rejects any jti it has already seen within the token's lifetime.
{
  "iss": "https://shield.cognitive.local",
  "sub": "operator_01J8...",
  "tenant_id": "acme-bank_01J8...",
  "scope": ["vault:read", "audit:export", "skill:execute:approved"],
  "jti": "tok_01J8...",
  "iat": 1751000000,
  "exp": 1751003600
}

The tenant_id propagates through the entire call chain — Core → Nexus → Vault → Hub. No component accepts a request without a valid tenant_id in the JWT. Cross-tenant reads are structurally impossible: the query layer always injects the tenant_id as a mandatory filter before executing.

Note · No bypassShield never issues a token without an explicit tenant_id. There is no super-tenant or admin bypass mode. Even administrative operations require a tenant-scoped token with the admin scope explicitly granted.

Isolation contract

Tenant isolation is physical and logical simultaneously. The following constraints are enforced at the data layer, not at the application layer:

  • Vector namespace isolation: each tenant's embeddings live in a dedicated Qdrant collection prefixed by tenant_id. Cross-namespace queries return 403 before touching storage.
  • Row-level security (PostgreSQL): every table carries a tenant_id column. Shield-issued RLS policies activate on connection and make cross-tenant rows invisible — they do not appear in query plans.
  • Chain ledger partitioning: trace events are written to tenant-partitioned append logs. Regulators receive an export scoped to their tenant; the export API rejects requests without a matching tenant_id.
  • Redis keyspace prefixing: all cache keys are prefixed tenant_id:. A keyspace notification flood from one tenant cannot bleed into another's event stream.
Spec · InvariantThe isolation contract is an invariant, not a configuration option. It cannot be loosened without a full architectural review and explicit sign-off from the operator's security team.

Dynamic Client Registration

Cognitive Server follows the OAuth 2.1 Dynamic Client Registration protocol (RFC 7591) for onboarding new tenants. The flow:

  1. The operator submits a registration request to Shield's /register endpoint, including the tenant_id, the allowed redirect URIs, and the public key for token verification.
  2. Shield issues a client_id and a client_secret encrypted with the tenant's public key. The plaintext secret never transits the network.
  3. The operator stores the client_secret in their HSM or secrets manager. Shield never stores or logs the plaintext.
  4. Token rotation is non-disruptive: the operator initiates rotation via /register with the existing client_id; Shield issues new credentials before invalidating the old ones.
// Registration request
POST /register
{
  "tenant_id": "acme-bank_01J8...",
  "redirect_uris": ["https://acme-bank.internal/callback"],
  "jwks_uri": "https://acme-bank.internal/.well-known/jwks.json",
  "token_endpoint_auth_method": "private_key_jwt"
}

// Shield response
{
  "client_id": "client_01J8...",
  "client_id_issued_at": 1751000000,
  "client_secret_expires_at": 0
}