Skip to content

License File

How the CodeCharter CLI locates and validates its license file, and which error messages can occur.

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.license already 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:

  1. The path passed via --license <path>.
  2. The environment variable CODECHARTER_LICENSE.
  3. codecharter.license next to the CLI binary.
  4. codecharter.license in the current working directory.
  5. $XDG_CONFIG_HOME/codecharter/codecharter.license (Linux/macOS).
  6. %APPDATA%\CodeCharter\codecharter.license (Windows).
  7. $HOME/.config/codecharter/codecharter.license (fallback for Unix systems without XDG_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 exp claim (expiry date) is in the future (five-minute tolerance for clock drift).
  • Optional min_ver/max_ver claims 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.