Enlitt

CLI

Manage your business account, applications, and keys from the terminal with the Enlitt CLI.

The Enlitt CLI is the terminal client for your business account. A coding agent can drive it without ever seeing a credential. You approve the sign-in in your browser, and the CLI writes secret keys straight to a file you name.

This page is the complete reference. It covers every command, flag, default, output shape, finding, and error code, plus recipes for the common tasks.

Install

npm i -D @enlitt/cli
npx enlitt --help
npx enlitt key rotate --help   # one command's flags, with descriptions and defaults

The CLI needs Node.js 22 or later and has no runtime dependencies.

Choose the deployment: --env

Every command talks to one Enlitt deployment, chosen by --env:

--envEnlitt siteAPI
production (default)https://enlitt.comhttps://api.enlitt.com
staginghttps://staging.enlitt.comhttps://staging-api.enlitt.com

Each deployment has its own accounts, applications, keys, and CLI sign-in, kept in a separate credential file. When you work against staging, pass --env staging on every command, including login, reauth, doctor, and the commands the CLI prints for you. A command without it acts on production. The commands the CLI prints already carry the right --env.

Sign in and approve

npx enlitt login

The CLI prints a URL and a short code (XXXX-XXXX) and opens your browser. Check that the code matches, then approve while signed in to your business account. If you have no business yet, the approval page lets you create one. The CLI never asks for a password, sign-in code, Google credential, or cookie.

A coding agent's shell call times out long before a person finishes approving, so split the step in two:

npx enlitt login --no-wait    # prints the URL and code once, then exits 0
# ... the developer opens the URL and approves ...
npx enlitt login --wait       # resumes and finishes sign-in

--no-wait saves the pending approval locally. --wait resumes it and polls until you approve or deny. --timeout <seconds> bounds one --wait. A timeout fails with authorization_pending but keeps the pending approval, so run --wait again. An approval left unanswered expires after about 10 minutes; then start again with --no-wait.

A sign-in is valid for 90 days or until revoked. It is bound to one business: the one you chose when approving. To work in another business, see Several businesses.

Fresh approval for key commands

Creating, rotating, and revoking a key, and listing Read-only keys, also need an approval from the last 15 minutes. When the last one is older, the command fails with reauthentication_required and changes nothing. Then:

npx enlitt reauth --no-wait   # prints a URL; the developer approves
npx enlitt reauth --wait
# rerun the key command

In an interactive terminal, the key command runs this flow itself and retries once. One approval covers every key command for the next 15 minutes.

Roles: what your sign-in can do

The CLI acts with your role in the business, and the role decides which commands run. npx enlitt whoami prints your role and, in business.allowed_commands, the commands you can run.

Commandviewerdeveloperadminowner
login, reauth, logout, whoami, status, doctorYesYesYesYes
business info, traffic, app list, app show, key listYesYesYesYes
app create, app updateNoYesYesYes
setupLink¹Yes²Yes²Yes
key create, key rotate, key revoke, key list --scope read-onlyNoNoNoYes

¹ A viewer can rerun setup on an application that already exists, which links the project and writes ENLITT_CLIENT_ID, but cannot create one. ² Without the owner role, setup does everything except create the key. See below.

A command your role cannot run fails with insufficient_permissions before it changes anything. The message names your role and the business, and action says who can do it:

  • Key commands: only the owner can create, rotate, or revoke keys. Ask the owner to run the command, or to create the key on the dashboard's Keys page.
  • App commands: ask the owner or an admin to make you a developer or admin, or to run the command for you.

The server checks your role on every request, so a role change applies to your next command, and removing you from the business stops your sign-in at once. Members, payouts, and ownership stay in the dashboard for every role (see What the CLI cannot do).

setup as a developer or admin

setup registers and links the application and writes ENLITT_CLIENT_ID, then skips the key and exits 0. Its output lists the key under missing, with the action to take:

{
  "wrote": ["ENLITT_CLIENT_ID"],
  "missing": [
    {
      "variable": "ENLITT_SECRET_KEY",
      "action": "Only the business owner can create keys. Ask the owner to create an app key for My App (app_…) and put it in ENLITT_SECRET_KEY in .env.local."
    }
  ]
}

Ask the owner for the key, and let them put it in the env file themselves; never send it through a chat. Until then, doctor reports secret_key_missing.

Several businesses

One login can own a business and be a member of others. A CLI sign-in acts for exactly one of them: the one you choose on the approval page. When you belong to more than one, that page shows a Business picker with your role in each. Choosing a business there also switches the business your dashboard shows.

To work in another business, sign in again and pick it when approving:

npx enlitt login --no-wait
npx enlitt login --wait
npx enlitt whoami

The new sign-in replaces the old one for that --env. In the dashboard, switch businesses from the business menu at the top of the sidebar; All businesses and invitations lists every business you can reach and the invitations waiting for you.

Output, exit codes, and global flags

FlagMeaning
--jsonstdout carries exactly one JSON document: the result, or { "error": { "code", "message", "action" } }.
--env production|stagingWhich Enlitt deployment and sign-in to use. Default production.
--help, -hThe command list, or one command's flags and defaults (npx enlitt setup --help).
--versionThe CLI version.
  • Streams. Results go to stdout. Progress text, such as a URL to open or "waiting for approval…", always goes to stderr, so it never corrupts a JSON document. Without --json, an error goes to stderr as error: <message> and next: <action>.
  • Exit codes. 0 success. 1 any refusal or failure, including confirmation_required and a doctor run with an error finding. 2 a usage error: an unknown command or flag, a missing flag, or a malformed value.
  • Stable JSON. A result may gain fields but never loses or renames one. IDs are Enlitt's wire IDs. Money is a NANOUSD integer string in JSON (1 USD = 1,000,000,000 NANOUSD) and USD in human output.
  • Dates in flags are YYYY-MM-DD, UTC, inclusive. Timestamps in output are RFC 3339.

Common tasks

Wire a new project

From the project root (the folder with package.json):

npx enlitt login --no-wait && npx enlitt login --wait
npx enlitt setup --name "My App" \
  --redirect-uri http://localhost:3000/auth/enlitt/callback \
  --origin http://localhost:3000
npx enlitt doctor

setup registers a test application, writes enlitt.json, and writes ENLITT_CLIENT_ID and a new App key (ENLITT_SECRET_KEY) to .env.local. Only the owner can create the key; for other roles see setup as a developer or admin. It prints variable names only. Then mount the login routes from Login and sessions. Against staging, add --env staging to each command; setup then also writes ENLITT_BASE_URL.

Add a callback URL or a port

Register both the callback URL and its origin. Leave enlitt.json linked so the CLI knows the application:

npx enlitt app update --add-redirect-uri http://localhost:3001/auth/enlitt/callback \
  --add-origin http://localhost:3001

http://localhost:3000 and http://localhost:3001 are different callbacks; see the redirect rules.

Diagnose a login that fails

npx enlitt doctor --callback-url http://localhost:3001/auth/enlitt/callback

doctor checks the sign-in, the project link, the env file, the client ID, the secret key, and whether the callback and origin are registered exactly. Each finding carries the exact command that fixes it. See doctor findings.

Answer a question about the account

QuestionCommand
Who am I signed in as, and which business?npx enlitt whoami
Is this project wired up?npx enlitt status, then npx enlitt doctor
How much have I earned? Can I be paid out?npx enlitt business info
How many visitors and calls per day? For one application?npx enlitt traffic --days 7, npx enlitt traffic --app <id>
Which applications exist, and their client IDs?npx enlitt app list
What callbacks and origins are registered?npx enlitt app show
Which keys exist, and when were they last used?npx enlitt key list

Replace a lost or leaked secret

npx enlitt key list                                   # find the key id
npx enlitt key rotate <key_id> --write-env .env.local

Rotation keeps the key's identity and writes the new secret over the old value in the env file. The output says when the old secret stops working (previous_key.expires_at), so you know how long a deployed server can keep the old value. Update every deployment's secret store before then. A live key needs --confirm.

Move to live

A live application is a separate registration with its own client ID and key. Keep the test one for development:

npx enlitt app create --name "My App" --environment live \
  --redirect-uri https://my-app.example/auth/enlitt/callback \
  --origin https://my-app.example --confirm
npx enlitt key create --app <live_app_id> --name production \
  --write-env .env.production.local

Then copy that secret into the production server's secret manager. A coding agent must ask you before passing --confirm.

Command reference

login

npx enlitt login [--label <text>] [--no-browser] [--no-wait | --wait] [--timeout <seconds>]

FlagMeaning
--labelName shown on the approval page and under Settings, CLI access. Default <hostname> (<platform>).
--no-browserDo not open a browser; only print the URL. Also the behavior when CI is set or BROWSER=none.
--no-waitStart the approval, print the URL and code, save it as pending, and exit 0.
--waitResume the pending approval and poll until a decision.
--timeoutSeconds one wait may poll. Default: until the approval expires.

Without --no-wait or --wait, it starts and waits in one run. Output: { login: { email }, business: { id, name }, credential_id, expires_at }. With --no-wait: { status: "pending", verification_uri_complete, user_code, expires_at }. A new sign-in replaces and revokes the previous one for the same --env.

reauth

npx enlitt reauth [--no-browser] [--no-wait | --wait] [--timeout <seconds>]

Same flags as login. Needs a sign-in. Must be approved by the same login, for the same business. Output: { reauthenticated_at }.

logout

npx enlitt logout revokes this CLI sign-in on the server and deletes the local credential, even if the server call failed. Output: { logged_out, revoked }. Without a sign-in it prints { logged_out: false, revoked: false } and exits 0.

whoami

npx enlitt whoami prints { login: { email }, business: { id, name, role, permissions, allowed_commands }, environment, api_origin, credential_expires_at }. allowed_commands lists the commands your role can run, such as "app create". The human form prints them as "You can run: …" and "Not with your role: …".

status

npx enlitt status is whoami plus the project link, and works when signed out. Output: { logged_in, login?, business?, project: { linked, application_id?, env_file?, env_file_has: { ENLITT_SECRET_KEY, ENLITT_CLIENT_ID } } }. It reports whether each variable is present, never its value.

business info

npx enlitt business info [--from <date>] [--to <date>]

FlagMeaning
--fromFirst UTC day of the traffic and usage window. Default: 30 days before --to.
--toLast UTC day, inclusive. Default: today.

Output: { business, earnings, payout, traffic: { usage }, range, data_fresh_at, application_count }. earnings.earnings_nano_usd is the balance you can be paid now, and payout is today's eligibility; neither is limited to the window. The fields are explained in Business reporting. In words, it states earnings, whether a payout is possible, and, when not, why (blocked_reason) and when it next can be (next_eligible_at).

traffic

npx enlitt traffic [--app <id>] [--days <n> | --from <date> --to <date>]

FlagMeaning
--appOne application. Default: the whole business.
--daysThe last n UTC days including today, 1 to 3,660. Default 30.
--from / --toAn explicit inclusive UTC day range, instead of --days.

Output: { range, usage, days: [{ date, visitors, standard_requests, premium_requests, premium_earnings_nano_usd }] }, one row per day, with zeros for a day with no calls. With --app, each day also has standard_used_nano_usd and premium_used_nano_usd, and usage is that application's total.

Visitors are distinct signed-in users who made at least one call that UTC day. The same person on two days counts on both, so summing days does not give unique visitors for a week or month, and no command reports a unique count over a range. Report the daily figures, or say "visitor-days" for a sum. Business-wide visitors are not the sum of the per-application counts either.

app list

npx enlitt app list prints { applications: [{ id, name, environment, status, client_id, active_key_count }] }.

app show

npx enlitt app show [<id>] prints one application. The ID defaults to the one linked in enlitt.json; without a link it fails with project_not_linked.

{
  id: string;
  client_id: string;            // public; ENLITT_CLIENT_ID
  name: string;
  environment: "test" | "live";
  status: string;               // "active" when sign-in works
  info: { icon_url?: string; description?: string } | null;
  redirect_uris: string[];      // callback URLs, matched exactly
  allowed_origins: string[];
  created_at: string;
  api_keys: ApiKeyMetadata[];   // see key list
}

app create

npx enlitt app create --name <n> --environment test|live [options]

FlagMeaning
--name (required)Name users see on the consent screen.
--environment (required)test or live. Both consume real credits; see Test and live.
--redirect-uri <url>A callback URL. Repeat for several.
--origin <origin>An allowed origin (scheme, host, port). Repeat for several.
--icon-url <url>Icon on the consent screen.
--description <text>Description on the consent screen.
--linkAlso write enlitt.json for this application.
--confirmRequired with --environment live, because real users sign in to it.

Prints the application, as app show does.

app update

npx enlitt app update [<id>] [options]. The ID defaults to the linked application. Only the lists and fields you name change.

FlagMeaning
--name <n>Rename.
--redirect-uri <url>…Replace the whole callback list with these.
--add-redirect-uri <url>…, --remove-redirect-uri <url>…Add to or remove from the callback list.
--origin <origin>…Replace the whole origin list.
--add-origin <origin>…, --remove-origin <origin>…Add to or remove from the origin list.
--icon-url <url> / --clear-icon-urlSet or clear the icon.
--description <t> / --clear-descriptionSet or clear the description.

Mixing a replace form with an add or remove form for one list is a usage error. A change takes effect at the next sign-in and signs no one out. The linked enlitt.json is updated to match.

key list

npx enlitt key list [--app <id>] [--scope app|read-only]

FlagMeaning
--appOnly this application's keys. Default: every application's App keys.
--scopeapp (default) or read-only. read-only needs a fresh approval.

Output: { keys: ApiKeyMetadata[] }. Never a secret.

interface ApiKeyMetadata {
  id: string;
  name: string;
  prefix: string; // first characters of the secret, to recognize it
  scope: "model" | "reporting"; // wire values: "model" is an App key, "reporting" a Read-only key
  application_id: string | null; // null for a Read-only key
  environment: "test" | "live" | null;
  status: "active" | "revoked" | "expired";
  created_at: string;
  expires_at: string | null; // set on a rotated-out key: when it stops working
  last_used_at: string | null;
}

key create

npx enlitt key create (--app <id> | --scope read-only) --name <n> --write-env <file> [--replace] [--var <NAME>]

FlagMeaning
--app <id>Create an App key for this application.
--scope read-onlyCreate a business-wide Read-only key instead.
--name (required)A label for the key.
--write-env <file>The env file to write the secret to, inside the project.
--var <NAME>Variable name. Default ENLITT_SECRET_KEY (App key) or ENLITT_REPORTING_KEY (Read-only key).
--replaceOverwrite the variable if the file already has it. Default: refuse with env_key_exists.
--revealPrint the secret instead of writing it. Human terminals only; see secret rules.

Output: { key: ApiKeyMetadata, secret_written_to: { file, variable }, gitignored }. Without --write-env or --reveal it refuses with secret_destination_required before any request, so no key is created that nobody captured. Needs a fresh approval.

key rotate

npx enlitt key rotate <key_id> --write-env <file> [--var <NAME>] [--confirm]

Same destination flags as key create, with one difference: --replace is on by default, because replacing the old value is the point. Rotation keeps the key's ID, name, type, and application, and issues a new secret. The old secret keeps working until previous_key.expires_at. A live App key and every Read-only key need --confirm. Needs a fresh approval. Output: { key, secret_written_to, gitignored, previous_key }.

key revoke

npx enlitt key revoke <key_id> --confirm disables a key immediately and permanently; calls with it fail at once. Always needs --confirm and a fresh approval. Output: { revoked: ApiKeyMetadata }.

setup

npx enlitt setup [options] wires a project in one step and is safe to rerun.

FlagMeaning
--app <id>Use this existing application.
--name <n>Name for a new application. Required when neither --app nor enlitt.json names one.
--environment test|liveEnvironment of a new application. Default test.
--redirect-uri <url>…Callback URLs of a new application.
--origin <origin>…Allowed origins of a new application.
--env-file <file>The env file to write. Default .env.local.
--replace-secretCreate a new App key even if the env file already has ENLITT_SECRET_KEY, and overwrite it.
--confirmRequired only when setup creates a live application.

It runs these steps in order:

  1. Resolve the application: --app, else the one in enlitt.json, else create one from the flags. When the application already exists, the creation flags (--name, --environment, --redirect-uri, --origin) are ignored, and stderr says so. Use app update to change an existing one.
  2. Write enlitt.json.
  3. Write ENLITT_CLIENT_ID to the env file.
  4. Against staging, also write ENLITT_BASE_URL, so the SDK calls the same deployment.
  5. If the env file has no ENLITT_SECRET_KEY, or with --replace-secret, create an App key named cli setup <date> and write it.

An existing secret is never rotated or overwritten without --replace-secret. Output: { application: { id, name, environment, client_id }, env_file, wrote, missing, gitignored, project_file, next }. wrote lists the variable names written, never values. missing lists what setup could not write with your role, each with an action; it is [] for the owner. next is the docs page to read next. Creating the key needs the owner role and a fresh approval.

doctor

npx enlitt doctor [--app <id>] [--env-file <file>] [--callback-url <url>]…

FlagMeaning
--appCheck this application. Default: the linked one.
--env-fileThe env file to check. Default: the one in enlitt.json, else .env.local.
--callback-urlA callback URL your app will use. Repeat for several. Checked together with enlitt.json's redirectUris.

Output: { ok, findings: [{ code, severity, message, action }] }. Exits 1 when any finding is an error. It reads the secret only to compare it locally and never prints it.

Doctor findings

Checks run in this order. A check that depends on a failed one is skipped, so fix the first finding and run doctor again.

CodeSeverityMeaning and fix
not_logged_inerrorNo CLI sign-in for this --env. Run npx enlitt login.
project_not_linkedwarningNo enlitt.json and no --app. Run npx enlitt setup.
application_not_founderrorThe application is not in the signed-in business. Check --env and the business in whoami.
application_not_activeerrorThe application is not active, so sign-in to it fails.
env_file_missingerrorThe env file does not exist. Run npx enlitt setup.
env_file_trackederrorgit tracks the env file. Remove it from git and use an untracked file.
env_file_not_ignoredwarninggit does not ignore the env file. Add it to .gitignore.
env_file_permissionswarningOthers can read the env file. Run chmod 600 on it.
client_id_missingerrorNo ENLITT_CLIENT_ID. Run npx enlitt setup.
client_id_mismatcherrorENLITT_CLIENT_ID belongs to another application. Run npx enlitt setup for the right one.
secret_key_missingerrorNo ENLITT_SECRET_KEY. Run npx enlitt setup.
secret_key_not_activeerrorThe secret is not an active key of this application: another application's, revoked, or expired. Create or rotate a key.
callback_mismatcherrorA callback URL is not registered exactly. The action is the app update command that adds it and its origin.
origin_mismatcherrorAn origin in enlitt.json is not registered. The action adds it.
environment_mismatchwarningenlitt.json says test and the application is live, or the reverse.

Errors and their actions

With --json an error is { "error": { "code", "message", "action" } }. Without it, stderr gets error: <message> and next: <action>. Do what action says; its commands already carry your --env.

CodeAction
not_logged_in, dashboard_login_requiredRun npx enlitt login.
reauthentication_requiredRun npx enlitt reauth --no-wait, have the developer approve the printed URL, run npx enlitt reauth --wait, then retry.
insufficient_permissionsYour role does not allow this, or the CLI cannot do it. Ask the business owner, or use the dashboard.
business_selection_requiredYou no longer have access to this business. Run npx enlitt login again.
confirmation_requiredAsk the developer to confirm, then rerun with --confirm.
secret_destination_requiredRerun with --write-env <file>.
env_file_trackedThe file is committed to git. Use an untracked file such as .env.local.
env_key_existsThe file already holds this variable. Rerun with --replace only if the developer wants it overwritten.
insecure_credential_fileRun chmod 600 <path>, then retry.
project_not_linkedRun npx enlitt setup or pass --app <id>.
no_pending_authorizationRun --no-wait first; there is no pending approval to resume, or it expired.
authorization_pendingThe developer has not approved yet. Run the same command with --wait again.
access_deniedThe developer denied the request in the browser.
expired_tokenThe approval expired. Start again with --no-wait.
reveal_requires_terminal--reveal works only in a human terminal. Use --write-env <file>.
usage_errorFix the command; --help lists the flags. Exit code 2.
network_errorCheck the network and retry.

Any other code: see Errors and recovery.

enlitt.json and the env file

enlitt.json sits at your project root (the nearest folder with a package.json or .git):

{
  "applicationId": "…",
  "environment": "test",
  "redirectUris": ["http://localhost:3000/auth/enlitt/callback"],
  "allowedOrigins": ["http://localhost:3000"],
  "envFile": ".env.local"
}

It records what the project expects and never holds a secret, so it is safe to commit. doctor compares it with what Enlitt has registered, and app update keeps it in sync.

The env file gets at most these variables:

VariableWritten bySecret?
ENLITT_CLIENT_IDsetupNo
ENLITT_SECRET_KEYsetup, key create, key rotateYes
ENLITT_REPORTING_KEYkey create --scope read-onlyYes
ENLITT_BASE_URLsetup, against staging onlyNo

The file must stay inside the project. The CLI refuses a file that git tracks. For an untracked file that git does not ignore, it adds the file to .gitignore and reports gitignored: true. Every other line of the file is kept unchanged. The file is written with owner-only permissions. Your deployed server needs the same variables in its own secret storage; the CLI writes only local files.

Where the credential lives, and how to revoke it

The CLI stores its sign-in in cli-<env>.json in ~/.config/enlitt (or $XDG_CONFIG_HOME/enlitt; %APPDATA%\enlitt on Windows), readable only by you. It is a CLI credential, not a browser session. Within your role, it can read the business, its applications and traffic, and manage applications and keys. It cannot do the things listed under What the CLI cannot do.

Revoke it with npx enlitt logout. You can also revoke it under Settings, CLI access in the business dashboard, which works for a machine you no longer have. Removing a member from the business stops their CLI sign-in at once.

Secret rules

  • A raw API key is written only to the file you named. It never appears on stdout or stderr, in JSON, in enlitt.json, in an error, or in a log.
  • --reveal prints a secret in human output only. It is refused with --json and whenever stdout is not a terminal, so an agent's shell cannot use it.
  • Do not paste a key into a chat, an issue, or a commit.

What the CLI cannot do

The following are dashboard-only. The server refuses them to a CLI sign-in even if you hold the owner role:

  • payouts and payout onboarding;
  • members and invitations;
  • ownership transfer;
  • business creation;
  • account settings.

--confirm is the only confirmation. There is no prompt, so the rule is the same with and without a terminal.

On this page