Skip to content

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 EXPERIMENTAL and the platform-settings grant ankasecure.key-protection.experimental.offered-backends does not name it. On a fresh deployment that grant is seeded with cloudhsm and gcp-kms-hsm, so luna and nshield are 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 EXPERIMENTAL mechanisms only, so it cannot be used to offer these — and the detail says so rather than giving advice that cannot work.

The composed verdict, for a mechanism m:

offered(m) = manifest.selectable(m) AND ( level(m) != EXPERIMENTAL OR m ∈ the grant )

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

  1. Read GET /api/v3/admin/platform/setup/backend/declarable. Every row carries this deployment's own verdict: a mechanism that is not offered reports admissible: false with refusalReason: NOT_OFFERED, so the projection and the declare answer from the same composition and cannot disagree.
  2. If you intend to use the mechanism, add its token to ankasecure.key-protection.experimental.offered-backendsPlatform Settings → Cryptography & Trust → HSM / Cloud KMS. It is a hot-reload setting: no rebuild, no release, no restart. Writing it needs admin.platform.config.dangerous in addition to admin.platform.config.write, because its widest state admits an unexercised mechanism into the key-wrapping path.
  3. 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.
  4. To change what the platform claims about a mechanism's evidence, move its level in 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.

* restricted content — see higher-tier documentation