Skip to main content

Licensing

A self-hosted license is a signed file. By default the gateway also checks in with Align once a day, to renew the license and keep the bill matched to use. The check-in sends four values and nothing else. See The daily check-in. An air-gapped install can use an offline license instead, which makes no network call.

Self-hosting is available on Growth and Enterprise.

At a glance​

  • Checks are off by default. Seat enforcement only runs if you set LICENSE_ENFORCEMENT_ENABLED=true. Nothing stops the gateway without a license.
  • The public key comes with your license. We send it at onboarding. Set it as ALIGN_LICENSE_PUBLIC_KEY through gateway.extraEnv.
  • When enforcement is on, it fails open. A missing, invalid or expired license is logged and not blocked.
  • Connectors are never gated. Every plan gets every integration.

How it works​

  • The license is a JWT signed with RSA-SHA256 (RSA-4096 keys).
  • The file is validated on your machine, against the public key.
  • After expiry there is a 14-day grace period. The status reports it. The app does not go read-only.

Get a license​

Email sales@align.tech for:

  1. A license.jwt file.
  2. The license public key.
  3. Your terms and expiry date.

Image access is separate. It is a grant to your AWS account ID, or an offline bundle. See Installation.

Install the license​

Create the secret:

kubectl create secret generic align-license \
--namespace align \
--from-file=license.jwt=./license.jwt

Add this to your values file. Paste the public key into ALIGN_LICENSE_PUBLIC_KEY.

license:
enabled: true
secretName: align-license
secretKey: license.jwt

gateway:
extraEnv:
- name: ALIGN_LICENSE_PUBLIC_KEY
value: |
-----BEGIN PUBLIC KEY-----
<the license public key>
-----END PUBLIC KEY-----

Apply it:

helm upgrade align oci://$ALIGN_REGISTRY/align/charts/align \
--version "$ALIGN_CHART_VERSION" \
--namespace align \
--values values.yaml

Check the gateway log:

kubectl logs -l app.kubernetes.io/component=gateway -n align | grep "License status at startup"

The line shows valid, plan, daysUntilExpiry and isExpired. It does not print the reason when valid is false.

What is in the file​

{
"iss": "https://align.tech",
"sub": "lic_abc123",
"aud": "align-self-hosted",
"iat": 1704067200,
"exp": 1798761600,
"nbf": 1704067200,
"customer": { "id": "cust_abc123", "name": "Acme Corp", "email": "admin@acme.com" },
"plan": "enterprise",
"limits": { "users": 500, "decisions": null, "connectors": [], "features": ["sso", "auditLog"] },
"deployment": { "type": "self-hosted" }
}
FieldMeaning
limits.usersSeat limit. null means unlimited.
limits.decisionsDecisions per month. null means unlimited.
limits.featuresFeatures the license turns on.

limits.connectors is carried in the file but never gates anything.

Seat enforcement​

If a valid license has a seat limit and the count reaches it, user provisioning returns 403 license_seat_limit. This needs LICENSE_ENFORCEMENT_ENABLED=true and the check wired to a route. It is not wired today.

Ambient capture has its own switch, ALIGN_AMBIENT_CAPTURE (see Configuration). It can turn capture off. It never grants a feature the license withholds.

GPU model serving​

gpu.llmServer is bring-your-own-model and is not gated by your license. Align does not ship a fine-tuned decision model image. The chart defaults to vllm/vllm-openai. Point it at any OpenAI-compatible model you run or mirror. See LLM setup.

Renew​

  1. Email sales@align.tech for a new license.jwt.
  2. Replace the secret:
kubectl create secret generic align-license \
--namespace align \
--from-file=license.jwt=./new-license.jwt \
--dry-run=client -o yaml | kubectl apply -f -
  1. Restart the gateway:
kubectl rollout restart deployment/align-gateway -n align

Plans​

PlanWho it is forNotes
GrowthTeams that want the shared, org-wide graph$25 USD per person covered per month, billed monthly, minimum 2 people. Hosted by Align, or self-hosted with a license.
EnterpriseTeams that need SSO, SCIM or other compliance controlsCustom pricing, billed annually. The license carries the seat count.

Both plans self-host with the same chart and the same license model. Both can mirror images to their own registry.

The daily check-in​

Status

The client is in the gateway. The licence service it calls, the admin portal that sets your numbers, and the final hostname are not live yet. Until they are, a check-in fails and shows a banner, and nothing else changes.

A connected install (the default) makes one call a day. The gateway sends this JSON, and exactly this:

{
"licence_id": "lic_...",
"install_id": "<random id made on this install>",
"align_version": "<the gateway build>",
"people_covered": 42
}

No names, no emails, no content, no telemetry. A test pins the four keys, so a fifth cannot be added without a failing build. The last attempt's exact JSON is stored in the database (licence_checkin_state), and the People page shows it, next to the exact JSON the next check-in will send.

  • It is signed. The request carries X-Align-Timestamp and X-Align-Signature. The signature is v1= plus the hex HMAC-SHA256 of <timestamp>.<raw body>, keyed with the install key inside your license (the install_key claim). Our service verifies it, so a count edited in transit fails, and a request more than 5 minutes old is refused. The key is in your own license file, so this is integrity on the wire. It is not a control against the customer's own count, which is why the offline report is chained and checked separately.
  • The reply renews the license. It is {"licence": "<jwt>"}. The gateway checks the RSA signature with your public key, checks it is the same license id, checks it is not older than the license in use, and only then adopts it. The renewal is kept in the database and re-adopted at the next start. The current starting number and ceiling travel inside that signed license (seats). When they change in our admin portal, the next check-in writes them to your seat settings. When they have not changed, your own ceiling on the People page is left alone.
  • If it fails, nothing breaks. The gateway retries after an hour and logs one warning a day, naming the host and the reason. Expected until our licence service is live: until then every check-in fails, so you will see that one warning a day and nothing else. Nothing refuses a request or locks any data, in any state. After more than 30 days without a successful check-in the install follows the offline rules below (a check-in that succeeds puts it back to connected). The gateway logs once a day that a usage report is due. The People page shows a banner when a check-in is failing, and again when an offline-rules install owes a usage report.
  • One hostname. The gateway calls the one address in license.checkin.url over HTTPS. The licence service is not live yet, so no host is published: when it is, the install guide names it and you allow exactly that host on your firewall. Until then the chart's default address does not resolve and every check-in fails, which the gateway logs once a day and never acts on. It goes through global.proxy like the other outbound calls.
  • It can be turned off. Set license.checkin.enabled: false (or LICENCE_CHECKIN_ENABLED=false). The gateway then makes no call. It counts as an install that cannot check in, so the offline rules apply from day 30, once those are built.
  • A license without a usable install key (base64, at least 16 bytes) makes no call. An older license file, or no license, behaves as before.

Offline license (Enterprise, air-gapped)​

Chosen in the order form (the licence carries checkin: "offline"). The gateway makes no network call at all.

Each quarter, once the quarter has ended, an org admin downloads the usage report from the People page backend (GET /admin/people/offline-report, self-host only) and sends it to Align. The report is the three months and their people-covered counts, with the licence id, install id and Align version, and nothing else. It is signed with the install key and hash-chained: each month's hash covers the month before it, and each quarter starts from the last hash of the quarter before. A quarter is built once and stored, so a second download is the same file.

Our side checks it with services/gateway/scripts/verify-offline-report.ts (the pure function is verifyOfflineReport). It rejects:

ReportVerdict
untouchedaccepted; prints the chain tip to keep
a number editedrejected (signature, and the chain even if re-signed)
a month missing, or from another quarterrejected
a quarter already accepted, or an older one (replay)rejected
a quarter skipped, or one that does not continue the accepted chainrejected
a first report for a later quarter than we expect, or from a different installrejected
a starting number or ceiling that differs from what we hold for the licencerejected (the report's own numbers are never trusted)

What this does and does not stop. The install key is in your own licence file. The chain stops editing a report after the fact, rewriting a quarter we already accepted, skipping a month or a quarter, and replaying an old report. It does not stop a customer who rebuilds and signs a whole new chain. Anything stronger is an audit right, not a signature.

The licence renews when we receive the report. If it is not received, the licence lapses to a banner and log lines. It never locks your data and never stops service. People-covered rows are kept 13 months, so the report cannot be built for a quarter older than that.

Status

The report builder, signer, verifier, the download route and the People page button (shown when the licence is offline) are built. Our licence service has code to accept a check-in, accept an offline report, renew the licence, and issue licences through an admin API and portal. It is not live: its host does not resolve yet and no image of it is built or released, so no install can reach it and nothing renews a licence yet.

Network​

The daily check-in above is the one call Align makes to a host we run. Everything else is yours: your LLM provider, your connectors and image registry. The check-in can be turned off (above), and an offline license makes none. A self-hosted Align reaches the network for:

  • the licence check-in host, unless you run an offline license or turn it off
  • image pulls, unless you mirror them or use the offline bundle
  • your LLM provider
  • OpenAI embeddings, for natural-language search
  • the tools your connectors read

See Network for the full list of hosts.

Troubleshooting​

If the startup line says valid: false, check these in order:

  1. ALIGN_LICENSE_PUBLIC_KEY is set on the gateway. Without it no license is valid.
  2. The secret exists and is mounted at /etc/align/license/license.jwt: kubectl get secret align-license -n align.
  3. The key matches the one that signed the file. If not, ask us for a new copy.
  4. The license has not expired. The 14-day grace period still applies.

Treat license files as sensitive. They carry customer details.

Support​

Email sales@align.tech, or contact your account representative.