License Types¶
The Three License Models¶
| Type | Machine Limit | Enforcement | Typical Use Case |
|---|---|---|---|
| Per-machine | Fixed seat count | Each activation consumes one slot; moving requires deactivation | Solo artists, small studios |
| Floating | Concurrent cap | Sessions checked out/released; any machine can activate up to the cap | Render farms, shared workstations |
| Site | Unlimited | No seat enforcement; geo spread detection disabled | Enterprise, studio-wide deployments |
Per-machine¶
Each activation is permanently bound to a machine fingerprint. Activating a new machine when all slots are full requires an admin to revoke an existing activation.
- Machine fingerprint is embedded in the JWT
machineFingerprintclaim. - Token storage is obfuscated using the machine fingerprint as the key. Copying the cache file to a different machine renders it unreadable.
- Admins can view and revoke individual machine activations from the admin API.
Floating¶
Licenses are borrowed for the duration of a job or session. Any machine can activate, but only max_machines may hold active sessions simultaneously.
- Sessions expire after 15 minutes without a heartbeat (cleaned up by periodic job).
- Render managers should call
/deactivateon job completion to release the seat. - Activation is refused if the concurrent cap is reached.
Site¶
A single key unlocks unlimited machines. Intended for studios deploying across an entire facility.
- Geo-spread detection is disabled for site licenses. Machine churn detection still applies.
- Rate limiting on
/activatestill applies (15 attempts/hour per key). - JWT tokens are still issued per machine (audit trail).
Variants¶
Variants define the commercial tier of a product. Each product can have multiple variants targeting different buyer segments.
| Variant | Seats / Concurrency | Offline Days | Trial | Typical Price Point |
|---|---|---|---|---|
indie |
1-2 machines | 30 days | No | Low |
studio |
3-10 machines | 30 days | No | Mid |
render-farm |
Floating, up to 50 | Session-only | No | Per-node or flat |
site |
Unlimited | 30 days | No | Enterprise |
trial |
1 machine | 30 days (token) | Yes | Free |
Variant configuration
Variants are configured per-product via the admin API. Key fields:
maxMachines: seat or concurrency capdurationDays: license validity period (not token validity; token validity is determined by threat level)defaultTrialDays: trial length in days (used when creating trial licenses via discount codes)licenseType:per-machine,floating, orsite
Offline Behavior by Status¶
License status affects how long the SDK will work without an internet connection.
| License Status | Token Valid? | Max Offline Duration | Behavior |
|---|---|---|---|
active |
Yes | 30 days | Full functionality; auto-refresh within last 7 days |
degraded |
Yes (short) | 1 day | Nag dialog shown; tool still runs |
suspended |
No | 3 days (cached) | Nag dialog shown; reconnect required soon |
revoked |
No | 0 days | Hard block immediately on next validation |
Suspended licenses
A suspended license will still pass local JWT validation for up to 3 days if the machine has a cached token. Once online, the server will reject the token and force a re-sync.
Revoked licenses
Revoked licenses fail online validation immediately. The server checks license status in Firestore on every /validate call, so a revoked license is blocked even if the local JWT has not expired yet.
Token Auto-Refresh¶
The SDK handles refresh transparently when calling /validate online.
Token Age Action
----------- ----------------------------------------
0 - 23 days No refresh. Server validates and returns existing token.
23 - 30 days Refresh window. Server issues a new 30-day token and
returns it in the response. SDK stores it locally.
Expired Token has expired. User must go online to re-activate.
(Token validity varies by threat level: 30d / 1d / 3d.)
Post-grace Hard block. User must go online to re-activate.
Refresh is automatic
No code change needed in your plugin. The SDK's check_license() call handles token refresh internally. See the Python SDK docs for details.