Tenancy & security¶
Concept 8 of 8
Multi-tenant safety in Telha is not a policy you have to trust; it is a property of the storage key layout itself. Classified data is not something Telha core is merely told to protect, it is encrypted before core ever sees it. Access is deny-by-default everywhere, and identity that binds evidence has to be verified, not just claimed.
Normative spec
Field-level encryption is defined in field-encryption (approved, security review complete). Identity and access are covered by Architecture › SSO & auth.
Cross-tenant reads are unrepresentable, not just forbidden¶
Every storage key in Telha begins with a tenant and workspace prefix, by construction. This is a stronger guarantee than an access-control check that could, in principle, contain a bug: a read for tenant A's data literally cannot address tenant B's data, because tenant B's keys do not exist in the address space a tenant-A-scoped operation can construct. Cross-tenant access is not "denied by policy," it is not a representable request in the first place.
flowchart LR
subgraph tenantA["Tenant A key space"]
KA["tenantA/workspace1/RISK/..."]
end
subgraph tenantB["Tenant B key space"]
KB["tenantB/workspace1/RISK/..."]
end
ReqA["Request scoped to Tenant A"] -->|can only construct| KA
ReqA -.->|cannot construct| KB This matters because it is the same guarantee that underlies every other tenancy-sensitive feature in the product: MCP sessions bind to one tenant scope at initialize (a token for another tenant mid-session is rejected), identity resolution for the Entra connector is tenant-scoped always, and clarification routing and quorum are computed inside a single tenant's graph, never across tenants.
Classified fields are encrypted before core ever sees them¶
Some fields, personally identifiable information, financial data, secrets, are too sensitive to store as plaintext even inside a correctly tenant-isolated store. For those, encryption happens in the application layer, before the data is ever sent to Telha core: core only ever sees an opaque, magic-prefixed ciphertext envelope, never the plaintext. Which fields count as classified is a declared, explicit policy per module, not tribal knowledge: a module that has no classification entry for a label is refused, forcing the decision to be made rather than silently defaulted.
The envelope is bound to more than just the field: its authenticated context includes the tenant, the label, the specific record's logical id, and the field name (AAD, additional authenticated data). Binding to the record's own identity closes a specific attack: without it, an encrypted value could in principle be cut from one record and pasted into another record of the same tenant, label, and field, and still pass authentication. With the record's identity baked into the binding, that transplant fails outright, the ciphertext only authenticates in the exact place it was created for.
flowchart LR
APP["App layer<br/>field-crypto.encrypt()"] -->|"envelope: alg, key_id, nonce, ciphertext, tag<br/>AAD = tenant + label + logical_id + field"| CORE["Telha core<br/>(stores opaque bytes only)"]
CORE -->|read| APP2["App layer<br/>field-crypto.decrypt()"]
APP2 --> PLAIN["Plaintext<br/>(app layer only, never in core)"] A direct consequence, stated plainly rather than hidden: classified fields are invisible to core's indexing and vector search, because core never has the plaintext to index or embed in the first place. Non-searchability of classified fields is the honest tradeoff for keeping them out of core entirely, rather than a limitation nobody tells you about.
Deny-by-default RBAC from IdP groups¶
Access to Telha's features is deny-by-default: roles come from your identity provider's groups, and a user who is not mapped to a role gets nothing, not a default reduced-but-present set of permissions. This mirrors the same principle that governs clarification identity: a chat display name or an unmapped account is not a basis for access, a verified, group-mapped identity is.
Discovery-level RBAC for agents¶
The same deny-by-default posture extends to AI agents acting through the MCP tool surface. Tool access is gated per role with an allowlist, so an agent operating on a user's behalf can only discover and call the tools that role is permitted to use, and every invocation lands in the audit log. An agent does not get a broader view of the system than the person it is acting for.
Scope never comes from the request¶
A request's tenant and organization scope is established by its authenticated credential (an API key minted for a specific tenant and organization, or a verified session), never by a field the caller supplies in the request body or query string. This is what makes the tenant-prefixed key layout meaningful in practice: even if a request tried to claim a different scope, there is no code path that reads scope from anywhere other than the authenticated credential.
Audience-bound tokens¶
Service tokens are further restricted by audience: a token minted for one purpose cannot be used for another. An ingestion-worker token can lease ingestion jobs and nothing else; a token scoped to the "clarify" audience can lease and answer clarification jobs and nothing else; a connector token scoped to "connector" can run sync jobs and nothing else. This is also what makes identity assertions in the clarification loop trustworthy: only a "clarify"-audience token, which has already verified a Teams or Slack callback, is permitted to assert an answer on behalf of someone else. Every other caller, including a normal API key, can only act as itself.
Why this belongs on the tenancy page
Audience fencing, deny-by-default RBAC, and tenant-prefixed storage are three layers of the same principle: a credential should be able to do exactly what it was issued for, nothing implied, nothing assumed, and nothing left to a policy check that could be bypassed if the underlying address space allowed it.
Related¶
- The tritemporal model - every fact is tenant-scoped from creation through every later correction.
- Clarifications - permission checks and verified identity in the clarification loop.
- Architecture › Field encryption - the envelope format, key hierarchy, and rotation procedure.
- Architecture › SSO & auth - identity provider integration and RBAC.