Tenant Variables
Scope variable values to a tenant so a solution deploys with tenant-specific configuration.
The model
Section titled “The model”Mantis has one variable entity, not separate “template” and “common” types. A
variable holds a key, a value and a set of scopes it applies to — tenant,
environment, target, solution and more. There is no per-tenant variable API;
variables live in variable sets and are managed at
/api/v1/variable-sets/{id}/variables, or under Deployment → Variable Sets in
Lens.
A variable’s variable_type is one of string, sensitive (encrypted at rest),
certificate or execution_pool. The resolver honours a scope_tenant_id
column on the variable (worth +200 below), but no API request field or UI sets
it today — the variable-set create path hardcodes it to None, so
tenant-scoped variables are not currently authorable. The examples below show
the resolution behavior the column produces once set.
Resolution: additive specificity
Section titled “Resolution: additive specificity”When a deployment needs a variable, every variable with that key whose scopes
all match the deployment context is a candidate; any whose scope does not match
is discarded. Each candidate’s score is the sum of the points for the scopes it
carries, and the highest total wins (ties break on created_at, then id):
| Scope | Points |
|---|---|
| Current action | 1000 |
| Current target | 500 |
| Target tags + action | 400 |
| Target tags | 300 |
| Tenant | 200 |
| Tenant tag | 150 |
| Environment | 100 |
| Solution | 40 |
| Variable set | 20 (+ set-position bonus, up to 19) |
| Global (no scope) | 0 |
Because scores accumulate, a variable scoped to tenant + environment (200 + 100 = 300) beats one scoped to the tenant alone (200) when both match. This is additive specificity, not an ordered override chain.
A set-scoped variable also carries a positional bonus, so its score is 20–39: where a solution links several variable sets, a variable from an earlier-positioned set outranks the same variable from a later one.
Example
Section titled “Example”For database_host, deploying solution customer-portal to tenant Acme in
production:
| Variable | Scopes | Matches? | Score |
|---|---|---|---|
db.default | global | yes | 0 |
db.acme.com | tenant=Acme | yes | 200 |
db.acme-prod.com | tenant=Acme, env=production | yes | 300 |
db.beta.com | tenant=Beta | no | discarded |
db.acme-prod.com wins with 300.
Setting a variable in a tenant’s set
Section titled “Setting a variable in a tenant’s set”curl -X POST \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d "{ \"name\": \"database_host\", \"value\": \"db.acme.com\", \"variable_type\": \"string\" }" \ "https://api.mantis.local/api/v1/variable-sets/$VARIABLE_SET_ID/variables"The request body rejects unknown fields, so sending scope_tenant_id here is
rejected (422) — and no request field sets it: scope_tenant_id is a column on the
variable the resolver scores at +200, while the set carries only tenant_id
for ownership. Tenant-scoped variables are therefore not authorable through the
API or Lens today; this call creates a set-scoped variable.
For a secret, set "variable_type": "sensitive" — the value is encrypted at rest, and
reads return the mask "********" rather than the plaintext. (<ENCRYPTED> is a
different artifact: it is the placeholder the Nickel config-as-code export writes in
place of a secret, not what a variable read returns.)
Sensitive variables
Section titled “Sensitive variables”sensitive variables are encrypted with the master key and never returned in
clear. Rotate one by updating it with a new value; there is no separate rotation
API.
Required variables
Section titled “Required variables”A deployment that references a variable with no matching candidate fails with
Missing required prompted variables: <names>. Provide a global (unscoped) value
as the fallback, since global matches every context at score 0.
Next Steps
Section titled “Next Steps”- Multi-Tenancy Overview
- Solution Variables for the full variable-set workflow
