Command-line client
Sign in, hold login profiles, and drive a space's leases and task attachments from a terminal with cadence.
cadence is the command-line client for Cadence. It signs in and holds your
login, lists and inspects the leases in your space, reads and writes task
attachments, and carries the verbs a worker runs from inside a lease. Each
command wraps one call to the same HTTP API the
web interface and the HTTP API use, so anything you
can do here you could also do with a raw HTTP request — the client is the shorter
path, not a separate set of capabilities.
The examples below use https://cadence.example.com for the instance address and
OPS for a space key — replace both with your own. Example task keys such as
OPS-42 and lease ids such as 33521 are invented; use the real ones from your
space.
This page assumes the cadence client is already installed and on your PATH.
Every command below starts cadence; if your shell reports cadence: command not found, the client is not installed on this machine. If your instance offers the
client for download, you can get it yourself from Download CLI in the web
interface's user menu — see
Downloading the command-line client. If that menu item
is not there, your instance does not offer downloads, so get the client from
whoever operates your instance.
The two authentication faces
cadence has two faces, and they authenticate differently. Which face a command
belongs to is fixed — the client never guesses.
- The operator face is the commands you run as yourself:
login,whoami,profile,lease, andtask-attachment. They authenticate with your personalsmc_login token, held in a profile (see Signing in). - The worker face is the
workercommands. They run from inside a running lease and authenticate as that lease, reading the lease's credentials from the environment Cadence sets for the lease — you do not type a token or run these by hand (see Worker commands).
Keeping the faces apart is why a worker command can never act as you, and an
operator command can never act as a lease.
Discovering commands
Run cadence with no arguments, or with -h / --help, to see the commands and
their options. Every command and subcommand takes -h / --help for its own
usage.
-v / --version prints the version and exits:
$ cadence --version
0.0.0-devA build you installed from a tagged release prints that release's version instead
of 0.0.0-dev. The version command prints the same string, and adds --json:
$ cadence version --json
{"version":"0.0.0-dev"}When you mistype a command, the client prints an error, suggests the closest
command it knows, and points you at --help rather than running anything.
Signing in
Before you can run the operator commands you need a personal smc_ API token.
Mint one in the web interface as described in
API tokens — the full value is shown only
once, so copy it then.
cadence login verifies a token against the server before it saves it, so a
bad token fails loudly instead of being stored:
$ cadence login --url https://cadence.example.com
Paste your cadence login token (smc_…):
Logged in as you@example.com (id 4021) at https://cadence.example.com (profile default)The prompt reads the token without echoing it. Instead of being prompted you can
pass the token with --token <smc_…>, or pipe it on standard input — useful in a
script:
$ echo "$MY_TOKEN" | cadence login --url https://cadence.example.comIf no token reaches the command by any of those routes, it refuses:
error: No token provided. Pass --token <smc_…>, pipe it on stdin, or run interactively.--url sets the base URL recorded for the profile. Leave it off and the client
keeps the profile's saved URL, or falls back to the built-in default when there is
none. --json emits { user, baseUrl, profile } instead of the line above.
Where your login is stored
cadence login saves the token as a named profile in ~/.cadence/config.json.
The file is created readable only by you (mode 0600), because every token
carries your full authority. A config written by an older version of the client is
migrated to the current format the first time a command reads it.
Never commit this file or share it — anyone who reads it holds your login.
Profiles
A profile is one saved login. Hold as many as you like — one per account or
instance — and pick one per command. One profile is the active default, marked
with *.
cadence profile ls lists your profiles. Tokens are never printed:
$ cadence profile ls
NAME TYPE BASE URL
* default operator https://cadence.example.com
work operator https://cadence.example.comWith no profiles saved it prints nothing and says so:
No profiles saved. Run `cadence login` to create one.cadence profile use <name> sets the active default. Naming a profile you have
not saved is refused:
error: No such profile "work". Run `cadence profile ls` to see saved profiles.cadence profile rm <name> deletes a saved profile. It refuses to remove the one
that is currently the active default unless you pass --force, which also clears
the active-default pointer:
error: Cannot remove "default": that's the active default — run `cadence profile use <other>` first, or pass `--force`.Each of ls, use, and rm takes --json.
Which login a command uses
An operator command resolves its credential in this order, taking the first that applies:
--profile <name>, when the command is given one.- The
CADENCE_PROFILEenvironment variable, naming a saved profile. - The active default profile.
CADENCE_TOKEN(withCADENCE_BASE_URLfor the address, if set) — an ephemeral login for scripting or continuous integration, needing no saved profile.
If steps 1 or 2 name a profile that does not exist, the command stops with a hard error rather than falling through — it never silently uses a different login. When none of the four applies, the command refuses:
error: Not logged in. Run `cadence login` first.Confirm who you are
cadence whoami shows the identity the active login resolves to:
$ cadence whoami
you@example.com (id 4021)--profile <name> runs it against a specific saved profile; --json emits the
server's { user } body. This is the same identity as
GET /api/v1/whoami.
Global controls
These apply to every command.
Request timeout
Every request is bounded by a per-request timeout, so a call to a wedged server
fails instead of hanging. The default is 30 seconds. Raise or lower it with
--timeout <seconds> on any command, or by setting the CADENCE_TIMEOUT
environment variable; the flag wins over the variable, which wins over the
default. The value must be a positive number of seconds.
When a request runs past the bound, the client aborts it and tells you which bound it hit and how to change it:
error: Request timed out after 30s: GET /whoami did not respond in time. If the server is healthy but slow, raise the bound with --timeout <seconds> or the CADENCE_TIMEOUT environment variable.Colour
Help and error text are coloured when the output is a terminal. Set NO_COLOR to
any non-empty value to turn colour off whatever else is set; set FORCE_COLOR to
keep colour on even when the output is not a terminal — for example when piping to
a pager.
Exit codes and errors
Every command that fails prints a single line to standard error, beginning
error:, and exits non-zero. The client never prints a raw stack trace or a raw
HTTP response body — a failure is always turned into a message that says what went
wrong.
If you interrupt a command with Ctrl-C it exits 130; a termination signal exits
143.
When the client is too old
Cadence and the client agree on a protocol version. If your cadence is older
than the version the server still accepts, the worker commands stop with an
upgrade message rather than failing obscurely:
error: This cadence tool is too old to talk to the Cadence server. It speaks protocol version 1, but the server supports 2-3. Upgrade to the latest cadence and try again.The supported range is whatever the server reports; install a newer cadence and
run the command again. If your instance offers the client for download, get the
newer version yourself from Download CLI in the web interface's user menu —
see Downloading the command-line client § Updating the client.
If that menu item is not there, get it from whoever operates your instance.
Watching leases
When a pool staffs a stage, it leases a task to a worker, and that lease is the unit of running work. These operator commands let you watch and reach the leases you can see. For what a lease is, its states, and what its logs and exec target mean, see Leases; the commands here show how the client reaches them.
Each command takes --profile <name> to run against a specific saved login.
List leases
cadence lease ls lists a space's leases, newest first. --space <key> is
required:
$ cadence lease ls --space OPS --state running
ID STATE POOL TASK STARTED
33521 running delivery OPS-42 2026-05-20T11:03:00ZFilter by lease state with --state, cap the page with --limit <n>, and page
with --cursor <cursor>. When more results remain, the client prints the cursor
to re-run with on standard error, so the table on standard output stays clean for
piping:
More results — re-run with --cursor eyJ...With no leases it prints No leases found. on standard error. --json emits the
server's raw { leases, next_cursor } body.
Read one lease
cadence lease get <id> shows one lease's detail:
$ cadence lease get 33521
id: 33521
state: running
attempt: 1
paused by: —
pool: 7 — delivery
task: OPS-42
workflow: delivery
worker version: 42
image: agent:1.4.0
created: 2026-05-20T11:00:00Z
dispatched: 2026-05-20T11:02:00Z
started: 2026-05-20T11:03:00Z
last heartbeat: 2026-05-20T11:15:00Z
finished: —The workflow: row shows the slug of the
workflow the lease's work sits on. A field
with no value shows an em dash, and so does workflow: for a lease with no
workflow, such as a space-subject lease.
The client no longer prints a board: row, so a script
that reads board: from this output stops matching. For scripts, use --json:
it emits the server's raw { lease } body, which the client passes through
without reshaping.
Read a worker's logs
cadence lease logs <id> prints a lease's worker log lines, whether the lease is
still running or has finished:
$ cadence lease logs 33521
Starting task OPS-42
Fetched payload
DoneNarrow the output with --tail <n> for the last N lines, --since <iso8601> for
a lower time bound, and --container <name> to pick a container; page with
--cursor <cursor>. When more logs remain it prints the cursor to re-run with on
standard error; with no events it prints No log events. there.
-f / --follow keeps polling and printing new events until you interrupt it with
Ctrl-C. --json emits the page body; under --follow it emits one JSON event per
line as each arrives.
Open a shell in a lease
cadence lease exec <id> opens a shell inside a running lease's container. It asks
the server where the lease runs and then shells in locally, so the matching tool
must be installed on your PATH — aws for a lease running under Amazon ECS, or
docker for one running under Docker. With no command it opens an interactive
sh; put -- before a one-shot command so the client passes its flags through
untouched:
$ cadence lease exec 33521 -- ls -la /tmpThe command exits with the child command's own exit code.
A lease you can reach still refuses a shell in these cases, each with its own message:
-
The lease was not started in a mode that allows a shell:
error: Cannot exec into lease 33521: it was not started in debug mode, so no exec session is enabled. Re-dispatch the lease in debug mode and try again.A lease runs in debug mode only when its pool has debug mode turned on. Set the pool's
debugsetting (see Fleet), then let the pool lease the task again, so the new lease is one you can shell into. -
The lease's runtime does not support shelling in at all — the message says so.
-
The needed tool is not installed:
error: Cannot exec into lease 33521: the `docker` command was not found on your PATH. Install docker and try again.
Lease attachments
A lease keeps the files a worker attached to it — a run log, a transcript — even
after it finishes. cadence lease-attachment lists and downloads them. For what a
lease attachment is, see Leases.
Each command takes --profile <name>.
cadence lease-attachment ls <lease-id> lists a lease's retained attachments,
newest first:
$ cadence lease-attachment ls 33521
ID KIND CONTENT-TYPE SIZE CREATED
9001 run_logs text/plain 14822 2026-05-20T11:16:00ZWith no attachments it prints No attachments. on standard error. --json emits
the server's { attachments } body.
cadence lease-attachment get <lease-id> [attachment-id] downloads one
attachment's bytes. Give exactly one selector — an attachment id, or --kind <kind> to fetch the newest attachment of that kind. Giving neither, or both, is
refused before any request:
error: Give an attachment id or --kind to say which attachment to fetch.By default the bytes go to standard output, byte-identical to what the server
stored, so you can pipe or redirect them; --out <path> writes them to a file
instead. Fetching by --kind when no attachment of that kind exists is an error
naming the kind. When several attachments match the kind, the client fetches the
newest and notes the choice on standard error:
2 attachments matched kind "run_logs"; fetched the newest (id 9001).Task attachments
cadence task-attachment reads and writes the file attachments on any task you can
reach. For what a task attachment is, its size limit, and the media types allowed,
see Task data. Each command takes
--profile <name>.
cadence task-attachment ls <task-key> lists a task's attachments, in the order
they were added:
$ cadence task-attachment ls OPS-42
ID FILENAME CONTENT-TYPE SIZE CREATED
512 spec.pdf application/pdf 20481 2026-05-20T09:00:00ZWith no attachments it prints No attachments. on standard error. --json emits
the server's response body unchanged.
cadence task-attachment get <task-key> <attachment-id> downloads one attachment
by id. A task's attachments are identified by filename and several may share one,
so list first to find the id. The bytes go to standard output, or to --out <path>
when given.
cadence task-attachment attach <task-key> <file> uploads a local file as an
attachment. --content-type <type> is required and is never inferred from the file
name — you state the media type the attachment is stored as:
$ cadence task-attachment attach OPS-42 ./spec.pdf --content-type application/pdf
Attached spec.pdf (20481 bytes) to task OPS-42.--json emits the server's attachment record. An empty file, or a file that does
not exist, is refused before any request:
error: Cannot attach: file "./spec.pdf" is empty; nothing would be uploaded.Worker commands
The worker commands are the worker face. An agent runs them from inside a running
lease, and they authenticate as that lease using the credentials Cadence puts in
the lease's environment — the lease id, its token, and the instance address. You do
not set these or run these commands by hand; they are what a worker image calls
instead of raw HTTP requests. Run outside a lease, where that environment is not
set, they refuse:
error: MC_BASE_URL is not set; worker commands must run inside a lease.Each command acts on the lease it is running inside — there is no lease-id argument.
claim
cadence worker claim claims the lease and prints its payload. A lease that has
already reached a terminal state cannot be claimed again:
error: Cannot claim lease: lease 33521 is already terminal (HTTP 410) and cannot be claimed again.payload
cadence worker payload prints the worker configuration for the lease — its system
prompt, model, skills, and the rest — as JSON. -o / --output <file> writes it to
a file and leaves standard output empty, for a script that reads the file.
heartbeat
cadence worker heartbeat reports the worker is alive and prints the control word
the server returns, one of none, cancel, expire, pause:pool,
pause:operator, or resume:
$ cadence worker heartbeat
noneA worker loop reads that word to know whether to keep going, stop, pause, or wind
down. expire means the lease is within 120 seconds of its pool's
max lifetime, after which
Cadence kills it. On expire, a worker should upload what it needs to keep, with
cadence worker attach or cadence worker attach-task,
before the cap.
When more than one word applies, cancel wins over expire, and expire wins
over pause:pool, pause:operator and resume. So a paused lease still hears
expire. A running lease in a paused pool hears expire too, and while it does,
that heartbeat does not pause the lease for the pool.
--json emits { control, reported_outcome, reported_summary }, passing the two
reported fields through exactly as the server sent them — null when nothing was
reported.
complete
cadence worker complete ends the lease with a terminal outcome. --outcome is
required:
$ cadence worker complete --outcome success --summary "Task done"
completed--summary <text> adds a short human-readable summary, --details <json> a
structured JSON object. --json emits { state }. This ends the lease; to record
an intended outcome without ending it, use report-outcome.
report-outcome
cadence worker report-outcome records the lease's intended outcome without
ending the lease — the non-terminal counterpart to complete. --outcome is
required; --summary <text> is optional:
$ cadence worker report-outcome --outcome success --summary "Looking good"
success — Looking good--json emits { reported_outcome, reported_summary }.
activities
cadence worker activities records activities on the lease's subject — the task
for a task lease, or the workflow or space for a workflow- or space-subject lease. Pass
them as a JSON array with --activities <json>, or pipe the array on standard
input. The value must be a JSON array:
error: activities must be a JSON array of activity objects.report-usage
cadence worker report-usage reports one reading of a usage counter. All four of
--counter <name>, --amount <n>, --unit <unit>, and --idempotency-key <key>
are required, and --amount must be a non-negative integer. The idempotency key
makes the report safe to repeat: a repeat with the same key records nothing new,
noted on standard error:
$ cadence worker report-usage --counter tokens --amount 1500 --unit token --idempotency-key run-1
usage recordedRepeating it prints usage already recorded (no change).
Attachments from a worker
A worker writes and reads attachments the same way an operator does, but scoped to its own lease and task:
-
cadence worker attach <file>uploads a file as an attachment on the lease.--kind <kind>is required;--content-type <type>defaults toapplication/octet-streamand is never inferred from the file name.--jsonemits the server's record:$ cadence worker attach ./run.log --kind run_logs --content-type text/plain Attached run_logs (14822 bytes). -
cadence worker attach-task <task-key> <file>attaches a file to the lease's own task.--content-type <type>is required. Attaching to any other task is refused as not allowed rather than as a bad token. -
cadence worker list-task-attachments <task-key>andcadence worker get-task-attachment <task-key> <attachment-id>read the lease's own task's attachments — the list, and one attachment's bytes to standard output or to--out <path>.