Commercial
License keys
Issuing keys, domain binding, expiry, and what happens when a key is missing.
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: "team", "whitelabel", "oem"
features: string[]; // "whitelabel", "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. It is
asynchronous because the signature check goes through WebCrypto, but you do not have to
await it: the synchronous checks — format, product, expiry, domain — run first and the state
is pending while the signature is verified, and features are granted during that window.
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 or pending license granting it
onLicenseChange((s) => console.log(s.state));
clearLicense(); // mostly for tests
state |
Meaning | Effect |
|---|---|---|
unlicensed |
No key has been set. | The ORTEX mark shows; one console warning. |
pending |
Payload accepted, signature still being checked. | Features are granted; the chart draws normally. |
valid |
Everything checked out. | Features per the payload. |
invalid |
Malformed, wrong product, or a signature that failed. | Treated as unlicensed. |
expired |
exp is in the past. |
Treated as unlicensed. |
domain |
The current hostname is not covered. | Treated as unlicensed. |
verified is a separate boolean from state. It is true only once the Ed25519 signature
has actually been checked. A browser whose WebCrypto lacks Ed25519 accepts the payload with
state: "valid" and verified: false, because breaking a paying customer's chart over a
missing platform primitive is worse than a missed check.
In every one of these states the chart works. Nothing is disabled, no data is degraded, and nothing appears on the user's screen except the mark. See the ORTEX mark.
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 by default. Override it for tests:
await setLicenseKey(key, { hostname: "app.example.com", now: Date.parse("2027-01-01") });
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 mark 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 and the mark comes back. Nothing else
changes. There is no countdown, no degradation and no interruption.
Under the White-label plan the last version released during the term keeps working with its key indefinitely, which is the perpetual fallback customers expect from this kind of agreement.
Features
| Feature | What it grants |
|---|---|
whitelabel |
branding.visible = false is honored, and the mark may be replaced with your own. |
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. |
Being precise about this: only whitelabel changes 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
Community key and a White-label key run the same code with the same capabilities; the mark
is the difference.
Getting a Key
- Community, Team and White-label 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. Community follows the same path without the payment step.
- 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. A key that has to be killed before its expiry is handled by
publishing a revocation list in a release rather than by a runtime call to ORTEX, which
would mean every customer page depending on ORTEX uptime. The practical implication for a
customer is that revocation takes effect on the next library update — and for a hosted kit,
that the kit simply stops being served.
Testing Without a Key
The library is fully functional unlicensed, so evaluation, unit tests and continuous
integration need nothing. localhost is always allowed, so local development with a real
key never fails a domain check either.
For tests that need a specific license state:
import { clearLicense, setLicenseKey, licenseStatus } from "@ortex-charts/financial";
beforeEach(() => clearLicense());
it("hides the mark under a white-label key", async () => {
await setLicenseKey(TEST_KEY, { publicKey: TEST_PUBLIC_KEY, hostname: "example.com", now: FIXED_TIME });
expect(licenseStatus().state).toBe("valid");
});
publicKey overrides the embedded key so a test can sign with its own pair, and now and
hostname make expiry and domain behavior deterministic.
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.