Set up semantic tenants
This guide walks an administrator through multi-tenancy: serving many customers from a single semantic layer, where every query returns only the rows belonging to the tenant the connection is bound to.
Available in Coginiti Team and Coginiti Enterprise only.
Overview
Multi-tenancy is three pieces that meet at query time:
| Piece | Where it lives | Who sets it |
|---|---|---|
| The tenancy policy — how isolation applies | The semantic model, in SMDL | Whoever writes the model |
| The tenants — the customers themselves | Coginiti, in Admin Settings | An administrator |
| The grants — who may connect as which tenant | Coginiti, on each tenant | An administrator |
The model never names a tenant. It says only which dimension marks ownership. The tenants are administered in Coginiti, so adding a customer does not mean editing and redeploying a model.
How isolation works
Every tenant's rows live in the same tables. Nothing is separated by schema, database or connection: one column marks which tenant each row belongs to, and the semantic layer adds the matching filter to every query.
The filter is applied by the semantic layer, so a client that reaches the warehouse another way — a direct database connection, or a BI tool with its own credentials — is not constrained by it. If your tenants must be isolated from those paths too, that needs warehouse-native policies such as row-level security, configured separately.
Name and key are different things
Every tenant carries two strings, and they do different jobs:
- The name is what a connection carries, as
tenant=<name>. It is how a person or a client asks to connect as that tenant. - The key is the value the semantic layer compares against each row's tenant field. It is your warehouse's data, not an identifier Coginiti issues — it has to match what the rows actually contain.
Getting these the wrong way round produces a tenant that connects successfully and returns nothing, which is the most confusing failure available. Decide the key by looking at the warehouse column, not by choosing a value.
Prerequisites
- Coginiti Team or Enterprise, and an account with the Admin role
- A semantic layer that declares a
tenancyblock — see Tenancy in the Semantic Model Reference - A column in your tables that marks which tenant each row belongs to, and the values it holds
Step 1: Declare the tenancy policy in the model
A layer becomes multi-tenant when its model declares a tenancy block naming
the dimension that marks ownership:
tenancy {
row_filter {
field = "tenant_id"
}
}
Every entity in the layer must then declare that dimension, override it with one of its own, or be listed as shared reference data. A layer where any entity does none of these fails to load, naming the entity — the check is exhaustive on purpose, because an entity that quietly went unfiltered would look exactly like one that is safe.
The full rules, including per-entity overrides and shared reference data, are in the model reference. Do this part first: until the layer declares a policy, the tenant dropdown in Step 4 is empty.
Step 2: Create the tenants
- Open Admin Settings.
- Go to Users & Access → Semantic Tenants.
- Click Add New Tenant.
- Fill in the fields:
| Field | What to enter |
|---|---|
| Name | What a connection will carry as tenant=<name>. Unique across the installation |
| Key | The value your rows hold in the tenant field. Unique across the installation |
| Enabled | Leave checked. Clearing it refuses every connection bound to this tenant |
- Click Save.
Repeat for each customer. The list is searchable, and unsaved tenants stay in it until you save or discard them, so several can be added in one pass.
Two tenants must never share a key. They would compile the identical row filter, and each would read the other's rows — silently, with results that look correct. Coginiti refuses the second one, comparing keys case-insensitively.
Renaming a tenant
A tenant that has grants cannot be renamed, because the name travels in every connection string that already names it and those connections would start failing. Remove its grants first, or create a new tenant.
Provisioning in bulk
An installation with hundreds of tenants should not create them by hand. The same operations are available over HTTP — see Tenants in the API Reference.
Step 3: Grant users and groups
A tenant nobody is granted is a tenant nobody can connect as. Grants are edited on the tenant itself:
- Select the tenant in the list.
- In Available users and groups, find the user or group. The Filter… box narrows a long list.
- Select it and use the › button to move it to Granted.
- Click Save.
Use ‹ to revoke.
A user's access is the union of their own grants and their groups'. Granting a group is the usual way to run this: put the customer's people in a group, and grant the group.
Grants are not transferred when a user is deleted, even if their assets are reassigned to a successor. A grant is authorization to read one of your customers' data, and handing that over without anyone deciding to would be implicit access. Grant the successor explicitly.
Step 4: Connect as a tenant
From Coginiti
- Open Connections and edit (or create) a connection to the semantic layer.
- Choose the project and layer as usual.
- In Tenant, pick the tenant to connect as.
The dropdown offers only the tenants you are granted, so what an administrator sees and what a customer's analyst sees differ. When a user is granted exactly one tenant, it is filled in for them.
If the layer declares no tenancy policy, the dropdown stays empty — that is the signal that Step 1 has not landed.
From an external client
JDBC and ODBC clients pass the tenant as a connection parameter:
tenant=acme
See Connect via JDBC for where it goes in the connection URL.
Verify the setup
-
Connect as one tenant and run a query that would otherwise return everything:
SELECT COUNT(*) FROM orders -
Connect as a second tenant and run the same query.
-
The two counts should differ, and together they should not exceed the total number of rows in the table.
A single tenant returning every row means the filter is not being applied — check that the layer really declares a policy, and that the entity you queried declares the tenant field rather than being listed as shared.
A tenant returning zero rows almost always means the key does not match the warehouse data. Compare the tenant's Key against the distinct values in the tenant column.
Troubleshooting
| Message | What it means |
|---|---|
this semantic layer requires a tenant: add 'tenant=<name>' to the connection | The layer declares a policy and the connection named no tenant |
tenant 'X' was supplied, but this semantic layer declares no tenancy policy | The reverse — a tenant was named on a layer that is not multi-tenant |
not authorized to connect as tenant 'X': ask an administrator to grant you access | The tenant is unknown, disabled, or not granted to this user |
Tenant with name 'X' already exists | Names are unique installation-wide, compared case-insensitively |
Tenant with key 'X' already exists | Keys are unique too, for the reason in Step 2 |
Tenant 'X' cannot be renamed while it has grants | Remove the grants first, or create a new tenant |
entity 'X' has no dimension 'tenant_id', so tenancy cannot filter it | The layer failed to load. Declare the dimension, override it, or mark the entity shared |
The third message is deliberately the same for all three causes. Telling them apart would let a connection dialog enumerate tenant names — which are the names of your own customers.
Related guides
- Tenancy in the Semantic Model Reference — the policy, its rules, and its errors
- Tenants in the API Reference — provisioning over HTTP
- Connect via JDBC — passing the tenant from an external client
- Set up a Consumer — onboarding the people who will use these connections