# Licensing & Features

Omnia is a commercial product. Its [LICENSE](https://github.com/AltairaLabs/Omnia/blob/main/LICENSE) grants use only under one of three licences: a Development Licence for development, evaluation, testing and demonstration, with every Enterprise feature included; a Core Edition Licence for production use without Enterprise features; and an Enterprise Licence, granted under a commercial agreement, for production use with them. This page explains how the software tells those situations apart at runtime, what a licence key changes, and what happens when it lapses.

The wider AltairaLabs ecosystem is open, so you can build and evaluate before deploying to Omnia:

| Project | Licence |
|---------|---------|
| [PromptPack](https://promptpack.altairalabs.ai) | Open specification (CC-BY) |
| [PromptKit](https://promptkit.altairalabs.ai) | Apache-2.0 |
| PromptArena CLI | Apache-2.0 |
| **Omnia** | **Commercial** |

## Three states, and only three

Two inputs decide everything: whether the chart installs the Enterprise product (`enterprise.enabled`), and whether a licence is present.

|  | Configuration | Behaviour |
|---|---|---|
| **Core** | `enterprise.enabled: false` | Enterprise features absent |
| **Dev mode** | `enterprise.enabled: true`, no licence | Every feature on, startup banner on every service |
| **Licensed** | `enterprise.enabled: true` plus a licence | Every feature on, silent |

Enterprise without a licence is dev mode. No `devMode` value exists to switch on: the chart derives it when `license.key` and `license.existingSecret` are both unset and `license.externallyManaged` is not set, and passes `--dev-mode` to arena-controller. A dev-mode install is fully functional and not licensed for production use, and every service says so in its logs.

Deriving dev mode avoids a second switch that could disagree with the first. Someone who turns Enterprise on to try it gets all of it, and the banner is the reminder that production needs a licence. A real licence takes over from the dev fallback on the next cache refresh, with no reinstall.

## How a service decides

One resolver, `ee/pkg/license.ResolveAndNag`, makes the startup decision for the operator, memory-api and privacy-api. arena-controller holds the only validator that reads the licence, and serves what it finds at `/api/v1/license`. Every other consumer is a client of that URL, which the operator hands to the data plane as `OPERATOR_API_URL`.

```mermaid
flowchart LR
  Secret["Secret arena-license<br/>(install namespace)"] -->|JWT, read| AC["arena-controller<br/>license validator"]
  CM["ConfigMap arena-license-public-key<br/>(optional)"] -->|public key, re-read on cache refresh| AC
  OP["operator<br/>ResolveAndNag"] -->|GET /api/v1/license| AC
  MEM["memory-api<br/>ResolveAndNag"] -->|GET /api/v1/license| AC
  PRIV["privacy-api<br/>ResolveAndNag"] -->|GET /api/v1/license| AC
  PB["policy-broker sidecar"] -->|GET /api/v1/license| AC
  DASH["dashboard"] -->|GET /api/v1/license| AC
  OP -->|activation, heartbeat| LS["license.altairalabs.ai"]
```

The licence is per install and lives in the namespace Omnia was installed into, found with `license.InstallNamespace()` (the operator's own namespace, falling back to `omnia-system` only when that cannot be resolved). Nothing about it is per workspace.

A licence is a signed RS256 JWT. arena-controller verifies the signature against an embedded public key, which the `arena-license-public-key` ConfigMap can override. Validation needs no network access. The validator caches the result for five minutes and re-reads both the licence Secret and the public-key ConfigMap when the cache expires. A rotated key or an applied licence therefore reaches arena-controller within five minutes without a restart. Services that resolve once at startup see it after their next restart.

The services then split in two:

- `--enterprise` (or `ENTERPRISE_ENABLED`) says the product is installed. The operator stamps it on every memory-api, session-api and privacy-api it deploys. A licence cannot switch on what was never installed.
- The licence decides whether the installed product runs. `ResolveAndNag` fetches it once at startup, with retries, and checks its tier. An enterprise-tier licence, dev licence included, activates the enterprise paths. If the licence endpoint cannot be reached, the service assumes it is entitled instead of falling back to Core, so an operator outage during a restart cannot quietly downgrade a paying install.

Each resolved decision is followed by one nag: the operator, arena-controller, memory-api, privacy-api and the policy-broker each log a single banner when the licence is not an issued, unexpired enterprise licence. arena-controller logs its banner once its cache has synced, so the banner names the licence it actually read. The nag never blocks.

## Enablement is gated, continuity is not

The line the design draws is between turning Enterprise on and keeping it running. No licence and no `enterprise.enabled` means no Enterprise. Once Enterprise is on, an expired licence nags and does not deactivate anything.

Within a running install a second line applies: capabilities are never blocked, and capacity can be. A capability is something the product can do, such as a Git or OCI source, a load-test or data-generation run, a cron schedule, or a custom facade. A licence that does not cover one produces a warning, and the resource runs. Capacity is how much of it the licence buys. The one capacity limit enforced today is `limits.maxWorkerReplicas`, and a run that asks for more worker replicas than that is refused.

The resolver tests the licence's tier, not its validity. Gating on validity would switch every Enterprise path off at the next restart after a licence lapsed, so the validator verifies the signature, ignores the expiry claims, and carries the expiry date on the licence for the nag to report. Every service logs its startup reminder, and the dashboard shows an expiry advisory: a warning in the 30 days before expiry, an error afterwards.

Enterprise paths run on the licence tier alone, and a missing, dev, or expired licence only logs a banner. arena-controller warns about Arena resources whose source type, job type or schedule the licence does not cover, and refuses only a run whose worker replicas exceed the licence's limit.

## What the licence gates

What a licence changes depends on the component:

- memory-api wires its enterprise paths once at startup: the institutional tier, multi-tier recall across the user, agent and institutional tiers, `MemoryPolicy` tier ranking and per-tier half-life, the shared-write gate and the Memory Galaxy. The same binary runs in every edition, with the enterprise code compiled in. Whether the enterprise paths run is decided at startup by the resolved licence: without it, recall is limited to the user and agent tiers with the identity ranker and the default half-life, and the institutional, gate and Galaxy paths are not wired.
- privacy-api registers its consent, opt-out, DSAR and audit-hub routes only when entitled.
- The operator injects the policy-broker sidecar into agent pods, which is what enforces ToolPolicy, when `ResolveAndNag` returns true. It also stamps the enterprise flag on the services it deploys.
- The dashboard fetches the licence from `/api/v1/license`, which management-api relays from arena-controller. White-labelling is checked server-side, by management-api, against the `whiteLabel` entitlement: without it the dashboard shows the Omnia default whatever the branding configuration says. Unlike the memory and privacy entitlements, `whiteLabel` is not derived from the tier, so an issued enterprise licence needs the bit set. Dev mode grants it. Server-side, memory-api returns 403 `enterprise_required` for the enterprise routes when it is not entitled. If management-api cannot reach arena-controller's licence endpoint it answers dev mode when Enterprise is installed and Core when it is not. See [White-label the Dashboard](/how-to/operations/white-label-the-dashboard/).
- The dashboard's Enterprise surfaces follow the same two lines. Whether one works depends on whether the Enterprise product is installed (`NEXT_PUBLIC_ENTERPRISE_ENABLED`), not on the licence. In Core, each surface (the memory pages, Arena projects and runs, the template registries, the agent registry, realtime evals) is shown switched off, beneath a notice that names it as an Enterprise feature and links to the upgrade page, sales and this page. A switched-off surface makes no requests: none of what it reads exists in Core, so it draws its loading or empty state instead of errors. Setting `dashboard.hideEnterprise` removes those surfaces instead. With Enterprise on, every surface works. Where a surface maps to a licence feature, a licence that does not cover the feature adds a warning notice and the surface keeps working: `memoryEnterprise` for the memory pages, `privacyEnterprise` for the agent registry, and the job type, schedule and source type in the run and template-source dialogs. Dev mode covers every feature, so it shows no such notice; its own advisory is the reminder. The dashboard never reads the licence's expiry to decide what to show. Capacity is the exception here too: the run dialog holds Run when the worker count exceeds `limits.maxWorkerReplicas`, because arena-controller would refuse the run.
- Arena Fleet is Enterprise as a whole: arena-controller is deployed only with `enterprise.enabled`, so a Core install has none of ArenaProject, ArenaRun, load testing, data generation, scheduled jobs or result output. On top of that, arena-controller checks each ArenaProject, ArenaTemplateSource, PromptPackSource and ArenaRun against the licence's features and limits: source type (Git, OCI, S3; an external ArenaProject counts as Git), job type (load testing, data generation), cron scheduling and worker replica count. Hosted ArenaProjects, ConfigMap and workspace sources and evaluation jobs are always covered. A capability the licence does not cover gets a `Warning` event with reason `UnlicensedCapability` on the resource, and the resource proceeds. A run whose worker replicas exceed `limits.maxWorkerReplicas` is refused: it gets `Ready=False` with reason `LicenseViolation` and goes to phase `Failed`.
- At admission: when `webhook.enabled=true` (default `false`), the same Arena checks also run at admission on ArenaProject, ArenaTemplateSource, PromptPackSource and ArenaJob. An uncovered capability is admitted with a warning, which `kubectl` prints. An ArenaJob over the worker-replica limit is rejected. Custom facades (`spec.facades[].type: custom` on an AgentRuntime) are checked only at admission, so only with the webhooks on, and an uncovered one is admitted with a warning.

## Core and Enterprise in practice

Core is a complete agent platform for production: the agent runtime and its providers, PromptPacks, the dashboard, observability, cost tracking, and per-user and per-agent memory.
Enterprise adds:

- Arena Fleet: hosted and external-repository ArenaProjects, evaluation, load-test and data-generation runs, multiple worker replicas, cron schedules, results to an S3-compatible bucket or PVC, and a per-job budget cap. See [Arena Fleet](/explanation/evaluation/arena-fleet/).
- The institutional memory tier, multi-tier recall with policy-driven ranking, the shared-write gate and Memory Galaxy.
- The privacy suite (consent, opt-out, DSAR erasure, audit hub) and ToolPolicy enforcement.
- White-label branding.

Plans are described on the Altaira Labs website.

To turn Enterprise on and install a key, see [Install with a License](/how-to/operations/install-license/).

## Where a licence comes from

Every licence ends up in the same place: the `arena-license` Secret in the install namespace, which arena-controller reads. It gets there from Helm values, from an existing Secret, from a dashboard upload, or, for a marketplace purchase, from the Altaira Labs control plane. The control plane sends the same signed JWT, so nothing downstream can tell the routes apart. The operator verifies the control plane's licence against the embedded key before writing it, and keeps whichever licence was issued last. An outage, or a licence that fails verification, leaves the installed one in force, so a purchase can never lose its licence to a network fault.

## Activation tracking

For enterprise-tier licences the operator can register the cluster with the Altaira Labs licence server (`https://license.altairalabs.ai`). It activates once, then sends a heartbeat every 24 hours. Core-tier licences skip activation, and so does an install with no licence Secret, since the reconciler is keyed on that Secret.

Activation exists to count cluster activations against the licence's limit. It records Kubernetes events on the licence Secret when the server rejects an activation as over its limit, or when heartbeats have failed past a seven-day grace period. It never disables a feature. Activation state is kept in the `arena-license-activation` ConfigMap in the install namespace. A failed activation emits one warning event and then retries with growing delays, so air-gapped clusters are unaffected and need no offline procedure.

The cluster fingerprint sent to the server is the first 16 bytes (32 hex characters) of a SHA-256 hash over the kube-system namespace UID and the install namespace UID; the kube-system UID alone is not the fingerprint.

## Trade-offs

Fail open: every ambiguity resolves toward "entitled": an unreachable licence endpoint, a missing endpoint URL, an expired licence, a failed heartbeat. The cost is that nothing technical stops an unlicensed production Enterprise install, and the banner and the licence terms carry the weight. The benefit is that a control-plane outage, a lapsed renewal or a lost Secret cannot take a running agent platform down. For a product that runs customer-facing agents, that was the right side to err on.

One validator: several components need the answer, and each could have parsed the licence itself. Giving every service its own validator would mean several sources of truth and several `--dev-mode` flags to keep in step, so only arena-controller verifies the signature and the rest ask it. The price is a dependency: Enterprise services need arena-controller to be reachable at startup, which is why the fallback is "entitled".

Losing the Secret is quiet: because a missing licence is dev mode and dev mode works, a deleted `arena-license` Secret looks like a healthy cluster that has stopped being licensed. Back the Secret up with the rest of your recovery material.

Capacity is the one thing that blocks. A capability outside the licence is a warning, like every other licence problem, because refusing it would take away something the install can already do. A worker-replica count over the licence's limit is refused, because it asks for more of something than the licence sells.

## Downgrading and evaluating

Setting `enterprise.enabled=false` removes arena-controller and runs the operator without `--enterprise`. Removing the licence from the chart values while leaving `enterprise.enabled=true` downgrades nothing: that install is dev mode with every feature on.

To evaluate before buying, author your prompts as a PromptPack, build and run the agent against PromptKit, and evaluate it with the PromptArena CLI. The same pack deploys to Omnia unchanged. For Omnia itself, run it in dev mode.

## Related pages

- [Install with a License](/how-to/operations/install-license/) covers enabling Enterprise, installing, updating and backing up the licence.
- [Arena Fleet](/explanation/evaluation/arena-fleet/) describes the testing capabilities the licence gates.
- [White-label the Dashboard](/how-to/operations/white-label-the-dashboard/) covers the branding entitlement.