Policy and entitlement
This page explains how you control who can use which model, and the rule the enforcement hook applies before it lets any request through.
The rule
The hook lets a request reach a model only when both of these are true:
- The model matches an admitted record in your catalogue, at the attestation version your policy names for it.
- Your own policy entitles the caller to that model.
Neither is enough alone. An admitted model that your policy does not entitle is refused. A model your policy names that is not admitted is also refused. Every refusal carries a stated reason, and the ledger records it (see ledger and audit).
The hook fails closed. If the policy engine is down, the catalogue cannot be read, the usage figures cannot be read, or the ledger write is refused, the request is denied. Nothing proceeds unlogged.
Where policy lives
Policy is written in Rego, the policy language of Open Policy Agent. It lives in your own Git repository and it is yours if you stop using Heliast. When you deploy, the commit you point at is checked out, its tests are run, and the commit SHA is recorded as the policy revision. The hook writes that revision on every ledger row, so you can answer "what were the rules on a given day" exactly.
The reference policy that ships with Heliast enforces one thing: entitlement. A caller's entitled models are the union of the models granted to each of their groups. A caller with no groups is entitled to nothing.
The policy config
Alongside the Rego you keep a policy config file in JSON. The hook loads it at start. These are its fields.
| Field | What it holds |
|---|---|
entitlements | A map from group name to { "models": [...] }. The model ids a group may use. |
model_digests | A map from model id to the digest of the model's weights. Recorded on every ledger row as the model digest. |
model_attestation_versions | A map from model id to the named attestation version of its admission. |
limits | A map from role name to rpm, tpm and monthly_token_budget. |
budgets | A map from team name to monthly_tokens. |
corpus_classification | The data class of the deployment. If you leave it out, it defaults to internal. |
An attestation version is three values joined by |: the key id, the issue time and the gate version, copied from the model's admitted record. The hook compares your string to the admitted record and requires them to be equal.
The hook checks the config against the catalogue when it loads. It refuses to start if the config:
- names a model the catalogue does not hold as admitted;
- names a model without both a digest and an attestation version;
- names an attestation version that differs from the admitted record.
A bad config is therefore caught when you build and deploy, not when a user asks for a model.
Limits and budgets
The limits and budgets objects are loaded and passed to the policy as input, together with live usage figures read from the ledger. The reference policy does not enforce on them. Rate limiting and token budget accounting are done by the routing layer the hook sits behind. You can write your own Rego that decides on them. The console's Budgets view reads the ledger and shows tokens used against allowances.
A worked example
This config entitles two groups to two admitted models. The model ids, digests and attestation strings are placeholders. Copy the real values from your own admitted records.
{
"corpus_classification": "internal",
"entitlements": {
"engineering": { "models": ["example-org/model-a"] },
"finance": { "models": ["example-org/model-b"] }
},
"limits": {
"standard": { "rpm": 60, "tpm": 10000, "monthly_token_budget": 2000000 },
"premium": { "rpm": 600, "tpm": 100000, "monthly_token_budget": 50000000 }
},
"budgets": {
"engineering": { "monthly_tokens": 50000000 }
},
"model_digests": {
"example-org/model-a": "sha256:<digest of model-a weights>",
"example-org/model-b": "sha256:<digest of model-b weights>"
},
"model_attestation_versions": {
"example-org/model-a": "<key id>|<issued at>|<gate version>",
"example-org/model-b": "<key id>|<issued at>|<gate version>"
}
}
A caller in group engineering can use example-org/model-a and nothing else. A caller in both groups can use both.
Your own models
A model you bring yourself has its own class in the catalogue, and it is never shown as admitted.
- Assessed: a fine-tune of an admitted base model that has a signed assessment. It needs an attestation version in your config.
- Registered: any other model you bring. There is no assessment, so you must give a digest and you must not name an attestation version. Naming one is refused, because it would claim an assessment that does not exist.
Both are reachable only where your own policy entitles them. The hook resolves the class, and a later release of the ledger schema records it on every row. Rows written under that schema carry the class as admitted, assessed, registered or an explicit null.
Where the catalogue is read from
The hook reads the admitted catalogue from a directory set by GATEWAY_ADMISSION_DIR (default catalogue). It reads the directory at start and again every GATEWAY_ADMISSION_REFRESH_SECONDS seconds (default 60). If a re-read fails, every request is denied until a later re-read succeeds. A fail-closed gateway is an outage by design.
Virtual keys
A virtual key is the credential a person or an agent presents to the hook. It stands in for the user, so provider credentials never reach users.
| Property | Meaning |
|---|---|
| Subject | The identity the key belongs to. |
| Groups | The groups the key carries. Entitlement is resolved from these. |
| Role | standard or admin in the console's issue form. The role selects a limits entry. |
| Expiry | Issue sets a default lifetime of 90 days. |
| Fingerprint | The SHA-256 of the secret. The secret itself is never stored. |
Issue, rotate and revoke
- Issue: the secret is shown once, at issue. Copy it then. It cannot be shown again, and only the fingerprint stays in the list.
- Rotate: replaces the secret and resets the lifetime. A revoked key cannot be rotated.
- Revoke: marks the key revoked, with a reason you can supply.
Every issue, rotation and revocation is audited. Expiry and revocation are checked on every request, with no cache, and fail closed. An expired key is a denial of that key.
You do these in the console's Keys view. The same operations are available on the hook's admin interface under /admin/v1/keys.
People signing in
A person using a browser session is identified through your identity provider, and the group claim they carry becomes their groups. If the identity provider cannot be reached, session requests are refused. Virtual keys, policy and the ledger carry on working.
Limits and what this does not cover
- The reference policy enforces entitlement only. Rate limits and token budgets are not enforced by it.
- Hosted-provider rules (which workloads and data classes may use a provider) exist in the reference policy. They have been tested against fixtures and have not run in a real estate. See environments.
- The model digest in the ledger is the value in your policy config. The hook does not recompute it from weights at request time.
- Writing the Rego for your own rules is your work. Heliast supplies the reference bundle, not your policy.