Skip to main content

Octane Advisor

Free. No license. Runs on any OpenSearch 3.x. Reads your cluster and changes nothing.

Point it at a cluster and it writes a single self-contained HTML page describing what it found — shard shape, JVM pressure, mapping growth, security posture — with the numbers each finding was computed from and the exact change to make.

There is no trial period on the advisor, no key to request, and no expiry. Run it every week for years if it is useful. It is the one Octane artifact with no license check anywhere in it.


Run it​

One command. It needs JDK 21 and nothing else.

curl -fsSL https://getoctane.lucenia.io | sh

It asks which cluster to read, offering http://localhost:9200 as the default, and writes octane-advisory.html beside you. Answer the prompt, or skip it by passing the flags straight through:

curl -fsSL https://getoctane.lucenia.io | sh -s -- --url https://my-cluster:9200

The script downloads the advisor, verifies its SHA-256 against the published checksums and refuses to run if they do not match, unpacks it under ~/.cache/octane-advisor, and runs it. It needs no root, installs nothing on your PATH, and touches nothing outside that cache and the report it writes. Delete the cache directory and it is gone. A second run reuses the cached copy and starts in about a second.

With no terminal to ask — CI, a Dockerfile, anything scripted — it does not hang on a prompt nobody will see: it reads http://localhost:9200, says so, and carries on. Pass --url to choose.

If you would rather read it before running it — which is a reasonable thing to want from anything you pipe into a shell — curl -fsSL https://getoctane.lucenia.io prints it.

Or download it yourself​

unzip octane-advisor-1.1.0.0.zip
cd octane-advisor-1.1.0.0
./bin/octane-advisor --url http://localhost:9200

That writes octane-advisory.html in the current directory. Open it in a browser — or add --serve and it hands you a URL.

The report is interactive​

On a report with more than a handful of findings you get a filter bar:

  • Severity pills — jump to just the critical findings. Built from the severities actually present, with counts, so there is never a pill that filters to nothing.
  • Category dropdown — shards, resources, mappings, security, opportunities.
  • Text filter — type an index name to see only findings that mention it.
  • Collapse all — scan the titles, then open the ones that matter.
  • Copy on each remediation, because they are meant to be run.

All of it is inline in the single file. Nothing is fetched from the network — no CDN, no font, no analytics — so it behaves identically on an air-gapped jump host as it does on a laptop.

And it degrades: every finding is expanded and present in the markup, and the script only ever hides things in response to a click. With JavaScript disabled you get exactly the static report, which matters because a locked-down browser on a jump host is a realistic way to open this.

Run it against a real cluster​

# Authenticated, with the security plugin's demo certificates
./bin/octane-advisor \
--url https://opensearch.internal:9200 \
--username admin \
--password "$OPENSEARCH_PASSWORD" \
--insecure

# Write the report somewhere specific, and serve it on a URL
./bin/octane-advisor --url https://opensearch.internal:9200 \
--out /tmp/prod-advisory.html --serve

# Also emit JSON, so next month's run can be diffed against this one
./bin/octane-advisor --url http://localhost:9200 \
--out advisory.html --json advisory.json

Options​

OptionMeaning
--url <url>Cluster REST endpoint. Default http://localhost:9200
--username <user>Basic-auth user
--password <pass>Basic-auth password
--insecureAccept a self-signed certificate — needed for the security plugin's demo certs
--out <file>Where to write the HTML. Default octane-advisory.html
--json <file>Also write findings as JSON
--serveServe the report on http://127.0.0.1:8731/
--port <n>Port for --serve
--offlineNever fetch vulnerability data; use the copy inside the release
--cve-db <file>Use this vulnerability database instead of fetching one
--helpUsage

What it needs to be allowed to do​

Read-only. If you are running it as a restricted user rather than as admin, these are the endpoints it reads:

GET /                       GET /_cluster/health
GET /_nodes/stats/... GET /_nodes/plugins,settings
GET /_cluster/settings GET /_stats
GET /_cat/shards GET /_mapping
GET /_settings

An endpoint it cannot read costs you the findings that came from it and nothing else — the report lists what it could not examine under Not examined, so a permissions gap never looks like a clean bill of health.

What it will never do​

  • It never writes. Every request is a GET. There is one request method in the collector and it hardcodes the verb; a build gate (readOnlyGate) fails the build if a write verb ever appears in that file.
  • It never sends anything about your cluster, and never contacts Lucenia. No telemetry, no license check, no version check, no analytics. Running this tool tells us nothing — not even that you ran it. The report embeds its own CSS rather than loading a font or a stylesheet, so it renders identically on an air-gapped jump host.
  • It makes exactly one outbound request, and you can turn it off. The vulnerability section needs the published CVE list, so the advisor fetches it from nvd.nist.gov. That request carries a fixed query and nothing else — not your version, not your cluster name, not your node count — and the list is filtered against your cluster here on your machine. We deliberately do not ask NVD "which CVEs affect 2.4.1", because that would disclose your production version to a third party to save a few kilobytes. If the request fails, or you pass --offline, the copy inside the release is used instead and the report says which it was.
  • It never binds to a public interface. --serve listens on 127.0.0.1 only. The report is an inventory of your cluster — version, index names, whether it is authenticated — which is the first thing an attacker would want. Copy the file if you need it elsewhere.

What it checks​

CategoryExamples
VulnerabilitiesPublished CVEs affecting the OpenSearch version each node runs, and the installed plugins, matched against the affected-version ranges in the advisory itself
ShardsUnassigned primaries and replicas; oversharding; shards too large to relocate quickly; shard count against available heap
ResourcesTime spent in old-generation GC as a share of uptime; thread-pool rejections as a rate
MappingsIndices approaching or at index.mapping.total_fields.limit, counting multi-fields the way the limit counts them
SecurityWhether the cluster answers unauthenticated requests; credentials sent over plain HTTP
OpportunitiesWorkloads present at a volume where a Lucenia product measures faster

How the vulnerability check works, and what it will not claim​

The data is generated from the National Vulnerability Database — never hand-written — by scripts/generate-cve-database.py, which queries the CPE OpenSearch vulnerabilities are actually recorded under and keeps the affected-version ranges verbatim. Each finding carries the advisory's own words, its CVSS score, and a link to the advisory, so nothing has to be taken on trust.

Three things it is careful about:

  • Version ranges are plural. A fix normally lands on more than one release line — "before 1.3.7" and "2.0.0 up to 2.4.0" — so each advisory keeps every range separately. Collapsing them would either clear an affected cluster or condemn a patched one.
  • Every node, not just the cluster. The version at / is the cluster manager's. A cluster halfway through a rolling upgrade runs two, and the finding names the nodes that still carry the affected one.
  • A plugin CVE needs the plugin. Plugin advisories are only reported when that plugin is actually installed, and are matched against the version it reports.

It does not check your operating system, your JVM, your container base image, or any application in front of the cluster. It checks the OpenSearch distribution and the plugins the cluster has installed, which is what it can see.

Refreshing it yourself​

On a connected machine the advisor refreshes the list automatically on every run. For an air-gapped site, generate a current database somewhere with a route out and carry it in:

./scripts/generate-cve-database.py          # writes the database and prints what it classified
octane-advisor --offline --cve-db ./cve-database.json --url "$CLUSTER"

A file named with --cve-db is never silently replaced by a refresh. If it cannot be read, the advisor reports that rather than quietly analysing against different data than you asked for.

How the security check works, and why​

It does not read your configuration. It sends one request with no credentials and records whether the cluster answered.

That is deliberate, and it is the result of getting it wrong first. opensearch-security appears in _nodes/plugins even when it is disabled, and plugins.security.disabled appears in neither _cluster/settings nor _nodes/settings. A check built on either would report a wide-open cluster as secure. Asking the cluster how it behaves is the only thing that cannot be wrong about it.

Opportunities, and what they are allowed to say​

One category can mention something Lucenia sells. It is governed by rules enforced in tests:

  • It fires on measurement, never on possibility — the workload has to be present at meaningful volume, not merely mapped.
  • It never recommends the accelerator for geo_point, which measured slower.
  • It is always filed as INFO. Nothing we sell is ever presented as your cluster being broken.
  • Any performance figure is labelled as our benchmark on our data, never as a prediction about your cluster.

If your cluster runs none of those workloads, the report will not mention Lucenia products at all, and the purchase section is not rendered.

Tracking a cluster over time​

--json writes the same findings as data, with stable ordering, so two runs against an unchanged cluster produce identical bytes:

./bin/octane-advisor --url "$CLUSTER" --json "advisory-$(date +%F).json"
diff advisory-2026-09-01.json advisory-2026-10-01.json

The collection timestamp lives in cluster, not in the findings, so a diff shows what actually changed rather than that time passed.

{
"cluster": { "name": "prod", "version": "3.8.0", "data_nodes": 6, "serves_anonymous_requests": false },
"findings": [ { "severity": "warning", "category": "shards", "title": "...", "evidence": [ "..." ] } ],
"counts": { "critical": 0, "warning": 2, "info": 1 },
"not_examined": []
}

A note on what a clean report means​

If the advisor finds nothing, that means these checks found nothing — not that there is nothing to find. It examines cluster shape, not your queries, your data model or your application. Treat a clean report as one class of problem ruled out.

The vulnerability section is held to a stricter version of the same rule, because an empty security section is the one place a reader is most likely to hear more than was said. It therefore states, on every run including the clean ones, how many advisories it compared, where they came from and the day they were generated. If that data is older than 45 days — longer than an OpenSearch release cycle — a clean result is reported as a warning, not an all-clear, and says so in words: nothing published since the data was generated was checked, because it was not there to check.

If you want to go further​

The advisor tells you what is true about your cluster. Whether a Lucenia product helps is a different question, and the only honest answer is a measurement on your own data. A 30-day trial is issued by the cluster to itself, with no sales contact:

curl -XPOST "$CLUSTER/_lucenia/license/_start_trial?issued_to=your-org"

See Compatibility for which OpenSearch versions the plugins install on — the advisor itself runs on any 3.x, including versions the plugins do not support.