Skip to main content
Version: v5.6.1

Licence status

Included from Basic tier

licence_status is the resolved-entitlements payload: the effective tier, expiry state, limits and features computed by re-verifying every stored licence envelope against the embedded keyring and denylist, plus each stored document's own state. One serializer (SerializeLicenceStatus) renders it everywhere it appears:

  • as the licence_status attribute of the GET /api/info singleton (served beside the legacy licence_info attribute, whose GUI contract is unchanged);
  • in the PUT /api/licence 201 response (beside the applied document) and the DELETE /api/licence/{licence_id} 200 response;
  • as the licence_status field of GET /api/admin/licence (and its original mount GET /admin/licence), wrapped in the usage report below;
  • over API v2 as LicenceService.GetLicenceStatus (field for field, same any-authenticated exposure; a resolution failure answers unavailable instead of embedding an error field). See opschain docs api-v2.

The v2 OpenAPI document declares this resource's schema; the resource-field coverage gate holds this page to it.

Fields​

FieldTypeMeaning
tierstringThe effective tier: free (no enterprise document — the embedded defaults) or enterprise. (pack exists only as a per-document tier, never as the resolution tier.)
statestringThe computed licence lifecycle state — vocabulary below. With no enterprise document the Basic tier applies and state is valid (the Basic tier never expires).
limitsobjectThe effective countable limits: projects, environments, assets, runners, agents (ints; -1 = unlimited), and runner_groups (optional int — absent from a licence document means unlimited).
featuresstring arrayThe effective feature entitlements. Always present (serialized [] when empty). Fixed vocabulary: agents, assets, maintenance, templates, triggers, plus packs:<name> per signed commercial pack.
unknown_featuresstring arrayGranted names outside this build's vocabulary — inert, never a verification failure (issued licences may carry names a build does not gate). Absent when none.
grace_daysintThe effective expiry grace window in days — the winning base document's own grace_days (default 30). Grace is licence-carried, never a server setting.
documentsarrayEvery stored, verified, non-denylisted document's own report (below). Always present.
invalid_documentsintStored documents that failed signature re-verification this resolution — they contribute nothing. Absent when 0.
denied_documentsintStored documents revoked by the embedded denylist — they contribute nothing. Absent when 0.

documents entries​

FieldTypeMeaning
licence_idstringThe document's identity; applying a renewal with a new id accumulates beside its predecessor.
licenseestringWho the licence was issued to.
tierstringThe document's own tier: enterprise or pack.
issuedstringIssue date (plain date or RFC 3339). Absent when the document carries none.
expiresstringExpiry (plain date or RFC 3339; a date-only value ends validity at 00:00:00 UTC that day).
statestringThis document's own computed expiry state (same vocabulary as the top-level state), or the reporting-only state revoked (denylisted) — such a document contributes nothing whatever the dates say.

state vocabulary​

Verbatim from the code (pkg/licence/resolve.go): valid, in_grace, expired; per document additionally revoked.

  • valid — before expiry.
  • in_grace — past expiry, within the document's grace window.
  • expired — past grace. This is the work-stop state: the server refuses starting new work (CheckWork) and the runner offer gate parks dispatch (WorkStopped), while reads and licence uploads always pass. Expiry never reverts entitlements — an expired enterprise licence keeps its limits and features, with state carrying the stop signal; only explicitly removing the last enterprise document reverts to the Basic tier.

When several enterprise documents are stored, the one expiring last decides the resolution's state; pack documents layer their features beside the base while valid or in_grace and stop contributing once expired.

Where the values come from and what changes them​

Resolution re-verifies every stored envelope on each read (never a stored validity flag), with a 15-second cache that additionally expires at the next expiry-state boundary so a state change is observed on time. PUT /api/licence (a verified Ed25519 envelope) and DELETE /api/licence/{licence_id} change the stored set and invalidate the cache; the clock changes state on its own.

Failure shapes: a resolution error on GET /api/info renders the attribute as {"state": "unknown", "error": "..."}; a serialization failure renders {"error":"licence status unavailable"}. Compositions without the entitlement service omit the attribute entirely (the legacy shape).

The admin wrapper (GET /api/admin/licence)​

The admin licence usage report embeds licence_status with:

FieldTypeMeaning
enforcementstringenforced, or not wired when the composition has no entitlement service (then licence_status and usage are absent).
store_kindstringThe server's data store: postgres or sqlite — the support bundle's licence.json carries it, so support sees at once which store the server runs on.
licence_statusobjectThe payload above.
usageobject mapPer countable limit — keys projects, environments, assets, agents, runners, runner_groups — each {"used": <int>, "limit": <int>}, counted by the same rules as the create gates (live non-deleted nodes per kind; registered unrevoked fleet members per kind; distinct runner groups). A key is absent when its count failed.
notesstring arrayNamed degradations (e.g. "node usage counts are unavailable: …") rather than silent omission. Absent when none.
  • opschain docs entitlements — enforcement gates, grace and the apply / remove lifecycle.
  • opschain docs fleet-queue — the licence expired dispatch verdict the expired state produces.