Commercial
License keys
Issuing keys, domain binding, expiry, and what happens when a key is missing.
Read this page as Markdown, or take the whole documentation in one file.
What a Key Is
A license key is a string in three dot-separated parts:
OC1.<base64url payload>.<base64url signature>
The payload is JSON. The signature is Ed25519 over the payload string, made with ORTEX's
private signing key, whose public half is embedded in @ortex-charts/core. Keys cannot be
forged without that private key, which never leaves ORTEX's secret store.
interface LicensePayload {
id: string; // unique key id, for support and revocation
customer: string;
product: string; // always "ortex-charts"
tier: string; // informational: "community", "pro", "business", "oem"
features: string[]; // "whitelabel", "brand", "financial", "viz", "drawings", or "*"
domains: string[]; // hostnames; "*.example.com" matches subdomains; "*" matches anything
iat: number; // issued at, Unix seconds
exp: number; // expiry, Unix seconds
}
Installing One
Call setLicenseKey once, as early as you can, before any chart is created. Every check,
the signature included, runs before the call returns, so the state is settled by the next
line; the promise it returns is for the optional revocation list, and awaiting it is never
required. The signature is verified by the library's own Ed25519 implementation rather than
by the browser's WebCrypto, so nothing a page can replace or remove takes part in the check.
import { setLicenseKey } from "@ortex-charts/financial";
const status = await setLicenseKey(process.env.NEXT_PUBLIC_ORTEX_CHARTS_KEY!);
console.log(status.state, status.verified, status.message);
On the hosted kit this is already done: the kit file ends with a
setLicenseKey call carrying your key, so nothing in your page has to know about it.
Where to Put the Key
The key is a public artifact. It ships to the browser, it is bound to your domains, and anyone can read it out of your bundle. Treat it like a publishable API key rather than a secret: an environment variable at build time is convenient and correct, and a secret store is unnecessary.
void setLicenseKey(import.meta.env.VITE_ORTEX_CHARTS_KEY); // Vite
void setLicenseKey(process.env.NEXT_PUBLIC_ORTEX_CHARTS_KEY!); // Next.js
What is secret is the registry token in your .npmrc, which grants access to the
package downloads. That one belongs in continuous-integration secrets and never in a commit.
The States
import { licenseStatus, onLicenseChange, hasFeature, clearLicense } from "@ortex-charts/financial";
licenseStatus(); // { state, payload, message, verified }
hasFeature("whitelabel"); // true only on a valid license granting it
onLicenseChange((s) => console.log(s.state));
clearLicense(); // mostly for tests
state |
Meaning | Effect |
|---|---|---|
unlicensed |
No key has been set. | Gated: on localhost the chart draws with the ORTEX mark; on any other host it draws the licence notice instead of its content. |
pending |
Reserved; no longer occurs, since the signature is checked before setLicenseKey returns. |
Treated as not valid. |
valid |
Everything checked out, signature included. | Features per the payload. |
invalid |
Malformed, wrong product, or a signature that failed. | Gated, as unlicensed. |
expired |
exp is in the past. |
Gated, as unlicensed. |
domain |
The current hostname is not covered. | Gated, as unlicensed. |
revoked |
The key id is on a revocation list. | Gated, as unlicensed. |
verified reports that the Ed25519 signature was checked and passed. Since the check is the
library's own and runs on every platform, valid and verified: true now always go
together; there is no browser on which a payload is trusted unchecked.
The Gate
Since 1.1.0 the licence is a gate. licenseGate() answers { blocked, reason } for the
current state and hostname, and every chart asks it before it paints a frame:
- On a development host (
localhost,127.0.0.1,::1, any*.localhost, or no hostname at all as in tests and server-side rendering) the gate is never closed. An unlicensed chart draws in full with the ORTEX mark on, so evaluation, unit tests and continuous integration need no key. The console stays quiet unless a chart asks to hide the mark, which writes one warning. - On any other host a chart in any state but
validpaints a notice on its own background, "ORTEX Charts. A licence key is required to display this chart", with the reason the check gave and the address of /pricing, and nothing else: no series, axes, legends, crosshair or tooltips. The moment a key verifies, every chart on the page repaints itself in full.
The gate decides from the key's payload, not from a remembered answer: on every frame it checks the expiry against the clock and the domains against the hostname of the moment, and it requires the signature to have been verified. A key that expires while the page is open closes the gate at that second, and a key installed on one host does not carry to another.
import { licenseGate } from "@ortex-charts/financial";
licenseGate(); // { blocked: false, reason: "" } on localhost
licenseGate("app.example.com"); // { blocked: true, reason: "No licence key set" } with no key
Treemaps, heatmaps and the other @ortex-charts/viz components paint the same notice.
Sparklines in @ortex-charts/lite are not gated.
Domains
domains is a list of hostnames the key covers.
- An exact hostname matches itself:
app.example.com. - A wildcard matches the base and every subdomain:
*.example.comcoversexample.com,app.example.comanda.b.example.com. *matches anything, and is what an OEM key usually carries.localhostis always allowed, along with127.0.0.1,::1and any*.localhosthost, on every key. Development never fails a domain check.
import { domainAllowed } from "@ortex-charts/financial";
domainAllowed("app.example.com", ["*.example.com"]); // true
domainAllowed("example.net", ["*.example.com"]); // false
The check runs against location.hostname, at install and again on every frame, and there
is no way to tell the library it is somewhere else.
Preview deployments are the case that catches people out. A branch preview on
pr-482.vercel.app is not covered by *.example.com, so add the preview host as a wildcard
domain when the key is issued, or accept the licence notice on previews.
Two Layers of Domain Enforcement
Hosted-kit customers are checked twice. The CloudFront function in front of the CDN compares
the request Origin or Referer against the domains registered on the kit and refuses to
serve the file to an unlisted host, and the key inside the file checks the hostname again in
the browser. Registry customers get the second check only.
Expiry and Renewal
Keys are issued for the subscription term with a 14-day grace period added on top, so a renewal that is a few days late never flips a customer's chart to the mark. A renewal re-signs onto the same key id and the same kit id, so your script tag and your key id do not change from year to year — only the key string does, and hosted-kit customers do not even see that.
When a key does expire, the state becomes expired, the gate closes and the chart paints the
licence notice instead of its content, naming the reason. There is no countdown and no
degradation before that date. The right to use the library ends with the term on every plan;
renewing issues a fresh key with no change to your code.
Features
| Feature | What it grants |
|---|---|
whitelabel |
branding.visible = false is honored: the mark may be hidden. Every paid plan. |
brand |
The mark may be replaced with your own logo, text, caption and link. OEM agreements. |
financial |
Informational; the financial package is not gated. |
viz |
Informational; the viz package is not gated. |
drawings |
Informational; drawing tools are not gated. |
* |
Everything, including whitelabel and brand. |
Being precise about this: only whitelabel and brand change behavior today. The others
are recorded on the key so that the plan a customer is on is visible in the payload, and so
that a future gate has a name to use, but no feature check in the library disables a package.
A Pro key and an OEM key run the same code with the same capabilities; the mark is the
difference. What a key of any plan does is open the gate.
Getting a Key
- Pro and Business keys are issued automatically. A purchase on ortexcharts.com signs a key, builds a hosted kit, registers your domains, issues a registry token and emails all of it within seconds of payment. There is no free key: evaluate on localhost and in the playground, then buy with the sixty-day guarantee.
- OEM and enterprise keys are issued by hand from the same signer after a conversation.
Changing the domains on a key means re-issuing it, which happens automatically when the domains on the subscription change; the kit is rebuilt at the same URL.
Revocation
Every payload carries an id, and ORTEX can revoke a key before its expiry. What happens to
a running implementation depends on how the library reached it:
- A hosted kit stops being served within seconds: the kit's domains are removed from the edge store, so the next page load gets a 403 rather than the script. A page that already has the script loaded keeps its chart until reload; on reload there is no chart.
- A registry install that passes
revocationListreads a small JSON document from the ORTEX CDN once after the key verifies. A revoked id found there drops the licence torevoked: the gate closes and the chart paints the licence notice instead of its content. The document is cached for a few minutes, so that is the delay. - A registry install that does not keeps working until the key expires or the id ships
in the library's built-in
REVOKED_KEY_IDSin a later release, because the licence check makes no network call unless asked to.
setLicenseKey(key, { revocationList: "https://cdn.ortexcharts.com/v1/revoked.json" });
The list can only revoke, never grant, so an unreachable or malformed list changes nothing;
a chart never goes dark because ORTEX was unreachable. Hosted kits set the option
themselves. Whichever route, revocation and cancellation also revoke the registry token, so
npm install stops as well, and the licence itself ends at that moment: Section 7 of the
licence applies.
Testing Without a Key
On a development host the gate never closes, so evaluation on localhost, unit tests in
jsdom and continuous integration need no key: the chart draws in full with the ORTEX mark
on. localhost is always allowed, so local development with a real key never fails a domain
check either. The first real hostname is where a key is needed.
For tests that need a specific license state, install your real key and read the status
back; clearLicense returns the page to unlicensed:
import { clearLicense, setLicenseKey, licenseStatus } from "@ortex-charts/financial";
beforeEach(() => clearLicense());
it("hides the mark under a white-label key", async () => {
await setLicenseKey(KEY);
expect(licenseStatus().state).toBe("valid");
});
setLicenseKey takes no publicKey, hostname or now overrides. Earlier versions
accepted them for tests, and the first of them let a page trust a signing key of its own,
so they were withdrawn in 1.2.0. A key is verified against ORTEX's embedded signing key,
on the hostname the page is served from, at the time it is installed and again on every
frame.
Related
- The ORTEX mark — what showing and hiding it actually means.
- Installation and delivery — kits, registry tokens and source access.
- Questions we are asked — expiry, telemetry and support.