License File Format
When you run forge activate <key>, Forge validates the Ed25519-signed license key entirely offline, then writes the parsed license data to ~/.forge/license.json. The raw key string is never stored — only its SHA-256 hash and the decoded payload fields.
The file is written with 0600 permissions on Unix (owner-read/write only).
File Schema
Section titled “File Schema”{ "license": { "v": 1, "tier": "solo", "seats": 1, "features": [ "dashboard", "plugins", "semantic_search", "workflow_tools" ], "issued": "2026-04-15T10:00:00Z", "expires": "2027-04-15T00:00:00Z", "trial": false, "machine_bindings": [], "team_id": null, "is_team_admin": false }, "key_hash": "a3f1b2c4d5e6...", "activated_at": "2026-04-15T10:30:00Z"}Top-Level Fields
Section titled “Top-Level Fields”| Field | Type | Description |
|---|---|---|
license | object | The decoded license payload from the signed key. See License Payload below. |
key_hash | string | SHA-256 hash of the raw license key string (hex-encoded, 64 characters). Used by the heartbeat client to identify the license server-side without transmitting the key itself. Value is "<unknown>" for licenses activated before v1.3.0. |
activated_at | string | ISO-8601 UTC timestamp of when forge activate last ran on this machine. Value is "unknown" for licenses migrated from the v1.2.0 bare format. |
License Payload Fields (license object)
Section titled “License Payload Fields (license object)”| Field | Type | Description |
|---|---|---|
v | integer | License schema version. Currently always 1. |
email | string | Email address associated with the purchase. Displayed masked in forge license output (e.g., p***[email protected]). |
tier | string | License tier: "community", "solo", "pro", or "team". |
seats | integer | Number of seats. Always 1 for Solo and Pro. Team licenses have the seat count at purchase. Team requires a minimum of 3 seats. |
features | array of strings | Feature flags granted by this license. See Feature Flags below. |
issued | string | ISO-8601 UTC timestamp when the key was signed by the Forge backend. |
expires | string | ISO-8601 UTC timestamp when the license expires. After this date, Forge falls back to Community Mode and displays a renewal prompt. |
trial | boolean | true for 14-day trial licenses. Trial licenses have full feature access for the selected tier. Defaults to false for licenses signed before v1.3.0. |
machine_bindings | array of strings | SHA-256 hashes of machine fingerprints this license is bound to. Empty means unbound (backward-compatible default). Populated by forge activate on each machine. |
team_id | string or null | Team subscription ID for Team tier licenses, in the format "team_<stripe_subscription_id>". null for Solo and Pro licenses. |
is_team_admin | boolean | true only for the first seat on a Team subscription (the admin who purchased). Grants access to the admin portal for seat management and team invite generation. |
Feature Flags
Section titled “Feature Flags”The features array contains string identifiers for capabilities granted by the license. Each tier is a strict superset of the tier below it.
| Feature Flag | Minimum Tier | Description |
|---|---|---|
dashboard | Solo | Local web dashboard (forge serve --dashboard). |
plugins | Solo | Custom health check plugins (.forge/plugins/*.yaml). |
semantic_search | Solo | Vector similarity search in forge_search. |
workflow_tools | Solo | forge_prepare, forge_validate, forge_understand. |
multi_repo | Pro | Index and query multiple repositories. |
ci_mode | Pro | forge ci command with CI annotation output. |
scip | Pro | SCIP index ingestion (forge ingest-scip, forge_ingest_scip). |
cross_repo_search | Pro | Search across multiple indexed repositories simultaneously. |
priority_updates | Pro | Priority update channel. |
shared_configs | Team | .forge/team.yml shared team configuration. |
volume_keys | Team | Volume license key management. |
admin_portal | Team (admin only) | Web admin portal for seat management and invite tokens. |
ci_cache | Team | R2-backed CI index caching (forge index --cache-to, forge ci --team). |
License Key Format
Section titled “License Key Format”The raw license key — the string you paste into forge activate — has this structure:
<base64(json_payload)>.<base64(ed25519_signature)>Both components use standard base64 (not URL-safe). The signature is an Ed25519 signature over the raw JSON payload bytes (not their base64 representation). The Forge binary has the matching public key compiled in and validates the signature entirely offline — no network call is made during activation.
Backward Compatibility
Section titled “Backward Compatibility”Forge handles three on-disk formats transparently:
- Current format (v1.3.0+): The
{ "license": {...}, "key_hash": "...", "activated_at": "..." }wrapper shown above. - Pre-v1.3.0 format: A bare
LicenseJSON object. Loaded and automatically treated as ifkey_hashis"<unknown>"andactivated_atis"unknown". The file is upgraded to the current format on the nextforge activate. - Licenses signed by v1.2.0: Keys that lack
trial,machine_bindings,team_id, andis_team_adminfields. These fields default tofalse,[],null, andfalserespectively via serde defaults — no re-activation is required.
Modifying the File
Section titled “Modifying the File”Do not edit ~/.forge/license.json by hand. The file contains a copy of the decoded license payload; it does not contain the signature. Altering the payload does not bypass validation because the signature is checked only at forge activate time, not at runtime. Feature gates throughout Forge check the features array from the loaded file, which is re-read from disk on each forge serve startup.
If your license file becomes corrupted, run forge activate <key> again with your original key.