Every CLI archive downloaded from the customer portal can include a file named
codecharter.license. It is a signed JWS token that ties the download to the
customer who triggered it. Without a valid license the CLI refuses to start and
exits with exit code 6.
Downloading with or without a license
On the Downloads page you will find two buttons per asset:
- Download with license delivers the archive with your personal
codecharter.licensealready embedded. This is the fastest option and is sufficient as long as the same account owns both the download and the CI workflows. - Without license delivers the generic, customer-agnostic archive without an embedded license. Useful if you mount the license separately from a secret store in your CI/CD pipeline, maintain an internal mirror, or want to decouple the build deterministically from a customer identity.
Both variants require an active subscription. The only difference is whether the license is included in the archive; the CLI itself is identical.
Where the CLI looks for the license
The CLI checks the following paths in order and uses the first file it finds:
- The path passed via
--license <path>. - The environment variable
CODECHARTER_LICENSE. codecharter.licensenext to the CLI binary.codecharter.licensein the current working directory.$XDG_CONFIG_HOME/codecharter/codecharter.license(Linux/macOS).%APPDATA%\CodeCharter\codecharter.license(Windows).$HOME/.config/codecharter/codecharter.license(fallback for Unix systems withoutXDG_CONFIG_HOME).
The simplest option: copy codecharter.license from the downloaded archive next to
the CLI binary. All invocations will then work without any additional configuration.
Automatic renewal in CI (API key)
In CI you usually do not download a license at all. Instead you store your portal
API key as a CI secret (CODECHARTER_API_KEY). On startup the CLI mints a
short-lived license (24h by default) from that key and caches it in the per-user
config directory (path 5/6/7 above), renewing it automatically as it nears expiry.
There is no daemon and nothing to schedule: a fresh runner fetches one license, a
long-lived runner refreshes only when the cached one drops below one hour of
remaining validity.
This keeps the API key as the only long-lived secret. A leaked cached license expires within 24h on its own, and revoking the key in the portal disables every license derived from it within the same window — no key rotation required.
Renewal authenticates with the API key only. The existing license file is never
sent back to the portal, so a leaked codecharter.license cannot be used to obtain a
fresh one.
If the portal is briefly unreachable while a still-valid license is cached, the CLI keeps using it until it actually expires (fail-open) and prints a warning, so a transient network blip does not break the build.
Two overrides are always respected and never touched by renewal: a path passed via
--license and the CODECHARTER_LICENSE environment variable. Use those to pin a
specific file; the CLI only ever writes to the managed cache location.
Offline or long-lived runners
A runner without access to codecharter.tools cannot renew. Give it a
full codecharter.license from the portal instead (download it, then place it on one
of the search paths or point CODECHARTER_LICENSE at it). Because a full license
carries a far-off expiry, it stays well above the refresh threshold and the API-key
path is never entered. The CLI decides purely on remaining validity, not on how the
license was obtained.
What is validated
At startup of every gated subcommand (analyze, validate, init, lsp) the
CLI performs the following checks:
- The file can be read.
- The content is a valid JWS token with
alg: RS256. - The signature matches one of the public keys embedded in the binary. During a key rotation the binary knows several keys simultaneously.
- The
expclaim (expiry date) is in the future (five-minute tolerance for clock drift). - Optional
min_ver/max_verclaims do not exclude the running CLI version.
If any of these checks fails, the CLI exits with exit code 6 and writes a short diagnostic to stderr.
Common error messages
No codecharter.license file found
None of the search paths contained a license file. Re-download the current archive
from the portal and copy codecharter.license next to the CLI binary, or set
CODECHARTER_LICENSE to the path of the file.
License at '<path>' is not a valid JWS
The file was found but is not a valid JWS structure. Possible causes are an accidentally overwritten file, line-ending conversion by a version control system, or a file from a very old portal version. Re-download the license from the portal.
License at '<path>' is not signed by a key this CLI trusts
The token is syntactically correct but signed by a key this CLI does not recognize. Typically occurs when there is a mismatch between a license from an older portal generation and a newer CLI. Solution: pull the current CLI version from the portal.
License expired on <date>
The token's signature is fine but the expiry date from the subscription has passed. Renew the subscription in the portal and then re-download the archive.
License requires CLI >= <version> / License only covers CLI <= <version>
The license was issued with a version bracket that does not include this CLI.
Only occurs when the portal is configured with Portal__License__DefaultMinVersion
or DefaultMaxVersion. Solution: regenerate the license in the portal, or use a
CLI version within the bracket.
Privacy
Validation runs completely offline. There is no phone-home; the CLI does not need network access to validate the license. The signature is sufficient.
The token contains: user ID (UUID), account email, plan name, issuance date, and expiry date. These values are obvious to the license holder anyway; they are not a secret but the anchor of the license itself.