Skip to content

License

The License page reports what the verifier decided at boot and is the one place a locked-out deployment can be recovered from without shell access.

The licensing model itself — editions, entitlements, offline verification, revocation and air-gap — is documented in Licensing. This page covers the console and its endpoints.

Why this page stays reachable

When a deployment is locked out, everything is refused except health probes, admin sign-in and these endpoints. That is deliberate: if the licence page were locked out with everything else, fixing a bad token would require a shell on the container.

For the same reason the Install card renders even when reading the current state has failed. Installing a token is the action that recovers the deployment, so it must not depend on a read succeeding.

Reading the state

GET /api/admin/license

Always returned:

{
"data": {
"operational": true,
"revoked": false,
"state": "verified",
"features": ["sso-oidc", "audit-log"],
"usage": {
"tenants": { "used": 1, "cap": 0 },
"seats": { "used": 12, "cap": 50 }
},
"grace": { "active": false, "revokedAt": null }
}
}

When state is verified, it also carries licensee, licenseId, tier, airgap, sourceAccess, issuedAt and expiresAt. Otherwise it carries reason — the verifier’s own failure string, which is the only thing that tells you which token you pasted.

The states

StateMeaningPanel reads
verifiedValid licenceServing
absentDevelopment mode, no tokenDevelopment
unverifiedMissing, malformed or unverifiableLocked out
verified + grace.activeRevoked, still inside the grace windowGrace period
revoked past graceGrace elapsedLocked out
uninitializedNothing cached yetLoading

grace.active is surfaced separately because a confirmed revocation inside its window looks identical to a healthy licence on every other field. Traffic is unaffected during grace; the banner is the only warning you get.

The verdict lives in memory, decided at boot. A failure to read this page says nothing about whether the licence is valid — if the gateway is still serving traffic, the licence is fine and the page is the thing that is broken. The panel says so rather than showing an alarming red banner.

Capacity

0 means unlimited. The issuer collapses “unset” to zero, so reading it as “none allowed” would brick every deployment on a tier that omits a cap.

Caps are checked when something is created and are never applied backwards. A licence that allows fewer seats than are already in use does not deactivate anyone — it refuses the next creation.

Only active users occupy a seat. Invited, suspended and deactivated accounts do not.

The panel shows seats only. A tenant cap is present in the payload and nothing gates on it — a deployment has exactly one tenant, created at boot, and no route creates another.

What the entitlements do

Eight flags. Two of them are narrower than their names suggest, and the panel spells both out because misreading them is the most common support question:

FlagGates
sso-oidcOIDC sign-in
sso-samlSAML 2.0 sign-in
scimSCIM 2.0 provisioning
audit-logReading the audit trail. Entries are still recorded — recording is a security control and never stops.
siem-exportForwarding audit entries to a SIEM
rbacDefining custom roles. Permission checks still run on every route; the built-in roles work as normal.
encryption-rotationRotating the encryption key
s3-backupsBackups to S3

Providers, routing strategies, retrieval, model selection and every /v1 endpoint are included at every tier and are never throttled by the licence.

Flags the build does not recognise are ignored, never an error, so a licence issued after a newer feature exists cannot stop an older build from running.

The tier string is a display label shared across the issuer’s other products. It switches nothing on. The features list decides.

Installing a token

POST /api/admin/license
Content-Type: application/json
{ "token": "eyJ…" }

Returns { "data": { "installed": true, "licenseId": "…", "tier": "…" } }.

It takes effect immediately. A locked-out deployment recovers without a restart.

A token that fails verification returns 400, carrying the verifier’s specific message — expired, wrong audience, unknown signing key.

Before posting, the console runs a few instant checks on the pasted value: three non-empty dot-separated segments, valid base64url characters, a readable header, and alg: EdDSA. These are not a substitute for verification; they answer an obvious paste error without a round trip.

A token supplied through LICENSE_TOKEN or LICENSE_TOKEN_FILE outranks anything installed here, so an upload can never silently override infrastructure configuration.

Removing a token

DELETE /api/admin/license

Returns { "data": { "removed": true } }, or removed: false when there was nothing stored — meaning the deployment is running from the environment, which this endpoint cannot touch.

The running state is deliberately left alone. The stored token is deleted and the process keeps serving on the licence it already verified; the effect lands on the next restart. The confirmation requires you to type the licence ID, and states that consequence.

The button is available while unverified as well as while verified, because clearing a token that fails verification is part of recovery.

Endpoints

MethodPathPermission
GET/api/admin/licensesystem:read
POST/api/admin/licensesystem:execute + deployment scope
DELETE/api/admin/licensesystem:execute + deployment scope

All three are allow-listed by the lockout middleware. None is reachable unauthenticated — the licensee name, licence ID and capacity are customer-identifying.

Installation and removal are audited as license.install and license.remove.

The system permission is granted to super-admin only, so this page is not reachable for any other role, including deployment-admin.

  • Licensing — editions, verification, revocation, air-gap and the FAQ
  • Environment VariablesLICENSE_TOKEN, LICENSE_TOKEN_FILE, LICENSE_PUBLIC_KEYS, LICENSE_MODE