Licensing and Entitlements
This page covers how ThreatWeaver licensing works, what each license tier includes, how to activate and manage your license, and answers to common questions from administrators and sales teams.
How ThreatWeaver Licensing Worksβ
ThreatWeaver uses a named-seat, module-gated licensing model. Every license encodes:
- Seat limit β the maximum number of active user accounts allowed in the tenant
- Allowed modules β the specific product modules the tenant is licensed to access
- Expiration date β when the license must be renewed
- Deployment type β single-tenant or multi-tenant (SaaS), and if multi-tenant, the maximum number of schemas and allowed schema names
Licenses are issued as JWT tokens signed with RS256 (RSA 2048-bit, SHA-256). The ThreatWeaver application verifies the signature against a built-in public key at startup and periodically every four hours. No license data is stored in plaintext β the JWT is persisted to the database and to an AES-256-GCM encrypted disk file, both machine-bound to the deployment.
License State Machineβ
At any given moment, a ThreatWeaver deployment is in one of five states:
| State | Meaning | User Impact |
|---|---|---|
| ACTIVE | License valid, within seat limit, phone-home current | Full access |
| GRACE | License expired but within 7-day grace window | Read-only: GET requests pass, mutations blocked |
| LOCKED | Grace period exhausted, or phone-home overdue | All API calls return 423 β no access |
| REVOKED | TLM has explicitly revoked this license | All API calls return 423 β no recovery path (contact support) |
| UNLICENSED | No license key found at all | Setup wizard shown β activate to proceed |
The transition logic is computed on every request and re-evaluated fully every four hours by a background job.
License Typesβ
ThreatWeaver issues four license types:
Trialβ
- Duration: 14 days
- Seats: Up to 5 users
- Modules: All modules enabled (full feature evaluation)
- Phone-home: Required every 72 hours
- Notes: Automatically converts to LOCKED when the trial period ends. No grace period extension is available for trial licenses. Contact sales to convert to a paid license.
Professionalβ
- Duration: 1 or 3 years (negotiated)
- Seats: 10β100 users (in blocks of 10, based on purchase)
- Modules: Core modules: Exposure Management, Vulnerability Management dashboard, AppSec Scanner, Compliance Reporting, AI Features
- Phone-home: Required every 72 hours (3 days)
- Notes: Cloud Security, Identity Security, and AI Security modules are available as add-ons
Enterpriseβ
- Duration: 1, 2, or 3 years
- Seats: 100βunlimited (maxUsers: 0 in the JWT means unlimited)
- Modules: All modules enabled (wildcard:
*in allowedModules) - Phone-home: Required every 72 hours
- Notes: On-premises deployments can request offline/air-gapped operation. Multi-tenant mode (SaaS reseller) requires Enterprise tier.
Internalβ
- Duration: Typically 1 year
- Seats: Unlimited
- Modules: All modules enabled
- Phone-home: Extended deadline of 168 hours (7 days) β for BluCypher internal deployments
- Notes: Internal licenses are distinguished by
type: "internal"in the JWT claims. They follow the same verification path but get longer checkin deadlines.
Modules and License Tiersβ
The following table shows which modules are enabled per license tier. Module access is enforced at the API route level via the requireModule() middleware β requests to disabled module endpoints return HTTP 403 with MODULE_DISABLED.
| Module | Module ID | Trial | Professional | Enterprise | Add-on |
|---|---|---|---|---|---|
| Exposure Management | vulnerability_dashboard | Yes | Yes | Yes | β |
| AppSec Scanner | appsec | Yes | Yes | Yes | β |
| Compliance Reporting | compliance_reporting | Yes | Yes | Yes | β |
| Scanner Management | scanner_management | Yes | Yes | Yes | β |
| AI Features | ai_features | Yes | Yes | Yes | β |
| Connectors | connectors | Yes | Yes | Yes | β |
| Fix Planner | fix_planner | Yes | Yes | Yes | β |
| Anomaly / Intelligence | intelligence | Yes | Yes | Yes | β |
| Cloud Security | cloud_security | Yes | No | Yes | Professional |
| Identity Security | identity_security | Yes | No | Yes | Professional |
| AI Security | ai_security | Yes | No | Yes | Professional |
Some older license JWTs use short-form module IDs. The license service handles these automatically: cloud maps to cloud_security, identity maps to identity_security, ai-security maps to ai_security, ai maps to ai_features.
When a module is disabled, the following happens:
- Sidebar navigation items for that module are hidden in the frontend
- All API routes for that module return
403 MODULE_DISABLED - Module-specific roles cannot be assigned (the
LICENSE_ROLE_MAPfilters available roles) - Existing data created while the module was active remains readable if the module is re-enabled
Seat Countingβ
What Counts as a Seatβ
ThreatWeaver uses named-user seat counting β each active user account consumes one seat, regardless of how many concurrent sessions that user has open.
- Active user accounts count against the seat limit
- Deactivated/suspended accounts do not count
- Service accounts used for API integrations count as seats
- Pending invites (unaccepted) do not count until the user accepts and their account becomes active
Seat Enforcementβ
When a new user is created or reactivated, the system checks the current active user count against maxUsers from the license JWT. If the deployment is at or over its limit, user creation is blocked.
Seat grace period: If the seat limit is exceeded (e.g., due to a bulk import or license downgrade), the deployment enters a 14-day seat grace period. During this window:
- No new users can be created beyond the limit
- Existing over-limit users retain access
- Administrators see a persistent warning banner
- After 14 days, the deployment transitions to LOCKED if the over-limit condition is not resolved
To resolve an over-limit condition:
- Deactivate unused accounts (Admin β User Management β deactivate users)
- Or purchase additional seats (see How to Request a License Upgrade)
Checking Current Seat Usageβ
Navigate to Admin β Settings β License to see:
- Current active user count vs. licensed maximum
- Which users are counted as active seats
- Days remaining before seat grace expires (if applicable)
Viewing Current License Status in the UIβ
Admin β Settings β License Panelβ
The license status panel is accessible to admin-role users at Admin β Settings β License.
It displays:
| Field | Description |
|---|---|
| Status | Current state: ACTIVE, GRACE, LOCKED, REVOKED, or UNLICENSED |
| License type | internal or customer |
| Expiration date | When the JWT expires |
| Days remaining | Countdown to expiration (negative = in grace) |
| Seat usage | Active users / licensed maximum |
| Allowed modules | List of enabled module IDs |
| Deployment ID | Unique identifier for this licensed deployment |
| Last phone-home | Timestamp of most recent successful check-in |
| Next phone-home deadline | When the next check-in is required |
| JTI | JWT ID (unique identifier for this specific license token) |
Warning Bannersβ
ThreatWeaver surfaces license warnings at the top of the application:
- Expiring soon (30 days or less): Yellow banner with days remaining
- Grace period: Orange banner with "read-only mode" notice
- Seat limit warning: Yellow banner when within 10% of seat limit
- Seat grace: Orange banner with countdown
License Activationβ
First-Time Activationβ
When ThreatWeaver starts without a license, it enters UNLICENSED mode and presents the setup wizard. To activate:
Option A β UI activation (recommended):
- Navigate to the setup wizard (shown automatically on first start)
- Paste your license key JWT into the license key field
- Click Activate β the system validates the JWT signature, stores it to the database, and transitions to ACTIVE
Option B β Environment variable (for automated deployments):
Set the LICENSE_KEY environment variable before starting the backend:
LICENSE_KEY="eyJhbGciOiJSUzI1NiIsImtpZCI6InYxIn0..." npm start
The backend reads this on startup, validates it, stores it to the database and encrypted disk file, then removes reliance on the env var for future restarts. The env var approach is suitable for initial Docker/Kubernetes deployments.
Option C β API endpoint:
curl -X POST https://your-deployment/api/license/activate \
-H "Content-Type: application/json" \
-d '{"licenseKey": "eyJhbGciOiJSUzI1NiIsImtpZCI6InYxIn0..."}'
This endpoint is available without authentication during the UNLICENSED state.
Importing a Renewed Licenseβ
When your license expires and you receive a renewal JWT:
- Go to Admin β Settings β License
- Click Import New License
- Paste the new JWT
- Click Import β the new license takes effect immediately without a restart
The old license is replaced in both the database and the encrypted disk file. A KEY_IMPORTED event is written to the license audit log.
License Key Storageβ
Once activated, the license key is persisted in two places (both survive restarts):
- Database β
license_configtable,id = 'default'row - Encrypted disk file β
./data/.license(AES-256-GCM, machine-bound encryption key derived from the host's machine identifier)
If the database is unavailable at startup, the disk file is used as a fallback. The database always takes priority over the disk file.
Phone-Home Validationβ
ThreatWeaver performs periodic check-ins to the ThreatWeaver License Manager (TLM) at https://license.threatweaver.ai. This serves two purposes:
- License validity confirmation β TLM can mark a license as revoked even before its JWT expiry date (e.g., for non-payment or policy violation)
- Key registry refresh β TLM can push updated public keys to support zero-downtime key rotation
Check-In Deadlinesβ
| License type | Check-in required every |
|---|---|
| Customer (Professional/Enterprise) | 72 hours (3 days) |
| Internal | 168 hours (7 days) |
If a check-in is overdue by more than 24 hours beyond the deadline, the deployment transitions to GRACE (read-only). If it remains overdue, it eventually transitions to LOCKED.
Air-Gapped / Offline Deploymentsβ
Deployments without outbound internet access can disable phone-home enforcement by not setting the LICENSE_CHECKIN_URL environment variable. When this variable is absent, the check-in deadline is not enforced.
Air-gapped operation is available for Enterprise license customers. Contact your account manager to enable it for your deployment.
What Happens When a License Expiresβ
Grace Period (Days 1β7 after expiry)β
- All read operations (GET, HEAD, OPTIONS) continue normally
- Write operations (POST, PUT, PATCH, DELETE) return
403 LICENSE_GRACE - Users can still view dashboards, download reports, and export data
- A persistent orange banner appears with the number of grace days remaining
- Administrators receive email notifications (if notification channels are configured)
After Grace Period (Day 8+)β
- The deployment transitions to LOCKED
- All API requests return
423 LICENSE_LOCKED - The UI shows a "License Locked" page with a link to
/settings/license - No data is deleted β all data is preserved and accessible immediately upon license renewal
Recoveryβ
Import a valid renewed license via the UI or API (see License Activation). The deployment transitions back to ACTIVE immediately without a restart.
Multi-Tenant Licensing (SaaS Mode)β
In SaaS deployments (DEPLOYMENT_MODE=saas), the license additionally controls tenancy:
| JWT Claim | Purpose |
|---|---|
tenancyMode | "single" or "multi" β single-tenant or multi-tenant operation |
maxSchemas | Maximum number of tenant schemas allowed |
allowedSchemas | Explicit allowlist of schema names (optional) |
schemaPrefix | Required prefix for all tenant schema names |
Per-Tenant Licensesβ
In multi-tenant mode, each tenant gets its own license managed through TLM. When a new tenant is provisioned, TLM issues a tenant-scoped license JWT that is stored in that tenant's schema. The platform-level license (the deployment license) controls how many tenants can be created.
Organization-Wide Licensesβ
Enterprise customers operating their own on-premises multi-tenant deployment (acting as a reseller or internal platform team) can request a single organization-wide license with maxSchemas set to their required tenant count and tenancyMode: "multi".
How to Request a License Upgrade or Add Seatsβ
For Existing Customersβ
- Contact your account manager at BluCypher
- Provide your current Deployment ID (visible in Admin β Settings β License)
- Specify the number of additional seats needed or the modules to add
- TLM issues a new license JWT, which you import via Admin β Settings β License β Import New License
For New Purchasesβ
Contact sales at sales@blucypher.com or through the ThreatWeaver website. Provide:
- Estimated user count
- Required modules
- Deployment model (SaaS managed or on-premises)
- Compliance requirements (air-gapped, data residency, etc.)
Self-Service (SaaS)β
SaaS customers on the BluCypher-managed platform can add seats and modules directly from the billing portal (accessible from Admin β Settings β Billing). License updates applied through the billing portal take effect within minutes via TLM push.
License Key Formatβ
ThreatWeaver license keys are RS256-signed JWTs (JSON Web Tokens). The JWT is structured as:
Header.Payload.Signature
Headerβ
{
"alg": "RS256",
"kid": "v1",
"typ": "JWT"
}
The kid (key ID) field identifies which public key version was used to sign the JWT. This enables zero-downtime key rotation β old licenses signed with v1 remain valid until v1 is explicitly revoked.
Payload (Claims)β
{
"type": "customer",
"deploymentId": "deploy_abc123xyz",
"jti": "lic_2026_pro_acme_001",
"iat": 1743811200,
"exp": 1775347200,
"allowedModules": [
"vulnerability_dashboard",
"appsec",
"scanner_management",
"compliance_reporting",
"ai_features",
"connectors",
"fix_planner",
"intelligence"
],
"maxUsers": 50,
"tenancyMode": "single",
"maxSchemas": 1,
"allowedSchemas": ["public"],
"schemaPrefix": null
}
| Claim | Type | Description |
|---|---|---|
type | string | "internal" or "customer" β determines phone-home deadline length |
deploymentId | string | Unique identifier for this licensed deployment |
jti | string | JWT ID β unique per-license-issuance, used in audit trail |
iat | Unix timestamp | License issue time |
exp | Unix timestamp | License expiration time |
allowedModules | string[] | Module IDs enabled for this license. ["*"] = all modules |
maxUsers | number | Maximum active users. 0 = unlimited |
tenancyMode | string | "single" or "multi" |
maxSchemas | number | Maximum tenant schemas (multi-tenant only) |
allowedSchemas | string[] | Explicit allowlist of schema names (optional) |
schemaPrefix | string|null | Required prefix for tenant schemas (optional) |
Signatureβ
The signature is created by TLM using its RSA 2048-bit private key. ThreatWeaver validates it using the embedded public key. The private key never leaves TLM. Customers receive only the signed JWT β it is not possible to modify claims without breaking the signature.
Audit Trailβ
All license lifecycle events are written to the license event log (license_events table) and visible in Admin β Settings β License β View Audit Log.
Events Loggedβ
| Event | When it fires |
|---|---|
KEY_IMPORTED | Admin imports a new license key |
KEY_IMPORT_FAILED | Import fails (signature invalid, claims error, etc.) |
CHECKIN_SUCCESS | Successful phone-home to TLM |
CHECKIN_FAILED | Phone-home attempt failed |
LOCKOUT_TRIGGERED | System transitioned to LOCKED or REVOKED |
STATE_TRANSITION | License state changed (e.g., ACTIVE β GRACE) |
Each event includes a timestamp, the event type, a human-readable message, and (where applicable) the license claims at the time of the event. Events are retained indefinitely and can be exported for SIEM ingestion.
Common Licensing Questions (FAQ)β
Q: Can I share one seat between two users if they never log in simultaneously?
No. Seats are named-user, not concurrent. Each active user account consumes one seat regardless of when or how often they log in.
Q: What happens to my data if my license expires and I don't renew?
No data is deleted. The deployment enters read-only mode (GRACE) for 7 days, then LOCKED. Data is fully preserved and accessible immediately upon importing a renewed license key.
Q: Can I move my license to a different server?
The license JWT can be imported on any server. However, the AES-256-GCM encrypted disk file is machine-bound β it cannot be copied to a different host. The database-stored license (primary storage) has no machine binding and is portable.
Q: We have 52 active users but only 50 seats. What happens?
The deployment immediately enters a 14-day seat grace period. A warning banner appears for all admins. During this window, no new users can be added, but the 52 existing users retain access. After 14 days, the deployment transitions to LOCKED. Resolve by deactivating 2 users or purchasing 2 additional seats.
Q: Our deployment is air-gapped. Can we use ThreatWeaver?
Yes. Enterprise licenses support air-gapped operation. Do not set the LICENSE_CHECKIN_URL environment variable. Phone-home enforcement is only active when that variable is set. Your license must still be renewed before the JWT expiry date β contact your account manager for a renewed license JWT before expiry.
Q: Can I see what modules a license includes before importing it?
Yes. The license JWT is a base64url-encoded JSON payload. Decode the second segment to inspect the claims:
# Decode the payload section of the JWT (middle segment between the dots)
echo "eyJ0eXBlIjoiY3VzdG9tZXIiLC4uLn0" | base64 -d
Alternatively, paste the JWT into jwt.io to view the decoded claims without exposing the key to a third party β jwt.io decodes locally in your browser.
Q: What is a Deployment ID and where do I find it?
The Deployment ID (deploymentId claim in the license JWT) is a unique identifier assigned by TLM when a license is issued. It is visible in Admin β Settings β License. Include it when contacting support or requesting a license upgrade so the support team can locate your license record in TLM.
Q: We are renewing. Do we need to restart the backend after importing the new license?
No. License import takes effect immediately in-memory. No restart is required.
Q: A module is listed in the license but I cannot see it in the UI. Why?
Module visibility depends on both the license AND feature flags. Some modules require a feature flag to be explicitly enabled in Admin β Settings β Feature Flags (for example, new beta modules). Check the feature flags panel if a licensed module is not appearing.
Q: Can we have different module access for different teams within one tenant?
Module access is tenant-wide, not per-user. However, individual users' access within a module is controlled by RBAC roles and permissions. For example, you can license the AppSec module for the whole tenant but restrict which users can view or manage AppSec findings using role assignments.
Q: How long does it take for a new seat purchase to take effect?
For SaaS customers on the managed platform, updated licenses are pushed by TLM and take effect within minutes. For on-premises customers, your account manager will issue a new license JWT which you import manually β this takes effect immediately upon import.
Q: The license shows "LOCKED" β what should I do?
- Confirm the license expiry date in Admin β Settings β License
- If expired: contact your account manager for a renewal JWT, then import it
- If not expired but locked due to phone-home overdue: check network connectivity from the backend server to
https://license.threatweaver.aion port 443 - If revoked: contact support β revocation is irreversible (a new license must be issued)
Q: What is the grace period for seats vs. license expiry?
These are two separate grace periods:
- License expiry grace: 7 days of read-only operation after the JWT
exptimestamp - Seat grace: 14 days where over-limit users retain access but no new users can be added
Both are configurable via TLM for specific customers who require different terms.