Skip to content

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 valid paints 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.com covers example.com, app.example.com and a.b.example.com.
  • * matches anything, and is what an OEM key usually carries.
  • localhost is always allowed, along with 127.0.0.1, ::1 and any *.localhost host, 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 revocationList reads a small JSON document from the ORTEX CDN once after the key verifies. A revoked id found there drops the licence to revoked: 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_IDS in 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.