Manage Your App Licensing From the Terminal
Most licensing tools give you a dashboard and stop there. That is fine until the third time you click through four screens to check whether one customer’s key actually activated, or until you want to know how many devices are active across six apps and find yourself opening six tabs.
A dashboard is built for a human with a mouse. Plenty of licensing work is not that shape. It is repeated, it is scripted, and increasingly it is handed to a coding agent that is already sitting in your editor.
So Keylight has a command-line client. keylight manages apps, key types,
licenses, customers, payment integrations, and usage — everything the dashboard
does, from a terminal. It is open source under Apache-2.0, and every operation
the management API exposes is reachable from a command, enforced by a test that
fails the build if a new endpoint ever ships without one.
Why a licensing CLI, when there is already a dashboard
Three reasons, and only the third is interesting.
The obvious one is speed. keylight licenses list --status active is faster
than navigating to a list and filtering it, every time, forever.
The second is repetition. Anything you do more than twice — checking usage before a release, exporting licenses for a report, verifying that your price mappings still resolve after you edited them at your payment provider — is a line of shell rather than a ritual.
The third is that a CLI can be driven by something that is not you. That is the part that changes what is possible, and it is worth being precise about why.
Why your coding agent can drive it
Any CLI can technically be run by an agent. Most are miserable at it, because they were designed for a human who can react. They stop and ask “are you sure?”. They take secrets as arguments. They print colour codes and progress spinners into whatever is parsing them. An agent hits the first prompt and hangs.
Two rules avoid all of that, and Keylight’s CLI is built on them.
No command ever asks a question. Every input is a flag. Nothing reads from stdin waiting for a human, so nothing hangs when there is no human.
Secrets come from environment variables you name, never from arguments.
Arguments end up in shell history and are visible to anyone who can run ps. A
token in KEYLIGHT_API_TOKEN is neither.
export KEYLIGHT_API_TOKEN=klm_...
keylight licenses list --json | jq -r '.items[] | select(.status == "active") | .displayKey'
On top of those: every command takes --json, and exit code 2 means one
specific thing — not authenticated. It is broken out from every other failure
because it is the single problem an unattended caller can fix by itself, by
re-authenticating rather than by giving up and waking you.
The practical result is that “check whether this customer’s license is active” becomes something you ask for in your editor, and it happens, without you leaving what you were doing.
Installing it
Homebrew is the short path on macOS and Linux:
brew tap keylight-dev/tap
brew trust keylight-dev/tap
brew install keylight
Three commands rather than one, and the middle one is worth understanding. Since Homebrew 6, a formula from any third-party tap will not load until you trust it, with no prompt to click through. That is a supply-chain control and a good one — a tap is Ruby that runs on your machine. Read the formula first if you like; it is about sixty lines and downloads a published release binary.
If you have a Rust toolchain, cargo install keylight-cli works. If you want
neither, every release publishes plain binaries for macOS, Linux, and Windows.
Then sign in:
keylight login
That prints a short code and opens your browser. Check the code on the page matches the one in your terminal — that match is what proves the request came from the session you started, which is why the flow is safe on a machine you do not fully control. The browser never has to reach the CLI, so it works over SSH and on headless boxes too.
For CI, skip the browser entirely and set KEYLIGHT_API_TOKEN to a token minted
in the dashboard.
What you can actually do
Apps and their key types:
keylight products list
keylight products create --display-name "My App" --key-prefix MYAP --support-email [email protected]
keylight products key-types create --product my-app --key-type-id pro --display-name "Pro"
keylight products key-types verify-prices --product my-app
That last one is quietly useful. It checks that every mapped payment-provider price still resolves, which is the kind of thing that breaks silently when you edit prices at the provider and only surfaces at someone’s checkout.
Licenses, end to end:
keylight licenses list --product my-app --status active
keylight licenses create --product my-app --key-type pro --customer-email [email protected]
keylight licenses remint <license-id>
keylight licenses deactivate-device <license-id> --instance-id <instance-id>
Keys are masked in list output, so a screenshot or a shared terminal does not leak one. The raw key is returned exactly once, when it is minted.
Customers, integrations, and usage:
keylight customers get <customer-id>
keylight integrations status
keylight usage --days 30
Two details that matter more than they look
licenses create accepts an --idempotency-key. Reuse it across retries and a
repeated call cannot issue a second license — the first success is stored and
replayed verbatim. This is the difference between a script you can safely re-run
after a network wobble and one you have to babysit. If you leave it off, a fresh
key is generated per invocation, which is the right default for a human typing
one command and the wrong one for a loop that might retry.
Then there are test purchases:
keylight test-purchase create --product my-app --key-type pro
keylight test-purchase get <run-id>
That runs a purchase end to end and lets you inspect exactly what it issued — which key type, which activation limit, which expiry — without a real card. It is the fastest way to answer “did I wire that price mapping up correctly?” before a customer answers it for you.
The operations that still need a human
Seven operations are dangerous enough that a token cannot run them on its own, no matter how broad its permissions: revoking a license, exporting licenses, deleting a key type, rotating an integration secret, reading or rotating your SDK key, and setting your webhook.
For those, the CLI prints an approval URL, you approve that specific call in a browser, and it retries automatically — the same shape as npm’s web-based two-factor login. The approval covers one request, not standing permission.
This is deliberate, and it is the part that makes the rest comfortable. An agent with a token can do the routine work unattended. It cannot quietly revoke your customers’ licenses, and neither can anyone who steals that token.
Scripting across several apps
If you ship more than one app, this is where a CLI stops being a convenience.
Every command takes the app as a flag and every command speaks --json, so a
loop over your products is a few lines of shell — one usage report across a
portfolio, a nightly export, a bulk key-type change:
for app in $(keylight products list --json | jq -r '.items[].productId'); do
echo "$app: $(keylight licenses list --product "$app" --json | jq '.items | length') licenses"
done
Doing that through six dashboard tabs is how you end up not doing it.
Where this sits
Stripe stays your payment processor. Keylight is the licensing layer on top: it turns a completed payment into a signed license key, tracks device activations, validates offline, and now gives you a terminal for all of it.
The CLI is on the CLI page, the full command reference is in the docs, and the source is on GitHub. It works on the free plan — see pricing.
If there is a command you want that does not exist yet, send us your feedback.
Frequently asked
Do I need to know Rust to use a licensing CLI written in Rust?+
No. Homebrew installs a prebuilt binary, so there is no toolchain involved. Rust is an implementation detail of how the Keylight CLI is built, not something you interact with. If you already have a Rust toolchain, cargo install keylight-cli works too.
Can a coding agent really run licensing commands safely?+
For read operations and routine writes, yes — no command opens a prompt, credentials come from an environment variable rather than command-line arguments, and every command speaks JSON. Destructive operations are deliberately different: revoking a license or reading your SDK key needs a human to approve that specific call in a browser first, so a leaked token cannot quietly do damage.
What is the difference between the CLI and the dashboard?+
They talk to the same management API and show the same data. The dashboard is better for looking at things; the CLI is better for repeating things, scripting across several apps, and being driven by something other than a person.
Does this replace Stripe?+
No. Stripe stays your payment processor. Keylight is the licensing layer on top — it turns a completed payment into a signed license key, tracks which devices activated it, and gives you the commands to manage all of that.
Ready to ship?
Create your account and start licensing your apps in under a minute. Free forever tier included.
Start Free