Operator CLI
Manage the platform from a terminal — organizations, applications, pricing, usage, readiness — with nothing to configure.
dl is the operator CLI for the platform — for a platform admin managing
the deployment, not for a tenant's backend or end users. It signs in as you,
the same way the admin console does, and needs nothing configured beyond which
deployment to talk to.
go install github.com/deeplinq-org/deeplinq-cli/cmd/dl@latest
dl auth login
dl org listWhy nothing needs configuring
dl reads /.well-known/oauth-protected-resource (RFC 9728) from the API,
which names the identity provider and the CLI's own public client id. It never
compiles in an issuer or a client id, so pointing it at a different deployment
is one setting:
export DEEPLINQ_API=https://api.example.com # default: https://api.ondeeplinq.comSign-in is the device authorization grant (RFC 8628): approve the login in a browser, on any device, including one other than the terminal you ran the command on. There is no client secret to leak and nothing to rotate on a laptop — the CLI is a public native client (RFC 8252).
Commands
The grammar is GitHub CLI's: dl <noun> <verb>, noun singular. Plural and
long noun forms (organizations, apps, …) are aliases, as are the
top-level dl login, dl logout and dl whoami.
Authentication
dl auth login [--api URL] [--client-id ID]
dl auth logout
dl auth status [--json] # alias: dl whoamiauth status answers three questions — am I logged in, as whom, what next —
and verifies rather than guesses: an expired token is put to the proof by
attempting the refresh, so the state shown is real. Diagnostic depth (issuer,
client, scope, claims) lives behind --json. Token values never appear.
Organizations
The engine's own organizations (see Organizations) — not the identity provider's. All platform-admin only.
dl org list [--status deleted|purging|all] [--json]
dl org view [<org>] [--json]
dl org create --name <name> [--application <id>]
dl org bind [<org>] --ref <external-ref>
dl org credit [<org>] --micro-usd <amount> [--idempotency-key <key>]
dl org use [<org>]
dl org delete <org> [--yes]
dl org restore <org>
dl org delete <org> --purge --confirm <name>
dl org purge <org> [--yes]
dl org purge-status <org> [--json]dl org use sets a sticky default organization (the kubectl
use-context pattern): read and bind/credit commands accept their org
positionally or fall back to it. Destructive commands never fall back — a
delete or purge always names its target explicitly.
Applications
Products built on the engine (see Applications); each owns a realm at the identity provider.
dl app list [--status deleted|purging|all] [--json]
dl app create --realm <realm> --name <name>
dl app delete <app> [--yes]
dl app restore <app>
dl app delete <app> --purge --confirm <realm>
dl app purge <app> [--yes]
dl app purge-status <app> [--json]Pricing, usage, readiness
dl pricing list [--json]
dl pricing set <model> --provider <p> --input <micro-usd/MTok> --output <micro-usd/MTok>
[--margin <pct>] [--tier economy|standard|frontier]
[--cache-read <20ths>] [--cache-write-5m <20ths>] [--cache-write-1h <20ths>]
dl pricing delete <model>
dl usage [--org <org>] [--json]
dl readiness [--json]pricing set is a full-row upsert (see
Models and pricing): omitting a
cache multiplier clears any stored override, and --input/--output are
required so a forgotten flag can never write a silent zero rate. usage
shows every organization's balance; with --org, month-to-date spend by
model. readiness is the platform's capability health, component by
component.
The deletion lifecycle
Deleting is two explicit steps, never one accident (ADR-0078). Both ship today.
deleteis reversible. Access ends immediately; every byte is retained indefinitely;restoreundoes it. The CLI asks before acting (--yesskips the prompt). Nothing is erased, at any point, by this step.purgeis irreversible. It erases an already-deleted organization's data — or an application's, including its realm — through a fenced, checkpointed background workflow:purge-statusreports the per-store checkpoint set (which store, if any, is stuck) and repeatingpurgere-drives a stalled run from the first incomplete step rather than starting over. Both commands resolve the row first, so they work for exactly as long as it exists — see the warning below for what that means for an application. The one-step immediate mode,delete --purge, demands--confirm <exact name>for an organization or--confirm <exact realm>for an application — the CLI never infers it, because that confirmation is the one step a script cannot perform by accident.
Purge is irreversible. It erases PostgreSQL rows, blobs, chunk and memory
vector tenants, memory graphs, provider grants, and the identity provider's
objects for the target — there is no restore once it completes. The engine
only reports *.purge.completed once every required store confirms
erasure; a store that failed, was skipped, or was never configured leaves the
target purging with *.purge.failed naming the stuck store instead. For an
organization, purge-status (or purge, to re-drive) is how you find out
which one: PostgreSQL is the last step, so the row is still there to ask
about.
For an application it is not. The application row is deleted in step 1 and
the realm in step 2, and both commands resolve the row before anything else —
so once step 1 lands, purge-status and purge answer 404, the same as an
id that never existed. If the realm deletion then fails, the engine writes
application.purge.failed naming identity_realm, retries up to five times
(≈6 minutes), and gives up; no command can finish the job from there and the
remedy is deleting the realm by hand at the identity provider. The audit event
is the only signal.
An organization purge erases the organization's identity objects — its service accounts and, if one was provisioned, its first administrator — never the application-tier account of whoever signed up through it; that account is only erased when the application itself is purged, dropping the whole realm.
The engine enforces the ordering. Deleting an application that still contains
organizations answers organizations_remain with the count (a
deleted-but-unpurged organization still counts), and the platform's default
application refuses deletion outright with default_application. Purging an
active (not yet deleted) organization or application answers not_deleted;
restoring one mid-purge answers already_purging; and financial history
(ledger_entries, and the audit trail) survives a purge while balances and
content do not.
Output conventions
Every list/view command takes --json for scripting — typed, indented, and
piped output never carries ANSI codes (color follows the terminal and
NO_COLOR). Mutating commands print one line saying what changed. Money is
integer micro-USD on the wire and dollars in tables.
What the token can do
The token carries your identity, not a machine's — engine-side audit, metering, and per-user limits attribute every call to you. It is the same operator lane the admin console uses: a role granted in the console's own identity-provider project, not a tenant credential, and not available to a tenant no matter what role they assert inside their own project.
Admin commands require platform-admin. A token that authenticates but lacks
the role gets a message saying so, rather than being sent back to
auth login. Org-scoped calls ride the X-Deeplinq-Admin-Org header, which
the engine accepts only from a platform admin — and which rewrites the
request's principal to that organization's org-admin, dropping
platform-admin for that request.
Session storage
The session — including the refresh token — is written to your user
configuration directory (~/.config/deeplinq/session.json on Linux,
~/Library/Application Support/deeplinq/session.json on macOS) with mode
0600. No token is ever printed. offline_access is always requested, so an
expired access token refreshes on the next command without a new login.
dl auth logout removes the local session file only — it does not revoke
anything at the identity provider. The refresh token stays valid there until it
expires or is revoked centrally. The sticky organization set by dl org use
survives logout; it is working context, not a credential.
See authentication for the tenant
lanes this CLI does not use, and per-command detail in the
deeplinq-cli README.