Licence

The licence is an offline signed file verified on the control plane at boot and scheduler ticks. It makes no call to a licence server.

Reference · Updated 2026-10-17 · Describes release 0.1.164

Evidence reviewed 8 October 2026; operating baseline 0.1.130 (the 16 October 2026 handover build; see the release notes).

The licence is an offline signed file verified on the control plane at boot and scheduler ticks. It makes no call to a licence server. Establish whether this installation uses Mode A or Mode B before diagnosing blocked runs.

Mode A and Mode B

Mode Configuration Result
A — no enforcement No licence token, or no public verification key unlicensed; Ops says no enforcement; runs continue with a boot warning
B — signed licence Matching licence.publicKey and licence token supplied together Claims and signature are verified; expiry and invalid-state enforcement apply

The published deployment procedure records no production signing key issued yet and Mode A for existing installations. Verification code and the deployment slot are delivered; production issuance and its approval job are not proof of a live Mode B installation. A development-key rehearsal is not a production licence.

Install or renew

For the managed pipeline, the secrets file remains the source of truth. The release/licence owner updates AIOPS_LICENSE there, updates the Azure DevOps group, and deploys with the current live image tag. The values file on the customer branch carries licence.publicKey. The pipeline passes the token through licence.value to the control plane.

The update command comes from deployment runbook §11. Set AIOPS_SLUG to the approved installation slug; ADO_PAT must already be supplied securely in the operator's environment and must not be printed.

AIOPS_SLUG=example-org
python3 scripts/customer-onboard.py ado "$AIOPS_SLUG" --update-group

That command writes every group value from the file; a value changed only in the Azure DevOps Library will be overwritten. Queue the normal approved deployment with the current image/chart. There is no upload or activation endpoint; renewal needs a rollout. Confirm the resulting licence card and licence.state_changed audit evidence. The scheduler picks up the state within a minute of startup.

For a chart using top-level existingSecret, put the licence in that Secret or licence.existingSecret; inline licence.value has nowhere to land and the chart refuses that combination. The licence reference defines the supported Secret keys.

Expiry and failure behaviour

State Operator sees Effect
valid Licence card Normal runs
grace Expired banner and grace days remaining Warning only; runs continue. A token with grace_days: 0 has no grace
expired Red banner New agent spend and approved executions refuse to start
invalid Red banner with reason Same block as expired; check signature, claims, time and key/token match
unlicensed No-enforcement card Runs continue

The default grace is 30 days unless the signed file overrides it. The verifier allows 300 seconds clock skew; keep system time correct rather than using clock changes as a renewal procedure.

Surface after expiry/invalid state Behaviour
Alert intake Alerts still ingest/correlate; a blocked investigation fails and escalates the incident to a human
New chat, discovery and agent API Refused before start; queued remote discovery can fail; a running chat stops at its next step
Approved change release and automatic execution Refused (licence_blocked, API 409); rollback and ticket routes remain available
Investigation or scan already past its first step Can finish; API-mode resumes are not rechecked, while embedded/database-mode resumes pass the start guard again
Login, portal, Ops, history, audit/RCA exports and reports Continue to work; boot is never blocked

Runner processes need no public key or licence token. The control plane publishes the verdict for database-mode runners and enforces it when handing jobs to API-mode runners. State changes land in the platform audit chain, not a workspace export.

Renewals and key changes to track

Keep expiry, grace, token id and renewal owner in the operating calendar. When the vendor changes the public signing key, install the new public key and a token signed by it in the same rollout. The verifier accepts one key; an old token paired with the new key is invalid. Keep licence renewal separate from tenant secret-store encryption-key rotation.

Sources: Offline licence reference, and Deployment runbook §11.

The file

A compact JWT. Header {"alg":"EdDSA","typ":"JWT"}; the Ed25519 signature is the raw 64 bytes, used directly as the JWS signature. Claims:

Claim Meaning
iss a fixed issuer string: the ISSUER constant in app/aiops/licence.py, which scripts/issue-licence.py also writes
sub the customer slug the file was issued to
aud always aiops
iat, nbf, exp issued / not-before / expiry, epoch seconds
jti unique id of this file
features list of enterprise feature keys; empty = all
max_workspaces integer ceiling or null; informational, shown on the ops console, not enforced
grace_days optional; overrides the platform default (AIOPS_LICENSE_GRACE_DAYS, 30)

Verification requires iss, sub, aud, iat, exp, jti, checks issuer and audience, accepts 300 s of clock skew on exp/nbf/iat, and rejects an iat further in the future than that (a tampered clock). Only EdDSA is accepted — a token signed with any other algorithm is invalid.

Install

Helm (deploy/aiops-helm/values.yaml):

licence:
  publicKey: |
    -----BEGIN PUBLIC KEY-----
    ...the vendor PEM published with the release...
    -----END PUBLIC KEY-----
  # either inline (lands in the chart Secret as AIOPS_LICENSE) ...
  value: "eyJhbGciOiJFZERTQSIs..."
  # ... or a pre-made Secret (External Secrets, sealed-secrets), key AIOPS_LICENSE
  existingSecret: ""
  existingSecretKey: AIOPS_LICENSE

The pod receives AIOPS_LICENSE and AIOPS_LICENSE_PUBLIC_KEY; the app also accepts a PEM whose newlines arrive as literal \n. Any other deployment (compose, bare uvicorn) sets the same two environment variables. With the chart's top-level existingSecret in use, licence.value has nowhere to land and the chart refuses to render — put AIOPS_LICENSE in that Secret or use licence.existingSecret.

Only the control plane needs the two variables. Runner processes (aiops-agent-helm, compose agents profile) carry no key: the control plane publishes its verdict to PlatformSetting key licence at boot and on every state change, and a key-less process's run guard reads that row instead of its own (always unlicensed) cache. api-mode runners never see the guard at all — the control plane enforces it when it hands out the job.

Renewal is a new value (or a new Secret) and a rollout. There is no renewal ceremony, no activation, no upload endpoint; the scheduler tick picks the new state up within a minute of the pod starting.

What it does not do

Next steps