Licence tiers and feature keys
Every licence FeatureKey, the tier that grants it, and the controllers and pages it switches off.
- Type: reference
Every capability the product can sell, the tier that grants it, and exactly what an operator
loses when the key is absent. Regenerate with make docs-generate.
Entitlements resolve as union(payload.features, featuresOfTier(tier)), so a key added to a tier
after a licence was signed is still granted to that licence — an upgrade never silently revokes.
Tiers (3)
| Tier | Label | Aliases | Keys |
|---|---|---|---|
starter | Starter | — | 11 |
professional | Professional | medium | 13 |
enterprise | Enterprise | — | 17 |
Feature keys (17)
FeatureKey | Label | Group | starter | professional | enterprise | What it switches off | Controllers | Pages |
|---|---|---|---|---|---|---|---|---|
chat | Chat gateway | core | ● | ● | ● | The chat gateway itself. Not gated: refusing it is refusing the product, which is what the expiry block (LicenseGuard) already does. | — | — |
proxy | API proxy | core | ● | ● | ● | Provider-passthrough API traffic. Same reasoning as chat — it is the product, not a module. | — | — |
icap | ICAP / inline proxy | core | ● | ● | ● | The inline-proxy / ICAP inspection admin surface: policy, attribution and proxy management. The /inspect/v1 data path is deliberately outside the gate. | InspectionAdminController, AttributionAdminController, InlineProxyManagementController | /inspection |
ocr | Image OCR | core | ● | ● | ● | Image OCR inside image-analysis. A pipeline stage, reached only through endpoints chat/documents already cover. | — | — |
documents | Document analysis | core | ● | ● | ● | PDF/DOCX extraction inside document-analysis. Pipeline stage, as above. | — | — |
redaction | Redaction | core | ● | ● | ● | Image/document redaction. Pipeline stage, as above. | — | — |
ext.browser | Browser extension | core | ● | ● | ● | The browser extension end to end: enrolment, attestation, deployment, the firewall EDL, and the AI-usage feed the extension produces. | AdminEventsController, AiUsageAdminController, AttestationController, BrowserExtensionAuthController, BrowserExtensionManagementController, EdlController, EdlTokensAdminController, ExtensionDeploymentAdminController, ExtensionDevicesAdminController | /browser-extension, /ai-usage |
cost.calculator | Token cost calculator | core | ● | ● | ● | Token cost and savings reporting, including the PDF export. | CostReportController | /cost-savings |
compliance | Compliance & GRC | core | ● | ● | ● | GDPR/HIPAA tracking, compliance findings, reports, snapshots, evidence packs and the GRC/CRQ simulator. | ComplianceController, ComplianceFindingsController, ComplianceReportController, ComplianceSnapshotController, EvidencePackController, GrcSimulatorController | /compliance, /compliance-reports |
governance | AI governance inventory | core | ● | ● | ● | The AI asset inventory: shadow/unowned discovery, ownership, and EU AI Act risk tiers. | GovernanceController | /governance, /governance/inventory, /governance/risk, /governance/ownership |
endpoint.agent | Endpoint agent fleet | core | ● | ● | ● | Fleet administration for the OS-level endpoint agent. The device-facing enroll/policy/heartbeat API is NOT gated — see the file comment. | EndpointAgentAdminController | /endpoint-agent |
mcp.server | MCP server | addon | — | ● | ● | The read-only MCP server (a separate container, opt-in by compose profile). Nothing in the gateway fronts it, so there is no route to gate; the key stays as the commercial flag. | — | — |
cost.token_reduction | Token cost reduction | addon | — | ● | ● | Prompt compression ahead of the provider call. Runs inside the chat path, so it has no route of its own — gating it belongs in prompt-compression/, not here. | — | — |
codesafety | Code Safety | enterprise | — | — | ● | Code Safety: repo tracking admin and the auditor model-redaction console. The detection path checks the same key in code-safety-detection.service.ts. | RepoConnectorAdminController, RepoAssignmentAdminController, ModelRedactionController | /repo-connector |
risk.detailed | Detailed risk analysis | enterprise | — | — | ● | The Risk Analysis findings store and review API. It has no page row of its own — the dashboard reaches it from /llm-review, which risk.llm_review gates. | RiskAnalysisController | — |
risk.llm_review | LLM Risk Analyst | enterprise | — | — | ● | The LLM Risk Analyst: review pipeline, attack knowledge, the group-leader view, and the ATLAS/OWASP reference tables the analyst reasons over. | AdminLlmReviewController, AttackKnowledgeController, TeamController, AtlasController, OwaspController | /llm-review, /analyst, /my-team |
integrations.siem | SIEM export | enterprise | — | — | ● | Outbound SIEM export (syslog/CEF + webhook). Configured from a Settings pane, which is not a registry-governed page of its own; the export processor checks the same key. | IntegrationsController | — |
Keys that gate nothing by design
chat, proxy, ocr, documents, redaction, mcp.server, cost.token_reduction carry empty controller and page lists. They are pipeline stages or the
product itself, reached through the chat and analysis endpoints rather than through a module of
their own; naming a controller to make the table look complete would be exactly the drift
module-features.ts exists to prevent. Expiry still blocks them, via LicenseGuard.
Deliberately ungated controllers (53)
Being on this list is a claim that the absence of a licence gate is correct — not that the
controller is unprotected. Most carry @Roles; several carry a callback token, a private-address
check or an HMAC. Licence gating is a separate axis from access control.
| Controller | File | Axis | Why |
|---|---|---|---|
AppController | cid-nestjs-gateway/app.controller.ts | fix_path | Root + /health. The healthcheck a licence-blocked box is judged by. |
AuthController | cid-nestjs-gateway/auth/auth.controller.ts | fix_path | Login. Nobody can upload a renewal without signing in first. |
LicenseController | cid-nestjs-gateway/license/license.controller.ts | fix_path | Status, activation request and licence upload — the renewal itself. |
EntitlementsController | cid-nestjs-gateway/license/entitlements.controller.ts | fix_path | The probe every screen reads to know what is licensed. Gating it would make the answer depend on itself. |
LicenseAdminController | cid-nestjs-gateway/license-admin/license-admin.controller.ts | fix_path | The owner console issues licences. It is gated on being the hub (OwnerConsoleGuard), a stronger and more specific condition than any tier. |
SetupController | cid-nestjs-gateway/setup/setup.controller.ts | fix_path | First-boot wizard. It runs before a licence exists. |
SystemUpdateController | cid-nestjs-gateway/system-update/system-update.controller.ts | fix_path | Installing a new version can be the fix for a licensing bug; a licence must not be able to block its own remedy. |
DiagnosticsController | cid-nestjs-gateway/diagnostics/diagnostics.controller.ts | fix_path | Appliance self-diagnostics (snapshot, support bundle). It exists to explain a broken box — including a box whose licence state is the problem — so it must never be behind an entitlement. |
CertificatesController | cid-nestjs-gateway/certificates/certificates.controller.ts | fix_path | The appliance's own HTTPS certificate. A box whose certificate expired or whose site proxy re-signs TLS cannot reach the licence hub or show the dashboard without a warning; HTTPS is platform plumbing every tier needs, never an entitlement. |
ProvisionController | cid-nestjs-gateway/provision/provision.controller.ts | fix_path | External tenant provisioning, authenticated by PROVISION_TOKEN. Not a product page and not reachable by a user. |
ChatController | cid-nestjs-gateway/chat/chat.controller.ts | data_plane | The product. chat is in every tier; refusing it is what the expiry block (LicenseGuard) already does, at the right layer and with the right status code. |
SessionsController | cid-nestjs-gateway/sessions/sessions.controller.ts | data_plane | Context for the chat path; same key, same reasoning. |
GuardrailsController | cid-nestjs-gateway/guardrails/guardrails.controller.ts | data_plane | Detection API. Gating detection is gating the safety control itself, which is never the right answer to a billing state. |
ImageAnalysisController | cid-nestjs-gateway/image-analysis/image-analysis.controller.ts | data_plane | ocr is a pipeline stage in every tier, reached through this endpoint rather than a module of its own. |
DocumentAnalysisController | cid-nestjs-gateway/document-analysis/document-analysis.controller.ts | data_plane | documents/redaction are pipeline stages in every tier — same reasoning as image analysis. |
HttpCalloutController | cid-nestjs-gateway/inline-proxy-inspect/http-callout.controller.ts | data_plane | The SWG inline callout (/inspect/v1). A 403 fails the proxy either open (traffic passes uninspected) or closed (the customer stops browsing); both are worse than an expired-licence banner. |
FileRedactionController | cid-nestjs-gateway/inline-proxy-inspect/file-redaction.controller.ts | data_plane | The file half of the same callout, and the same failure either way. |
InternalController | cid-nestjs-gateway/internal/internal.controller.ts | data_plane | Service-to-service (key validation, code-safety assess), authenticated by shared secret. Breaking it breaks the pipeline, not a page. |
EndpointAgentController | cid-nestjs-gateway/endpoint-agent/endpoint-agent.controller.ts | device_api | Enroll/policy/heartbeat. A deployed fleet must not brick mid-cycle because a renewal is late — the ADMIN surface goes dark (EndpointAgentAdminController, endpoint.agent), the agents keep enforcing policy. |
PacFileController | cid-nestjs-gateway/inline-proxy-inspect/inline-proxy-management.controller.ts | bootstrap | Unauthenticated PAC file for client browsers. There is no actor to have an entitlement. |
ApplianceCaPublicController | cid-nestjs-gateway/certificates/certificates.controller.ts | bootstrap | Unauthenticated download of the appliance root CA — what a machine needs before it can trust the dashboard at all. No actor, no entitlement. |
InlineProxyReloadController | cid-nestjs-gateway/inline-proxy-inspect/reload.controller.ts | bootstrap | Internal reload hook for the inline proxy container. |
RepoWebhookController | cid-nestjs-gateway/repo-connector/webhook.controller.ts | bootstrap | Inbound git webhook, authenticated by HMAC. Dropping deliveries on a lapsed licence loses events that are never resent; the admin surface is gated by codesafety instead. |
GithubManifestController | cid-nestjs-gateway/repo-connector/github-manifest.controller.ts | bootstrap | GitHub App manifest exchange (OAuth state-authenticated), part of connector setup rather than the product. |
LlmReviewInternalController | cid-nestjs-gateway/llm-review/llm-review-internal.controller.ts | bootstrap | Where cid-risk-analyst posts a finished review, authenticated by private address + a one-time callback token. It has no JWT and no tier. |
AllDetectionsController | cid-nestjs-gateway/inline-proxy-inspect/all-detections.controller.ts | no_single_key | The unified feed spans extension, ICAP and chat traffic, so no one key owns it and gating on any of them would blank rows the box is licensed to show. |
LlmReviewUserController | cid-nestjs-gateway/llm-review/llm-review-user.controller.ts | no_single_key | What a LOCKED user can reach: read their own lock, ask for it to be lifted. A lock keeps enforcing after a licence lapses, so the route that explains it cannot be gated on the same licence. |
FiltersController | cid-nestjs-gateway/filters/filters.controller.ts | always_on | Input filter rules — core configuration, sold with every tier. |
FilterImportExportController | cid-nestjs-gateway/filters/import-export/filter-import-export.controller.ts | always_on | Import/export of those same always-on rules; a customer must be able to take their own filter configuration with them. |
FilterOverridesController | cid-nestjs-gateway/guardrails/overrides/filter-overrides.controller.ts | always_on | Per-detection overrides for those same rules. |
DetectionEscalationController | cid-nestjs-gateway/guardrails/escalation/detection-escalation.controller.ts | always_on | The count rule that turns a bulk MASK into a REJECT. Part of the always-on detection policy: gating it would let a billing state decide whether bulk personal data leaves the building. |
OutputFiltersController | cid-nestjs-gateway/output-filters/output-filters.controller.ts | always_on | Response-side filter rules — the output half of the same always-on module. |
DetectionsController | cid-nestjs-gateway/detections/detections.controller.ts | always_on | Querying what the always-on detection pipeline found. |
EventLoggingController | cid-nestjs-gateway/event-logging/event-logging.controller.ts | always_on | The audit trail. Hiding audit evidence behind a tier is not something to sell. |
TenantsController | cid-nestjs-gateway/tenants/tenants.controller.ts | always_on | Account management. Seat COUNT is licensed (limits.users, AccountLimitService); the ability to manage accounts is not. |
TenantGroupsController | cid-nestjs-gateway/tenant-groups/tenant-groups.controller.ts | always_on | Group membership and group credentials — core account management. |
CredentialsController | cid-nestjs-gateway/credentials/credentials.controller.ts | always_on | Provider API keys. Without them no tier works at all. |
LlmReadinessController | cid-nestjs-gateway/credentials/llm-readiness.controller.ts | always_on | Whether those credentials actually reach a provider. |
ApiKeysController | cid-nestjs-gateway/api-keys/api-keys.controller.ts | always_on | Gateway API keys (cid_key_*) — the other way to reach chat/proxy, which every tier includes. |
ModelsController | cid-nestjs-gateway/models/models.controller.ts | always_on | Model discovery for the chat path — which models a tenant may pick. Every tier has chat, so every tier needs this. |
AdminModelsController | cid-nestjs-gateway/models/admin-models.controller.ts | always_on | Admin side of the same model inventory: adding and pricing the models the always-on chat path routes to. |
ModelRoutingController | cid-nestjs-gateway/model-routing/model-routing.controller.ts | always_on | Which model a request goes to — a setting on the always-on chat path. |
MlEngineController | cid-nestjs-gateway/ml-engine/ml-engine.controller.ts | always_on | Health and control of the detection ML services, which every tier runs. |
MlTuningController | cid-nestjs-gateway/ml-tuning/ml-tuning.controller.ts | always_on | Detection thresholds for those same always-on detectors — tuning a safety control, not a capability anyone buys separately. |
DocumentRedactionSettingsController | cid-nestjs-gateway/document-analysis/document-redaction-settings.controller.ts | always_on | Settings for redaction, which is in every tier. |
OcrSettingsController | cid-nestjs-gateway/ocr-settings/ocr-settings.controller.ts | always_on | Which one OCR engine image analysis runs. OCR itself is gated where it runs (image analysis); this is only the superadmin pane that picks the engine, and a tier without images sees a setting with nothing to apply to rather than a 403. |
TenantAnalyticsController | cid-nestjs-gateway/tenant-analytics/tenant-analytics.controller.ts | always_on | Usage statistics for the box. cost.calculator gates the cost REPORT (CostReportController); raw usage is not sold separately. |
LdapController | cid-nestjs-gateway/ldap/ldap.controller.ts | always_on | AD/LDAP authentication. Gating it would lock every AD user out of a box whose licence lapsed, including the admin coming to renew it. |
PasswordDeliveryController | cid-nestjs-gateway/password-delivery/password-delivery.controller.ts | always_on | How reset links are delivered — part of getting back into the box. |
HelpController | cid-nestjs-gateway/help/help.controller.ts | always_on | The in-product support assistant, which exists precisely to explain states like an expired licence. |
RbacAdminController | cid-nestjs-gateway/rbac/rbac.controller.ts | always_on | Page-access matrix administration. Access control is not a tier feature. |
RbacManifestController | cid-nestjs-gateway/rbac/rbac.controller.ts | always_on | The my-pages manifest every dashboard route guard reads. |
PromptCompressionController | cid-nestjs-gateway/prompt-compression/prompt-compression.controller.ts | always_on | Superadmin runtime settings for prompt compression. The CAPABILITY is sold as cost.token_reduction and enforced where it runs, in the chat path — this is only the pane that toggles it. Left ungated deliberately: on a tier without the key the switch is inert rather than hidden, which is a settings pane that does nothing rather than a 403 with no explanation. Gating it is a defensible product change; make it deliberately if at all. |
TS ↔ issuer drift
cid-nestjs-gateway/license/tiers.ts and appliance/licensing/tiers.json agree on every key and every tier.