Licensing
The free core needs no licence key at all: it is AGPL-3.0 software that is fully functional on its own, and nothing about it asks a server for permission.
Pro modules are different. They are gated by an online check against the Nyxtrace licence server, and this page says exactly what that means for an appliance in your basement.
The key is an identifier, not a permission
Section titled “The key is an identifier, not a permission”A licence key carries no rights. It is a short identifier that says which subscription this installation belongs to; the entitlements — which modules, which limits, until when — live on the server and are looked up there.
A self-contained key that grants rights by itself was the other option, and it was rejected for three concrete reasons: it cannot be revoked, it cannot be moved between installations without handing the old one a permanent copy, and it cannot carry the part of Pro that is only worth paying for because a server keeps it current.
Entitlement tokens
Section titled “Entitlement tokens”The appliance exchanges its key for a short-lived entitlement token:
| Property | Value |
|---|---|
| Signature | Ed25519, by the licence server |
| Time to live | 6 hours |
| Renewal | Every 1–2 hours, in the background |
| Bound to | The installation identity (install_id) — the seat |
| Transport | HTTPS to the licence server, with certificate pinning |
The token is what the Pro modules actually check, and the public signing key is compiled into the Pro binaries. A renewal that succeeds simply replaces the token; nothing in the interface moves.
Revocation takes effect at the next renewal. Cancel a subscription, move a seat, or reset an installation in the portal, and the affected appliance stops being entitled within one renewal interval — an hour or two, not a month, and without anyone having to reinstall anything.
Seats are bound per installation. The identity is the random installation identifier described under instance registration — a value the appliance writes once for itself. Restore the installation from a backup and the seat comes with it, because the identifier is part of the backup.
No hardware fingerprinting. No MAC address, no disk serial, no CPU ID, no machine ID is read to lock a licence to a machine. There is nothing to break when you move the appliance to a bigger box.
When the licence server is unreachable
Section titled “When the licence server is unreachable”The governing rule: never break what already works.
| State | Behaviour |
|---|---|
| Entitled | Pro modules active |
| Server unreachable, within 72 hours | Everything still active, with a visible warning and the remaining time |
| Server unreachable, past 72 hours | Pro modules go read-only: existing data, incidents and reports stay visible and exportable. New Pro evaluations and response execution rest. |
| Revoked or expired subscription | The same read-only state, at the next renewal |
| No key configured | Pro modules do not start and report the exact reason. The free core runs unchanged. |
Seventy-two hours is an operator decision rather than a law of nature: it is long enough to cover a weekend outage on our side and a router replacement on yours, and it is documented so nobody has to guess.
Never, under any circumstances: data deletion, an ingest stop, incident loss, a crash, a start-up failure, or a silent behaviour change. The core keeps running throughout — ingest, storage, detection, incidents, search and export do not depend on the licence check at all. A licence problem must never blind your network.
And one specific guarantee: a lapsed entitlement may not leave an existing block hanging. The expiry mechanism in the response module is independent of the licence, so a containment that is supposed to lapse lapses.
What the internet connection is actually for
Section titled “What the internet connection is actually for”Pro is not a switch that unlocks code you already downloaded. A good part of what it sells only exists on the server side and only makes sense for an activated installation:
- Threat-intelligence feeds, kept current centrally.
- Model packs, published and versioned by us.
- The AI analyst quota, which costs real money per request.
- Signed release manifests for the Pro modules, so an appliance can tell a genuine module build from something else.
That is why the check is online: the thing being paid for is partly a service, not only a binary.
What the appliance sends
Section titled “What the appliance sends”The licence check and instance registration share one identity and one channel. The full field list, what is never sent, and the switch are documented under instance registration and telemetry. On the pro plan registration is required, because it is the seat binding: there is no state “Pro active, but not registered”.
One gate, not many
Section titled “One gate, not many”Each Pro module asks once, at startup, and registers itself in the core’s
capability registry only on success. There are no if licensed checks scattered
through the code; a renewal that fails withdraws the capability at the boundary
instead of leaving half-working features behind.
The consequence you can see: the interface presents an unregistered module as not provisioned — exactly the way it already presents a source that is not configured. Not an upsell banner in the middle of a workflow, and not a button that fails when you press it.
GET /api/v1/licenseReturns the tier, features, limits, status, the time the current entitlement
expires and the reason for the current state. The status is valid, grace,
expired, missing or invalid. The subject name is not disclosed to
non-administrators.
Extensions
Section titled “Extensions”An extension is a separate process speaking a versioned JSON protocol over standard input and output — not a shared library and not a compiled plugin. Each one ships a manifest that the same strict YAML loader validates:
extension: id: example-source version: 1.2.0 api_version: 1 # protocol version, not product version kind: source # source | model | action | sink requires_feature: connectors.example # empty means a free extension exec: /opt/nyxtrace/ext/example-source emits: [example.client, example.device] config_schema: {} # generates the operator form secrets: [username, password] health_interval: 60s rate_limit: { requests_per_minute: 30, burst: 5 }Four verbs, one bounded line of JSON each way:
| Verb | Purpose |
|---|---|
describe |
Confirm the manifest, report capabilities and protocol version |
test |
The test-connection button. Must not write. |
collect |
One bounded batch from a cursor |
act |
Exactly one approved action, with a readback. Action kind only. |
The trust boundary is narrow on purpose. An extension receives only its own configuration and its own secrets, through environment variables — never a tenant key, never a database connection, never database credentials. Its output is untrusted data: the core assigns tenant, sensor identity and trust class from the registry. An extension cannot grant an approval, change a threshold or invoke the executor.
It is also sandboxable by ordinary means, as a service unit with no new privileges, a strict system protection profile, its own user and its own network permissions.
Detection models stay declarative: a “model extension” is a package of YAML models plus a description, not a process. No foreign code executes in the detection path.
Third-party licences
Section titled “Third-party licences”The bundle ships third-party components — the database, the packet source, the transport, the flow collector, Grafana, the dashboard’s design system — each under its own licence, with a third-party licence inventory in the release. Attribution requirements, including the CC-BY attribution for DB-IP Lite, are yours to keep visible where you publish results.