Key Backend Not Offered
URI: https://docs.ankatech.co/errors/key-backend-not-offered
HTTP Status: 422 Unprocessable Entity
When you see this
An operator-plane transition into the key-wrapping path named a mechanism this deployment is not offering. The token is one of the nine the platform supports, which is why this is a 422 and not a schema rejection: the mechanism is real, and the deployment has simply not granted it.
Four operations raise it, on the two operator-facing planes — the declare and the bind of each:
| Operation | Plane | Why it is a separate gate |
|---|---|---|
POST /api/v3/admin/platform/setup/backend/declare | deployment | the declare chooses the mechanism |
POST /api/v3/admin/platform/setup/backend/activate | deployment | on this plane activate is the bind: it takes no backend argument and reads the token off the persisted declaration, so the declare gate cannot see it |
PUT /api/v3/admin/platform/key-backend/tiers/{tier} | ANKA-managed tier | the tier selection chooses the mechanism |
POST /api/v3/admin/platform/key-backend/tiers/{tier}/bind | ANKA-managed tier | the bind acts on a choice already persisted — a tier left in DECLARED_PENDING_BIND on a mechanism the operator has since withdrawn |
A declaration written before the offering gate existed, or one left standing when the operator narrowed the grant, is exactly what the two bind-side gates catch.
Nothing was written by any of them. No descriptor was derived, no credential was unsealed, no endpoint was dialled, and nothing was audited as a mutation. On the tier plane the refusal is not audited at all, deliberately: ManagedBackendTierAuditRecorder carries no refusal arm, and auditing this one refusal would leave a trail on which a single refusal type is queryable and the others beside it are not.
A backend already bound is never invalidated. The tier bind's gate sits after that verb's idempotent ACTIVE return, and a deployment already serving its declaration is exempt for the same reason: withdrawing a mechanism must not take away access to data already wrapped under it.
Common Causes
- The mechanism's certification level is
EXPERIMENTALand the platform-settings grantankasecure.key-protection.experimental.offered-backendsdoes not name it. On a fresh deployment that grant is seeded withcloudhsmandgcp-kms-hsm, solunaandnshieldare the two mechanisms that arrive not offered. - The mechanism is declared in the release's certification manifest at a level the grant cannot reach, or is not declared in the manifest at all. The grant governs
EXPERIMENTALmechanisms only, so it cannot be used to offer these — and thedetailsays so rather than giving advice that cannot work.
The composed verdict, for a mechanism m:
A CERTIFIED mechanism is therefore never refused here: the grant cannot withdraw evidence that exists.
Response Example
{
"type": "https://docs.ankatech.co/errors/key-backend-not-offered",
"title": "Key Backend Not Offered",
"status": 422,
"detail": "The key-protection backend 'luna' is at certification level EXPERIMENTAL and is not currently offered on this deployment. A platform administrator can offer it by adding 'luna' to the setting ankasecure.key-protection.experimental.offered-backends.",
"instance": "/api/v3/admin/platform/setup/backend/declare",
"timestamp": 1787494020,
"extensions": {
"requestId": "822b94fa-7f0f-4c2e-9b53-2f0f4c2e9b53"
}
}
Content-Type: application/problem+json — the response follows RFC 9457 Problem Details.
The detail has three shapes, and which one you get says what will help:
| The mechanism | What the detail says |
|---|---|
Declared EXPERIMENTAL | Names the mechanism, its level, and the setting to add it to. This is the only shape that promises the setting will help. |
| Declared at another level | Names the mechanism and its level, and says the grant offers EXPERIMENTAL mechanisms only and cannot offer it. |
| Not declared in the manifest | Says so, and says the grant reaches only mechanisms the manifest declares EXPERIMENTAL. |
This is the deployment plane. The tenant plane refuses differently
The same offering verdict is the fourth factor in per-tenant BYOK admission, but a tenant-plane refusal answers 422 key-backend-not-admissible with a body that is byte-identical for every refused token and every refusing factor. That is deliberate: a tenant must not be able to probe the deployment's configuration by reading refusals. The discriminant — NOT_OFFERED, alongside NOT_ENTITLED, DEPLOYMENT_POLICY and OPERATOR_ALLOWLIST — is recorded on the admin audit trail, not in the response.
How to Resolve
- Read
GET /api/v3/admin/platform/setup/backend/declarable. Every row carries this deployment's own verdict: a mechanism that is not offered reportsadmissible: falsewithrefusalReason: NOT_OFFERED, so the projection and the declare answer from the same composition and cannot disagree. - If you intend to use the mechanism, add its token to
ankasecure.key-protection.experimental.offered-backends—Platform Settings → Cryptography & Trust → HSM / Cloud KMS. It is a hot-reload setting: no rebuild, no release, no restart. Writing it needsadmin.platform.config.dangerousin addition toadmin.platform.config.write, because its widest state admits an unexercised mechanism into the key-wrapping path. - Narrow the grant again when you are finished. Removing a token gates the declare and the bind on all three planes — the deployment, the ANKA-managed tier and tenant BYOK — from that moment on; it never invalidates a backend that is already bound.
- To change what the platform claims about a mechanism's evidence, move its
levelin the Integration Certification Matrix*. That is a claim about evidence and it is a code change, deliberately — this setting is not a way to make one.
For full schema definitions, examples, and interactive testing, see the Developer Hub Reference.