codecharter login signs a single machine in to the portal using your normal
browser session. It is the convenient way to license a developer workstation:
no API key to copy around, no license file to download by hand. The machine
receives a device token that can do exactly one thing, renew a short-lived
license for your account, and that you can revoke at any time from the portal.
For CI and other headless environments an API key remains the right credential. Which one applies where is covered under Credentials and precedence.
Signing in
Run the command on the machine you want to sign in:
codecharter login
The CLI starts a sign-in with the portal and opens your default browser on the verification page. It also prints the page and the code, so an SSH session or a machine without a desktop works just as well:
Your browser has been opened to complete the sign-in.
https://codecharter.tools/activate
Code: 7QRX-4KTB
If no browser could be opened, the first line reads "Open this page in a browser to complete the sign-in:" instead, and you open the printed page yourself, from any device. The browser you confirm in does not have to be on the machine that is signing in.
The code is eight characters in two groups of four. It only uses digits 2-9
and capital letters without I and O, so there is nothing to confuse when you
read it off one screen and type it into another. It is valid for ten minutes;
after that the sign-in has to be started again.
While you confirm, the CLI keeps checking with the portal and prints
Waiting for you to confirm the code....
Confirming in the browser
The verification page is /activate and requires you to be signed in to the
portal. If you are not, you land on the normal login page first and are taken
back afterwards. When the browser was opened by the CLI the code is already
filled in; you always get to see it and have to confirm it explicitly.
The page shows two things before you decide:
- Device, how the machine identifies itself. The CLI sends
CodeCharter CLI on <machine name>. - Code, so you can compare it with what your terminal printed.
Compare both. If a code was sent to you by someone else, do not approve it: approving grants that machine the right to renew a license for your account until you revoke it. Choose Approve to complete the sign-in, or Reject to refuse it.
After confirming
Once you approve, the CLI stores the credentials and finishes:
Signed in. The device token and license are stored on this machine.
The command exits with code 0. Every gated command now works on this machine
without further configuration, and the license is renewed automatically from the
stored device token as it nears expiry.
If you reject the sign-in, the CLI prints Sign-in was denied in the browser.
and exits with code 1. If the ten minutes run out, it prints The code expired before it was confirmed. Run 'codecharter login' again. and also exits with
code 1. If your subscription lapsed between approving and the next poll, it
prints Your account has no active subscription. Visit the portal to start a trial or subscribe. and stops there rather than waiting the code out. Subscribe
in the portal, then run codecharter login again — it issues a fresh code for
you to approve.
What is stored, and where
Two files are written into your per-user config directory, the same location the license cache already uses:
| File | Contents |
|---|---|
codecharter.device-token |
The device token for this machine |
codecharter.license |
The short-lived license issued along with it |
The directory is $XDG_CONFIG_HOME/codecharter on Linux and macOS,
%APPDATA%\CodeCharter on Windows, with $HOME/.config/codecharter as the
fallback on Unix systems without XDG_CONFIG_HOME. On Unix the token file is
created with owner-only permissions, so it is never briefly readable by other
accounts on the machine.
If the CLI cannot determine a per-user config directory, it reports "signed in,
but no per-user config directory was found to store the credentials" and exits
with code 1. Nothing is stored in that case, so simply run the command again
once one of the directories above is available.
What a device token can do
A device token starts with cgd_ and has exactly one permission: minting a
short-lived license for your account. That is the whole scope.
- It cannot read or change your rules, profiles, or drafts.
- It cannot download CLI archives or publish checks.
- Every portal endpoint other than license renewal rejects it with 403 Forbidden, regardless of what your account is otherwise allowed to do.
This is the practical difference to an API key, which carries read:rules and
optionally write:rules. A leaked device token cannot be used to read your rule
content, and it cannot be used to push anything.
The token itself has no expiry date. It stays valid until you revoke it in the portal, which is why the portal lists every signed-in machine for you, see Authorized devices.
Which account a machine is signed in as
A machine is signed in as whoever confirms the code in the browser, and the license it renews is that account's. That account needs an active subscription. If it has none, the approval page says so instead of accepting the confirmation, and your code stays valid — start a trial or subscribe, then approve the same code, as long as the ten minutes have not run out. Rejecting a device you do not recognize works whatever your subscription says.
So browser sign-in covers your own machines, not your colleagues'. The portal uses a single-account model, so for a team the license file remains the way to license everybody else, see Rolling out CodeCharter to your team. Signing a machine in does not consume or assign a seat; seats are counted per developer, not per device entry.
Checking the current state
codecharter auth status reports which credential this machine would
authenticate with, without ever printing the credential itself:
Portal: https://codecharter.tools
Credential: device token (/home/you/.config/codecharter/codecharter.device-token)
License: valid, plan Team, expires 2026-08-01 09:14:22Z
The Credential line reads API key from CODECHARTER_API_KEY when the
environment variable is set, device token (<path>) when this machine is signed
in, and none - run 'codecharter login', or set CODECHARTER_API_KEY when
neither is available.
Because neither the key nor the token is printed, the output is safe to paste
into a support request. Add --json for a machine-readable object with the
fields portal_url, credential (api_key, device_token, or none),
device_token_path, and a license object carrying valid, plan,
expires_at, and message. The command is not license-gated and always exits
with code 0.
Signing out
codecharter logout
This removes the stored device token from this machine:
Signed out. The stored device token was removed from this machine.
The cached license is left in place and will simply expire; it is no longer renewed from here.
Two things are worth being precise about here.
Logout is local. It removes the token from this machine but does not invalidate it in the portal. To lock a machine out for good, for example because you no longer have access to it, revoke the device in the portal instead, see Authorized devices.
The cached license is deliberately kept. It may have come from an API key or from an installer rather than from a browser sign-in, so deleting it would break setups that logout never touched. It is short-lived and, with the token gone, nothing renews it any more.
If no token was stored, the command reports "Not signed in on this machine;
there was no stored device token to remove." Either way it exits with code 0,
so you can run it in a cleanup script without special-casing.
Options
| Option | Command | Effect |
|---|---|---|
--portal-url <url> |
login |
Sign in to a different portal instead of the configured one |
--json |
login |
Emit the sign-in as newline-delimited JSON events |
--json |
auth status |
Emit machine-readable JSON instead of text |
--license <path> |
auth status |
Inspect a specific license file instead of the standard search path |
login, logout, and auth status are not license-gated. They are how a
machine obtains a license in the first place, so requiring one would be
circular.
Without --portal-url, login uses the CODECHARTER_PORTAL_URL environment
variable if it is set, and the configured portal otherwise. Only https URLs
are accepted, plus http to loopback for local development.
Scripting the sign-in
codecharter login --json writes one JSON object per line to standard output so
an editor extension or script can follow the sign-in without parsing prose:
{"event":"code","verification_uri":"https://codecharter.tools/activate","user_code":"7QRX-4KTB"}
{"event":"status","status":"pending"}
{"event":"status","status":"success"}
The status vocabulary is pending, success, denied, expired, and
subscription_required. The last one ends the sign-in the moment the portal
reports it, rather than letting the code run out. Failures are written to
standard error as {"event":"error","message":"..."}, so the event stream on
standard output stays clean.
Troubleshooting
The browser opens but the code is not accepted
Codes are valid for ten minutes. If the terminal has been sitting open longer
than that, the code is gone and the page tells you so. Run codecharter login
again and use the new code.
"Your account has no active subscription"
Approving a device only mints a license if the approving account has an active subscription. Two places tell you when it does not, and neither of them costs you the code:
- In the browser. The approval page refuses the approval and explains why, with a link to your billing page. Nothing is recorded, so the code stays valid and the machine keeps waiting. Subscribe in another tab, come back, and approve the same code — the sign-in then finishes normally.
- On the waiting machine. If the subscription lapsed after you approved, the
CLI prints
Your account has no active subscription. Visit the portal to start a trial or subscribe.and exits with code1straight away, instead of waiting for the code to expire. Because the command has already stopped, runcodecharter loginagain after subscribing and approve the new code it shows.
Neither case spends your approval, so nothing is left half-done in the portal.
Many sign-in attempts in a row
The sign-in endpoints are rate-limited per IP address. If you script the command
in a loop, the portal answers 429 Too Many Requests with a Retry-After header
telling you how many seconds to wait.
The machine is a CI runner
Do not sign a runner in with codecharter login. Give it an API key as a
secret instead, see API keys in secrets. If a
runner happens to be a machine somebody once signed in on, the API key in the
environment takes precedence anyway, so the job never borrows a developer's
identity.