Skip to main content

Licensing

One plugin registers the license and the others share it. There is a self-serve 30-day trial, and nothing contacts a Lucenia server at any point — verification is a local signature check, and the trial record lives in your own cluster state.

What is gated, and what never is​

The gate is deliberately asymmetric. Committing to something requires a license; unwinding it never does.

   licensed only                      always allowed
───────────── ──────────────
create an autoscale policy delete a policy
evaluate capacity cancel a drain
start a drain renew a lease
acquire a drain lease release a lease
scale out / start scale-in continue a scale-in already under way
plan a rolling restart finish a rolling restart already under way
create an accelerated index OPEN an accelerated index
create a content pipeline RUN a content pipeline

The two capitalised rows are the important ones, and they are enforcements Octane deliberately does not make:

  • Opening an accelerated index is never gated. A codec is recorded in every segment. Refusing to open would make your existing data unreachable the moment a license lapsed — an outage caused by billing, which is not an enforcement mechanism.
  • Running a content pipeline is never gated. An ingest pipeline sits in front of indexing. A processor that refused documents on a lapsed license would not degrade a feature; it would stop the cluster accepting writes for every document routed through it.

The same principle covers the unwind operations. A lapsed license must never leave you with a drained node nothing will reclaim, an allocation exclusion nobody can clear, or a half-finished rolling restart with allocation still clamped. Those are the states that turn a billing problem into an incident, so they work in every license state.

What a license entitles you to​

Beyond its end date, a license carries four terms, and the signature covers all of them — editing any one of them in the file invalidates it:

FieldMeaning
featuresWhich products are unlocked: accelerator, content, autoscale, or * for all.
max_data_nodesThe most data nodes the license covers. 0 means no ceiling.
cluster_uuidsThe clusters the license may be installed on. Empty means any cluster.
max_primary_store_bytes_per_data_nodeHow much primary store each data node's share of the ceiling allows. 0 means no volume ceiling.

Data nodes rather than nodes, because that is the number that tracks what a cluster costs to run. Dedicated cluster-manager and coordinating nodes are the cluster's own plumbing, and billing them would charge you for overhead you added on our advice.

You are warned before the volume ceiling, not after it​

The cluster ceiling is max_primary_store_bytes_per_data_node × your data-node count, so adding a node adds its allowance. Three things happen as you approach it:

your primary storewhat happens
under 85% of the ceilingnothing; the figure is reported on GET /_lucenia/license
85% of the ceiling and abovewarned in the log and on the status endpoint. Nothing is refused.
past the ceiling plus its headroomcreating accelerated indices and content pipelines is refused

The headroom past the ceiling is deliberately small — 256 GiB, or a fifth of the ceiling for very small licenses. It is not extra capacity you have bought: it exists because the cluster measures its own store on a schedule, so there is a short window in which you can be over and nothing yet knows, and being refused for that would be unfair.

The warning is the part that matters. Nothing in Octane contacts Lucenia, so nobody here learns you are approaching your ceiling — you do, from your own logs and your own status endpoint, with enough room left to decide whether to buy nodes, raise the volume term, or delete data. How much notice that gives depends on how fast you are growing: a cluster adding 1 TB a day to a 100 TiB ceiling sees the warning about a fortnight out.

Writes, reads, opening an index and unwinding work already begun are never refused at any volume.

A license names the cluster it is for​

cluster_uuids lists the clusters a license may run on. Installing it anywhere else is refused, and so is using it anywhere else — a license that arrived by some route other than the install endpoint, such as inside a restored snapshot, is refused by the gate instead.

Find your cluster's UUID; this is the value checkout asks for:

curl -s localhost:9200/ | grep cluster_uuid

Read it off the terminal and type it into checkout. Disconnected is the normal case, not a special one: the UUID is a short string, so the machine that buys a license never has to be the machine that runs the cluster, or even be on the same network. Buy on a laptop, carry the file in.

The cluster UUID is the anchor because the cluster generates it at bootstrap and no setting can forge it — unlike the cluster name, which is configuration anyone can retype. The check itself is still entirely local: a field inside the signed file is compared against the UUID the node already holds in its own cluster state. Nothing is fetched and nothing is reported.

One license can name several clusters — production, staging and DR arrive in one file.

If you rebuild a cluster, its UUID changes. Restoring a snapshot into a new cluster produces a new UUID, and the license that travelled in that snapshot no longer matches it. What that costs is bounded: every index, pipeline and codec already in the restored cluster keeps working, because the gate covers creating accelerated indices and content pipelines and never opening or reading them. What stops is creating new ones, until the license names the new cluster. Reissue it from your Lucenia account. There is no grace period, for the same reason there is none on expiry — and a cliff met during a disaster recovery is the worst possible time to find it. Rebind before you need to.

Entitlements narrow the gate above; they never widen it. A cluster that has outgrown its node count, or that bought one product and not another, is refused the committing operations and keeps every unwind. That asymmetry is what makes a node ceiling safe to enforce at all: without it, adding a fourth node to a three-node license would leave you unable to drain it back out, and the licensing code would have made the cluster unfixable by the one action that fixes it.

format_version​

A license states the format it was signed in. Files issued before entitlements existed carry no format_version, load as version 1, and are treated as unlimited — narrowing a license somebody already bought is the one thing a licensing change must never do. Version 2 added the entitlements; version 3 added cluster_uuids, and a v1 or v2 license names no cluster and so runs on any.

Each version signs a different set of bytes, and each says its own version inside them. That is what stops a license being re-presented as an earlier format: delete the binding from a v3 file and call it a v2, and the signature simply stops verifying rather than handing over a license that runs everywhere.

A file that carries entitlements but no format_version is refused rather than guessed at. The two available guesses differ by the whole product: read as version 1 it would ignore the limits, so a license reading "3 data nodes" would be enforced as unlimited. For the same reason a v1 or v2 file carrying cluster_uuids is refused: that binding would sit outside the bytes those versions sign, so it would be a restriction nobody signed — one anybody could add or remove with a text editor.

Start a trial​

curl -XPOST 'localhost:9200/_lucenia/license/_start_trial?issued_to=acme-corp'
{
"status": "active",
"license": {
"format_version": 2,
"uid": "6f1c9e2a-...",
"type": "trial",
"issued_to": "acme-corp",
"issued_at": "2026-09-14T10:15:00.000Z",
"issued_at_millis": 1789380900000,
"expires_at": "2026-10-14T10:15:00.000Z",
"expires_at_millis": 1791972900000,
"max_data_nodes": 0,
"features": ["*"]
},
"expires_in_days": 30,
"trial_started": true,
"trial_available": false
}

A trial is deliberately unlimited on every axis — nodes, products and volume alike. A trial that covered fewer nodes, fewer products or less data than the thing it exists to demonstrate would have you evaluating a product nobody sells.

The limits that make a trial a trial are elsewhere: thirty days, once per cluster. "Once" means once ever — a cluster whose trial has expired has still used it, and a second request fails rather than renewing.

The trial is written through the cluster-manager's state-update thread, which serialises it: two simultaneous requests cannot both win.

A trial travels with a snapshot​

The license lives in cluster state and so is written into snapshots alongside it. Restoring a snapshot onto a different cluster carries the trial record with it. That is intended — it is what stops a trial being renewed indefinitely by snapshotting a cluster and restoring it somewhere new.

What expiry actually looks like​

Existing indices keep working. Existing pipelines keep running. Work in flight completes. The autoscale controller keeps evaluating and keeps telling you what it would do — it simply stops doing it, and says so at warning level rather than as an error, because nothing is broken.

What stops is starting something new: a new accelerated index, a new content pipeline, a new scaling action. That is the whole of the enforcement.

Install a license​

curl -XPUT localhost:9200/_lucenia/license \
-H 'Content-Type: application/json' \
--data-binary @license.json

The signature is verified locally against a public key compiled into the plugin. A license that fails verification is rejected outright; there is no degraded mode.

A license issued for a different cluster is refused here too, with 400 and both UUIDs named:

HTTP 400 Bad Request

this license is issued for cluster [kGxRLHMTQ2y...] and this cluster is
[7bQpVnMhSKa...]. Reissue it for this cluster from your Lucenia account.

The binding is checked after the signature, never before: a binding is only worth reading once we know Lucenia wrote it. Refusing at install rather than at the first create is the point — someone who installs the wrong cluster's file finds out immediately, not the next time they build an index.

Check status​

curl -s localhost:9200/_lucenia/license
statusMeaning
activeLicensed. All operations permitted.
expiredWas licensed. Committing operations refused; unwinds still work.
noneNo license and no trial has been started.

The response also carries trial_started and trial_available, and — whenever the status is not active — a next_step field naming the exact call to make. An operator usually reaches this endpoint because something was just refused, so the answer to "now what" is in the response rather than only here.

signature_verified​

status does not answer whether Lucenia actually signed the license you have. A license that has been altered is still installed and still unexpired, so it reports active — honestly, because that is what active has always meant. But the gate refuses every committing operation on it. Without a second field you would read active while being refused, with two answers from one product and no way to reconcile them.

signature_verified is reported on every response rather than only when it fails. An absent field is indistinguishable from a build too old to check, and "no news" is the wrong default for a security property. When it is false, a signature_note names the cause and the remedy: reinstall the license exactly as it was issued to you with PUT /_lucenia/license. The remedy is a reinstall rather than a purchase, because the license you bought is not the problem — the copy on the cluster is.

A trial reports signature_verified: true. Trials carry no signature by design: nothing outside the cluster issued one, and what keeps a trial honest is the once-per-cluster record instead. Reporting every trial as unverified would be false, and alarming in exactly the population least equipped to tell it was a false alarm.

What a refusal looks like​

A gated operation on an unlicensed cluster fails with a message that names the operation and tells you what to do:

HTTP 403 Forbidden

Cannot create an accelerated index: this cluster has no license.
Start a free trial with POST /_lucenia/license/_start_trial,
or install a license with PUT /_lucenia/license.

The message names the operation, why it was refused (has no license or has expired), and the remedy — and the remedy changes depending on whether a trial is still available, so it never suggests a trial to a cluster that has already used one.

An entitlement refusal says which entitlement, and with the actual numbers:

HTTP 403 Forbidden

Cannot create an accelerated index: the license on this cluster covers
[content] and does not include [accelerator].
Install a license that covers it with PUT /_lucenia/license.
HTTP 403 Forbidden

Cannot create a content ingest pipeline: the license on this cluster covers
2 data nodes and this cluster has 5.
Install a license that covers it with PUT /_lucenia/license.

A license that belongs to another cluster says so, rather than blaming a missing product — that is the root cause, and reporting anything else would send you to buy something you already own:

HTTP 403 Forbidden

Cannot create an accelerated index: this license is issued for cluster
[kGxRLHMTQ2y...] and this cluster is [7bQpVnMhSKa...].
Install a license that covers it with PUT /_lucenia/license.

You reach this one rather than the 400 above when the license arrived without passing through the install endpoint — a restored snapshot carries cluster state, and cluster state carries the license.

Neither offers a trial, even on a cluster that has never used one. The license is active; a 30-day trial would be a downgrade rather than a remedy, and suggesting one in answer to "your license does not cover this" reads as though the product had not noticed you were a customer.

The controller licenses separately​

The autoscale controller runs outside the cluster, so it cannot read cluster state to find a license. It verifies a license file at startup:

octane:
license_path: /etc/octane/license.json
license_issued_to: acme-corp

Without one it starts in recommendation mode and logs what it would have done. The trial record remains cluster-side in a single document with a fixed id, so a trial started through the REST API is the same trial — you cannot get sixty days by running a controller as well. The cluster UUID is recorded on that document for support rather than used as its key, and the distinction is the whole point: keying on it is exactly what would let a snapshot restored into a new cluster mint a second trial.

The controller checks the license's cluster binding at startup, alongside the signature, and refuses to start on a cluster the license does not name. A license that does not apply is a configuration mistake, and a controller that starts happily and then refuses to act at the one moment it is needed has hidden that mistake until it costs something.

The controller enforces the same entitlements as the plugin: a license must name autoscale, and the managed cluster must be within max_data_nodes and its volume ceiling, or scaling out, starting a scale-in, and planning a rolling restart are refused. Everything that finishes or undoes work already begun keeps working, so a cluster that has grown past either ceiling can still be drained back down to it — the alternative would be a license refusing the only action that returns the cluster to a licensed size.

It reads the volume figure the cluster itself publishes rather than measuring independently, so the controller and the nodes can never disagree about whether you are over.

Only data nodes count. A dedicated cluster-manager node does not consume a licensed slot.

A trial is the exception, and deliberately so: it is a document on the managed cluster rather than a signed file, so there are no entitlements to read, and it is treated as unlimited on every axis. Reading that absence as "entitled to nothing" would refuse every trial the product offers.

Why one plugin owns the registration​

OpenSearch will not start a node where two plugins register the same named writeable or the same REST route. The license model, the gate and the service are all shareable, but the registration is exclusive — so it lives in octane-license, and the accelerator and content plugins declare it as an extended plugin. That makes "exactly one registration" structural rather than a convention someone has to remember, and it is why the install order matters.