The saggar CLI¶
saggar (the saggar-cli crate) is the client: it drives
saggar.dev over its REST API. One page, every command.
$ saggar <command> [flags]
Connection settings¶
Every subcommand takes the same two connection flags:
--server <url>Base URL of the instance to talk to. Resolution order: this flag, then
SAGGAR_SERVER, thenserverin the client config file, then the default instancehttps://api.saggar.dev.--token <secret>API token. Resolution order: this flag, then
SAGGAR_TOKEN, thentokenin the client config file, then none; a command that needs auth then fails with 401.
The client config file is ~/.config/saggar/cli.toml
($XDG_CONFIG_HOME honored; $SAGGAR_CONFIG points at an
explicit file, which must exist):
server = "https://build.example.com:7020"
token = "sgt-…"
Unknown keys in the file are refused: a typo must not silently run
against the wrong server. saggar login (see Sign the CLI in)
writes this file for you.
Exit behavior: 0 on success, 1 on any error, including a
failed build followed with submit, and any failed job of a group.
saggar submit¶
$ saggar submit <source> [flags]
Submit a source and follow the build.
<source>A git URL (
https://,http://,ssh://,git://or theuser@host:pathspelling; remote URLs only), a local directory, or a path to a tarball. Local trees travel as uploads; the service never reads your filesystem.
Flags:
--format <format>Explicit format (
deb,docker,native, …); auto-detected from the tree by default.--name <name>Project name override (derived from the tree when absent).
--project <slug>|<owner/slug>Which project to submit into: a slug you own (created on first submit) or
owner/slugfor someone else’s (builder standing required). Default: yourpersonalproject.--arch <arch>Target architecture (
amd64,arm64, …). Without it the job names no arch; the queue prefers a runner whose arch matches.--arches <list>Arch fan-out (comma list, e.g.
amd64,riscv64): with a formats selector the group runs one job per format × arch. Mutually exclusive with--arch.--env <ref>Build environment: an OCI image ref native/node builds run in, or a fingerprint (
debian:12@amd64/glibc-2.36) the build machine resolves to an image.--ephemeralEphemeral build: the artifact is excluded from the registries and collected after the instance’s ephemeral TTL (24 h by default). For check-builds (agents, quick verification) that must not pollute the store.
--testRun the tree’s own test suite instead of building (see Run a project’s tests).
--test-cmd "<command>"Explicit test command; implies
--testand overrides the autodetected suite.--formats <auto|list>Formats selector (see Fan out: formats and architectures):
autofans out to every claimant, or a comma list (deb,docker) pins the set. The response carries a group id; the whole group is followed and the command fails if any job does.--no-reuse-buildDisable the build-once hand-off: every job of the group gets its own fresh tree again.
--cache <mode>Cross-job reuse:
incrementalkeeps the build tree between submissions (only what changed rebuilds);artifactreuses the stored artifact of an identical previous build (instant green). Default: fresh. A memo lookup never hits on runner-backed builds;incrementalis the one that pays there.--incrementalShorthand for
--cache incremental.--cachedShorthand for
--cache artifact.--no-waitDo not follow the build; exit right after submission.
--quietPrint only the job (or group) id, machine-readable; implies no waiting.
While following, the CLI streams the log and prints the verdict. On
success it names the artifact route (saggar download, the
registries); on failure it prints the outcome and the failure class
(infra-retryable, infra-permanent).
saggar download¶
$ saggar download <reference> [flags]
Download the artifacts of a job (j-…) or artifact (a-…).
<reference>A job id (downloads the job’s artifact) or an artifact id (downloads every file of that artifact).
-o, --out <dir>Output directory. Default: the current directory.
--extractExtract
.tar.gzartifacts into<out>instead of saving them, preserving executable bits (entries that would escape<out>are refused).--arch <arch>Target arch to fetch. Defaults to this machine’s arch: job downloads give you what runs here. Arch-independent (
all) artifacts always come along. Explicit artifact ids are never filtered.--group <group-id>Fetch every job of a fan-out group instead of one reference (mutually exclusive with a reference). A group member that produced no artifact (build-check success) is reported and skipped; any real failure exits
1.
A job that has not succeeded is an error naming its state and outcome.
saggar logs¶
$ saggar logs <job-id>
Print a job’s full log as plain text. For live streams, the SSE
endpoint GET /api/v1/jobs/:id/logs is the interface (see
The management API); submit’s follow loop already shows the log as the
build runs.
saggar cancel¶
$ saggar cancel <job-id>
Cancel a job (queued or running). A queued job is cancelled outright;
a running build stops at the next command boundary, and host-executed
children are killed immediately. The job ends cancelled: terminal,
log kept, no failure metadata.
saggar login¶
$ saggar login [--server <url>] [--token <sgt-…>]
Sign the CLI in and store the credentials (see Sign the CLI in). Two modes:
Browser (default): starts a device login. The CLI prints and opens the instance’s verification page (
https://saggar.dev/deviceon the default instance) where the code is typed, then long-polls until the code is confirmed and stores the minted token. The typed code is normalized server-side: case, dashes and spaces do not matter.Headless (
--token): validates the token against the server (GET /auth/memust answer) and stores it in~/.config/saggar/cli.toml. A rejected token writes nothing.
Both accept --server; without it, the default instance
(https://api.saggar.dev) is used.
saggar whoami¶
$ saggar whoami
Show who the current token authenticates as: handle, name, email, provider, storage usage against the account’s artifact quota, and the projects the token can see with their roles. The first command to run after wiring a fresh token into the config.
saggar token¶
Manage your API tokens, the long-lived secrets the CLI and CI hold. Mint one per machine; revoke it when the machine dies.
$ saggar token create <name> [--expires-days <days>]
$ saggar token ls
$ saggar token revoke <id>
token createMint a new token; the secret (
sgt-…) is shown exactly once.--expires-dayssets an expiry; omit it for a non-expiring token. Minting needs an existing credential: bootstrap happens throughsaggar loginor the web app, never through the CLI alone.token lsList your tokens; secrets are never shown again.
token revoke <id>Revoke the token with the listing’s id (
t-…): it stops authenticating immediately.
saggar projects¶
Manage your projects and their git remotes.
$ saggar projects ls
$ saggar projects create <slug>
$ saggar projects remotes <project>
$ saggar projects remote add <project> <url>
$ saggar projects remote remove <project> <url>
lsThe projects you own or are a member of, with your role.
create <slug>Create a project you own. The slug is lowercase letters, digits and dashes.
remotes <project>List the project’s remotes: its git mirrors, the URLs git submissions derive their project from.
remote add <project> <url>Register one remote (owner; idempotent). The same URL on two forges is one project with two remotes. Registering a watch adds its URL as a remote too; a remote in use by a watch cannot be removed.
remote remove <project> <url>Forget one remote (owner; refused while a watch of the project polls that URL).
<project> is owner/slug, or a bare slug of your own.
saggar watch¶
Register a repo once and every push builds itself (see Set up CI: watched repos, webhooks, statuses).
$ saggar watch add <url> [flags] [--disabled]
$ saggar watch ls
$ saggar watch show <id>
$ saggar watch edit <id> [flags]
$ saggar watch rm <id>
$ saggar watch trigger <id>
add <url> [flags]Register a repo: every push to a matching branch or tag builds with the given defaults (and runs the tree’s tests, by default). One watch per repo.
--disabledregisters dormant: nothing triggers until it is enabled.lsThe watches you can see: id, URL, project, state.
show <id>One watch in full: patterns, settings, interval, enabled, last poll.
edit <id> [flags]Edit a watch: only the flags you pass change.
rm <id>Remove a watch. Its build history stays; re-registering the repo starts from a clean slate and re-triggers the current head.
trigger <id>Poll right now, whatever the interval says; also how you test a fresh registration. Prints what triggered (nothing new → exit 0 with a “no changes” line).
The flags shared by add and edit:
--branch <glob>Branch glob pattern, repeatable:
main,release/*, or a fullrefs/heads/...form. Default: every branch.--tag <glob>Tag glob pattern, repeatable:
v*. Default: no tags.--formats <auto|list>Formats selector for triggered builds. Default: the single-winner build.
--arch <list>Comma list of target arches (
amd64,arm64): one build per arch. Default: no arch named; the queue prefers a runner whose arch matches.--env <ref>Build environment for triggered builds.
--cache <mode>Cross-job reuse for triggered builds:
incrementalkeeps the build tree between pushes. Default: fresh. (artifactis refused: memoization does not compose with groups.)--ephemeral/--no-ephemeralTriggered artifacts are ephemeral / kept permanent.
--test/--no-testTests only (pushes run the suite, no artifact builds) / builds only (undoes the build+test default).
--test-cmd "<command>"Run this command as the test suite instead of the autodetected one (implies the test member on pushes).
--interval <secs>Poll interval override; five minutes otherwise.
--project <slug>|<owner/slug>Which project the watch belongs to and its builds are attributed to. Default: your personal project.
On add, an absent flag means the server default; on edit,
absent means unchanged. There is no credential flag: polling
authenticates through the host’s git/ssh configuration; the forge
credential for commit-status reporting is set through the API (see
The management API).
saggar node¶
Manage the runners of your account: the roster, and retirement.
$ saggar node ls
$ saggar node retire <node-id>
lsThe roster: the runners you own or are shared with, each row carrying node id, arch, engine version, state, declared conduct profile (
network,dockercapability) and owner.retire <node-id>Retire a runner you own: its token stops authenticating immediately. The row stays listed
retiredfor the record. Not idempotent: an unknown or already-retired node is an error, not a silent no-op. Non-owners are refused.
Sharing a runner with another account is a management-API call (PUT
/api/v1/nodes/:id/shares, see The runner protocol) or a flip in the
web app; the roster shows the sharees’ runners beside your own.