License
Read the appliance's licence state, tier, expiry and warnings, understand what a lapse blocks, and install a renewal without leaving the dashboard.
- Version: 0.4
- Role: admin_user
- Type: task
License is where the appliance states what it is entitled to run, until when, and what it will do when that lapses. The page works offline, it works on an expired licence, and it is deliberately never blocked — the page that fixes a licensing problem can never be hidden by one.
What do I need?
- Licence
- Any
- Role
admin_user
Prerequisites
- You sign in with a role that reaches the page — superadmin, admin_user, or the read-only viewer.
- The appliance host clock is correct and disciplined by NTP. Licence validity is bounded by dates, and a clock that moves backwards invalidates a good licence.
- For an activation or a renewal, a path to your CID222 contact from any machine — the appliance itself needs none.
/assets/screenshots/license-page@0.4.pngRead the page
Open the licence page
Select License in the navigation.
The page reads This appliance runs under a signed, time-limited license. Renewals are installed here — works fully offline. Four tiles summarise the state; the page refreshes itself every 30 seconds.
Read the state badge
Read the License state tile.
It shows one of Valid, Expiring soon, Expired — grace period, Expired, Trial, Trial expired, Invalid, or Licensing not active (dev). Under it the tile names what the state is bound to: the installation id, or the date a trial must be activated by.
Read the tier and the expiry
Read Tier and Expires.
Tier names your package and the licence id and customer it was issued to. Expires counts the days remaining, and turns amber inside 30 days.
Read the fourth tile
Read Accounts, or Capabilities where the licence carries no seat cap.
Accounts shows active accounts against the licensed seat cap and warns when the cap is reached. Capabilities counts how many of the product's capabilities this tier enables.
Read the warnings
Read the Warnings tile if it is present.
Each line is the appliance's own sentence about something it noticed — a self-generated installation identity, a clock rollback, a licence file it could not read, a licensed capability whose service is not running.
What the states mean
| Badge | State | What the product does |
|---|---|---|
| Trial | trial | A built-in 30-day trial from first boot, at the Starter capability set. Fully usable. |
| Valid | valid | Normal operation. |
| Expiring soon | expiring | Normal operation, inside the last 30 days. |
| Expired — grace period | grace | Normal operation, past expiry but inside the grace period the licence file itself names. |
| Expired | expired | Product routes answer 402 LICENSE_EXPIRED. |
| Trial expired | trial_expired | The same, with the trial elapsed and no licence installed. |
| Invalid | invalid | Nothing verifiable: no trust anchor, a wrong installation, a bad signature, or the host clock moved backwards. Product routes answer 402. |
| Licensing not active (dev) | disabled | No trust anchor is installed and enforcement is not required. Nothing is blocked. |
Each transition into expiring, grace, expired or trial-expired writes an event —
LICENSE_EXPIRING, LICENSE_GRACE, LICENSE_EXPIRED, LICENSE_TRIAL_EXPIRED — so a lapse can
be alerted on in your SIEM rather than discovered by someone opening this page.
What a lapse actually blocks
A 402 is narrow by design. It blocks the product paths — chat, guardrails, gateway API-key
validation, the inline-proxy inspection intake, image analysis, document analysis, and posting a
message into a session — and leaves everything else working. Sign-in, the dashboard, the whole administrative surface, this
page and the upload on it are never blocked, so an appliance can always be re-licensed without a
rescue procedure.
The body is explicit about the remedy:
In the dashboard the same condition raises one banner: This appliance's licence has expired — chat, the proxy and inspection are blocked. An administrator can install a renewal under Settings → License.
Danger
Release images ship with licence enforcement switched on (CID_REQUIRE_LICENSE). On such an
appliance a missing trust anchor is not a permissive state — it fails closed, and the warnings
tile says so.
Capabilities and grandfathering
The licence file carries a tier, and the tier carries a capability set. The appliance grants the union of the capabilities named in the file and the capabilities of its tier. A capability added to the product after your licence was signed therefore works without a re-issue, and the page says so in its own words under Capabilities: a licence keeps every capability its tier includes, including ones added after it was issued.
An endpoint outside your package answers 403 FEATURE_NOT_LICENSED, naming the capability and
your tier. In the dashboard that becomes Not included in this installation's licence package.
Contact your CID222 representative to add it. Which capability belongs to which package is
generated from the code on the licence tiers reference.
Install a renewal
The full procedure, including the air-gapped path, is on Licensing and activation. From this page it is three actions.
Export the activation request
Select Export activation request.
A file named cid-activation-<installation-id>.json downloads. It carries the installation
id, the appliance fingerprint and how strongly the identity is bound — nothing else.
Send it and receive a signed file
Send the file to your CID222 contact and collect the .lic they return.
The appliance contacts no licence server at any point.
Upload it
Drop the file on Drop your .lic file here, or select Upload license (.lic).
A toast reports License installed — valid until date, and the tiles switch to the new tier within seconds. Nothing restarts and no setting is lost.
Fields reference
GET /admin/license/status returns what the page shows.
| Field on the page | Response field | Notes |
|---|---|---|
| License state | state | The eight states above. |
| Tier | tier | starter, professional, enterprise, or none. |
| Expires | license.expires_at, license.days_left | From the signed payload. |
| License ID, Customer | license.license_id, license.customer | Who the licence was issued to. |
| Installation, Fingerprint | installation.installation_id, installation.fingerprint | What the licence is bound to. A soft binding is labelled (self-generated). |
| Accounts | accounts.used, accounts.limit | The seat cap, when the licence carries one. |
| Capabilities | entitlements, feature_catalog | The resolved union, and the labels for each capability. |
| Warnings | warnings | The appliance's own sentences, rendered verbatim. |
| — | trial | Trial start, end and days remaining. |
A separate read, GET /admin/license/entitlements, is what every screen consults to know what is
licensed. It carries no role guard on purpose — gating it would make the answer depend on itself.
Verify
- License state reads Valid, Tier names your package, and Expires is in the future.
- The Warnings tile is absent, or names only the soft-binding notice.
- Make one chat request. It succeeds rather than returning
402. - Open a page that belongs to your tier. It opens instead of reporting that the capability is not licensed.
- Sign in as a
viewerand confirm the page renders read-only: the upload is refused withREAD_ONLY_ROLE, not with a licence error.
If it fails
The upload is refused with a 400 naming the signature:
License upload fails with 400 "rejected: signature"
You might see: 400 rejected: signature · 400 rejected: ENOENT · License upload → 400 · licence file will not upload
Before you start
Checks
- 1
Verify the licence trust anchor exists on the host
diagnostics · appliance.license.trustAnchorPresent = true
- 2
Verify the appliance reports an installation id
diagnostics · appliance.license.installationId
- 3
Verify the host clock is disciplined by NTP
diagnostics · host.timeSynced = true
- 4
Read the licence state the gateway reports
GET /admin/license/status → {"state":"active|trial"}
Causes and fixes
| Reached when | Cause | Explanation, fix and verification |
|---|---|---|
| check 1 fails | missing-anchor (critical) | The licence-signing public key (/etc/cid/license-pubkey.pem) was never baked into this image, or was removed. Verification has nothing to check the signature against, so every licence — including a perfectly valid one — is rejected as forged. The API surfaces this as a 400 with a signature or ENOENT message rather than as a missing-key error, which is why the licence file gets blamed. Fix: Restore the trust anchor on the host and restart the gateway container, then upload the licence again. On an appliance the anchor is part of the image: an image that lacks it was built wrong and the fix is to re-run the host repair, not to hand-place a key of unknown provenance. Follow Licensing and activation; if the appliance was built from an image that shipped without the anchor, collect the support bundle and escalate rather than sourcing a key yourself. Verify: diagnostics · appliance.license.trustAnchorPresent = true If that does not apply: Attach the support bundle. The image build, not the appliance, is at fault. |
| check 2 fails | null-installation-id (critical) | The appliance reports no installation id, so no licence can have been issued for it and no licence can bind to it. This normally means the identity was never generated during first boot, or the file holding it is not readable by the gateway container. Fix: Re-run the first-boot identity step and confirm the id appears on Settings → License. Request a licence only once a stable id is shown; a licence issued against a null id is unusable. See Licensing and activation. Verify: diagnostics · appliance.license.installationId |
| check 3 fails | clock-skew (warning) | Licence validity is bounded by dates. A host clock that is wrong by more than the licence's grace window makes a valid licence appear not-yet-valid or expired, and the error surfaces at the same place as a signature failure. Fix: Correct the host clock and enable NTP or chrony, then restart the gateway container and upload the licence again. See Network prerequisites for the NTP egress the appliance needs. Verify: diagnostics · host.timeSynced = true |
| check 4 fails | wrong-licence-for-this-box (critical) | The trust anchor, installation id and clock are all correct, so the signature check is meaningful — and it fails. The licence was issued for a different installation id, was modified in transit (a mail client re-wrapping lines is the usual culprit), or was truncated. Fix: Re-download the licence file from the issuing portal, transfer it as a binary attachment or over a checksum-verified copy, and upload it again without opening it in an editor. If the installation id in the licence does not match this appliance, request a re-issue. Verify: GET /admin/license/status → {"state":"active|trial"} If that does not apply: Attach the support bundle and the installation id shown on Settings → License. |
Everything answers 402 although the setup wizard completed:
Every request returns 402 LICENSE_EXPIRED after the wizard completes
You might see: 402 LICENSE_EXPIRED · chat returns 402 after finishing setup · the dashboard loads but every action fails with payment required · product blocked immediately after setup
Before you start
Checks
- 1
Read whether licence enforcement is switched on for this deployment
diagnostics · appliance.license.requireLicense = true
- 2
Read the licence state the gateway resolved at boot
diagnostics · appliance.license.state = "active|trial"
- 3
Verify the licence trust anchor exists, since without it no licence can ever resolve as active
diagnostics · appliance.license.trustAnchorPresent = true
- 4
Check how long the installed licence has left
diagnostics · appliance.license.expiresAt
Causes and fixes
| Reached when | Cause | Explanation, fix and verification |
|---|---|---|
| check 3 fails | enforcement-on-without-anchor (critical) | CID_REQUIRE_LICENSE is true and the licence-signing public key is absent. The guard demands a valid licence, and verification cannot succeed without the anchor, so every licensed route answers 402 no matter what is uploaded. The appliance is enforcing a check it cannot pass. Fix: Restore the trust anchor and upload the licence, following License upload fails with 400 signature. Turning enforcement off is a deliberate, temporary measure for a lab only — never the fix on a customer appliance, because it disables the entitlement checks the tier relies on. Verify: diagnostics · appliance.license.trustAnchorPresent = true |
| check 2 fails | no-valid-license (critical) | Enforcement is on and the licence resolved to a state other than active or trial — missing, invalid or expired. The setup wizard does not install a licence, so a freshly completed appliance with enforcement on reaches exactly this state until one is uploaded. Fix: Upload the licence issued for this appliance under Settings → License. If no licence has been issued yet, request one against the installation id shown on that page. See Licensing and activation. Verify: GET /admin/license/status → {"state":"active|trial"} |
| check 4 fails | license-genuinely-expired (critical) | A licence is installed and verifies, but its validity period has ended. Enforcement then blocks the product routes while leaving administration reachable, so the appliance can be re-licensed without a rescue procedure. Fix: Upload a renewed licence. If the expiry looks wrong, check the host clock first — a skewed clock ages a valid licence out early and produces exactly this state. Verify: diagnostics · appliance.license.state = "active|trial" |
| check 1 fails | unexpected-402-without-enforcement (warning) | Enforcement is off, so the licence guard should not be blocking anything. A 402 in this state means the value the gateway read at boot differs from the value now configured — usually the container was started before the setting changed and has not been restarted. Fix: Restart the gateway container so it re-reads its configuration, then retry. If 402 persists with enforcement off, collect the support bundle: the effective configuration and the configured one disagree. Verify: diagnostics · appliance.license.requireLicense = false If that does not apply: Attach the support bundle, including the effective posture section. |
Shorter answers:
- license is for installation X, this box is Y → the file was issued against a different appliance. Export the activation request again and ask for a re-issue.
- license is already expired → the file's term ended before you uploaded it.
- A page or endpoint reports
FEATURE_NOT_LICENSED→ the capability is not in your package. - Creating a user is refused with
LIMIT_EXCEEDED→ the seat cap is reached. The refusal names the limit and how many are in use. Deactivating a leaver frees a seat, and existing users are never blocked from signing in. The refusal also writesLICENSE_LIMIT_EXCEEDED. - The warnings tile reports a clock rollback → fix the host clock and NTP. The appliance keeps a high-water mark and treats a large backward jump as tampering.
- The warnings tile reports that the installation identity is self-generated → the licence is bound to a file rather than to the machine. It is enforced normally; bind it to the hardware before the next renewal.
Next steps
- Licensing and activation — the full activation procedure, online and across an air gap.
- Licence tiers — the generated table of which capability each package entitles.
- System updates — updates never change your package; the licence file is what defines capabilities.
Last updated on