CLI reference
sboot — learn by building. One static binary. The graded commands run from
anywhere inside your course repo — it finds the workspace by walking up to
sboot.toml — and the rest (courses, login, start, resume, bare
sboot) work from anywhere at all.
sboot where you are and what's next
sboot start [course] create a course workspace and its git repo, named
after what you finish with (./word-game-sb/)
sboot test [stage] practice: grade the current lab locally
sboot hint [stage] [check] a hint for a failing check — one hint deeper per run
sboot explain [check] the AI tutor, fed your last run's evidence
sboot submit [stage] official: check locally first, then upload for the
server grade
sboot courses the catalog + your progress
sboot login connect this machine to your account
sboot repo put this workspace on GitHub — a private repo, pushed (via gh)
sboot resume <path|url> pick a course back up on this machine (clones a git
URL first)
That is sboot help. sboot help --all adds the rest — reveal, logout,
whoami, debug, fetch, where, version — plus every flag and
environment variable.
The stage argument is optional
test, submit, hint and debug all default to your current lab: the
first live lab the server has not recorded as complete (server truth when
online, the cached answer offline). The choice is never silent — the command
prints it, and names the override:
grading 02-glass-cockpit — "The glass cockpit" # your current lab; sboot test <stage> overrides
An explicit stage always wins, and must start with its lab number:
02-glass-cockpit, or just 02; not glass-cockpit.
Commands
sboot (no arguments)
The orientation screen, exit 0 in every state. Inside a workspace: the course,
every lab with its state (✓ verified · ▸ current · locked · not published
yet), your last practice score on the current one, and the next action.
Kernel in Rust: Own the Machine — kernel-in-rust · 0 of 2 live labs verified
▸ 00 welcome up next
01 first-light locked until 00 completes
⋮ (24-lab arc — labs open in order)
next: sboot test # grades 00-welcome
stuck: sboot hint · read: https://sourceboot.com/courses/kernel-in-rust/stages/00-welcome
Outside a workspace it prints who you are connected as, the courses you have started, and how to start another. Offline it answers from the last sync and says so.
sboot start [course] [--dir NAME]
Creates the workspace with the course scaffold: your source tree, the pinned
build config, a README, an MIT LICENSE, a .gitignore, and sboot.toml (the
file that tells sboot which course the repo is for). In the same run it
fetches the first lab's tests and the grading engine into the SourceBoot data
directory — outside your repo — and ends by naming the first lab, the command
that grades it, and its page on the site.
The folder is named after what you finish with, not after the course id:
word-game-sb for Rust for Beginners, kernel-sb for the kernel courses,
sqlite-reader-sb for Rust for Systems. A course that names no such artifact
falls back to its course id (os-rust). --dir NAME overrides either.
It is local-only — it never speaks to GitHub, so it cannot fail there.
After unpacking it runs git init inside the new folder, asks once for the
name and email git stamps on commits if you have not set them anywhere (saved
--global, so no course asks again), makes the first commit, and prints one
line: "when you want it on GitHub: sboot repo." With no terminal to ask on,
it leaves the commit undone rather than guessing an identity, and prints the
three commands that finish it.
Run it again and it repairs. In or beside a workspace for that same course
it re-fetches the scaffold, copies across only the paths that are missing,
and prints what came back — a file you have edited is not compared, not backed
up and not touched. A directory that is not empty and not one of ours is still
refused, naming both ways out (--dir, and sboot resume).
With no argument — and no SBOOT_COURSE in the environment — it prints the
catalog and how to pick, and creates nothing.
sboot test [stage]
The practice loop: resolve the lab's tests (fetching them on first use), run
the course's build, run the result, grade it against the lab's published
checks. Build output streams live; the verdict is [PASS]/[FAIL] per check
with points and evidence, then score: N/M. When you are online the run is
recorded to your account as a practice run — the score and per-check
verdicts, never your source — and shows up on /home and on the lab's
page. Practice never completes a lab, and an offline or unrecorded run costs
you nothing.
A failing run ends by pointing at both help surfaces: sboot hint, and the
lab page's #stuck section.
sboot hint [stage] [check-id]
A hint for a failing check, on courses that publish hints. Bare sboot hint
targets the first check your last sboot test recorded as failing (so run
sboot test first); naming a check id targets that one. Each ask for the same
check goes one hint deeper — the header counts them,
[hint 1 of 2 · <check-id>], and the last one is the deeper debug ladder — and
it only ever goes forward. It works offline: it reads the hints shipped
with the lab's tests and your own local state. A
course with no published hints says so and exits cleanly; so does a check that
is already passing.
If your last sboot test stopped at the build, no check ran, so sboot hint
works from the compiler's first error instead. The first ask quotes that
error (its code, message and file:line). Each later ask for the same error
goes one hint deeper, into what the lab's hints say about it: the likely cause
behind that error code, then the lab's compile hints. A different first error
starts again at the top. When no written hint covers the error, it says so and
still quotes the error.
When you are online, each hint shown sends one line of usage data, so we can
see where people get stuck: the course, the lab, the check (or build), how
deep the hint was and which kind it was. Never your code, your compiler's output
or a file path. SBOOT_OFFLINE=1 sends nothing.
The last written hint hands off to the next step up the ladder — sboot explain, and past that sboot reveal — and to the lab page's Stuck?
section, which mirrors the same ladder and hosts the chat.
sboot explain [stage] [check-id] [--here] [-m "question"]
The step above the written hints: the AI tutor, with your last run's evidence
already attached. Bare sboot explain targets the same check sboot hint
would.
By default it prints — and opens, where a browser is available — the lab page's Stuck? chat with your check in the link. The chat lives there because it keeps context across turns and can render code.
--here answers in the terminal instead: one question, one streamed
reply, no browser. -m / --message supplies your own question; without it
the CLI asks for direction on the check you are stuck on. What it sends is
your question plus what your last sboot test recorded — the lab, the failing
check ids and the score, or the reason a run never reached a check. It does
not send your source.
The tutor is metered per day by plan. When the allowance is spent, the refusal
says so with the numbers and points back at sboot hint, which is never
metered. explain needs the network in both halves; the hint ladder does not.
sboot reveal [stage] [--yes]
The end of the ladder: the course's own solution for a lab, in two steps — the module skeleton first, and the full reference only on a later run. The server decides whether a lab can be revealed and in which order; the CLI asks first and tells you the price before anything is fetched.
A reveal marks that lab solution-assisted on your record. The mark is an annotation on your own progress record — it never blocks completing the lab, and it changes no score. You are told this in the confirm prompt, before the solution arrives, and again after.
The files are printed with a header each and saved beside your run (under the
scratch directory sboot where prints), never into your own tree — it is
a copy to read next to your code, not a patch applied to it. --yes answers
the prompt for a non-interactive shell; without a terminal and without
--yes, reveal refuses rather than reveal silently.
reveal is online by nature: the solution is fetched when you ask for it and
is never shipped with the tests, so offline it refuses — and says plainly that
nothing is wrong with your setup.
sboot submit [stage] [--force]
The official grade, in four moves: ask the platform whether the stage is open
to you (before spending a build on it) → run the same local check sboot test runs — one build, one run → on a local pass, upload that run's captured
result plus your source tree (source only: target/ and .git are
excluded) → our server judges it and answers with the verdict in the same
command.
A passing submission is submitted for review, not finished: the last line is a link to that submission's review page, and the Complete button there is what records the lab and unlocks the next one.
A local fail stops the submit: nothing is created, nothing is sent.
--force / -f submits anyway after a failed local run — for when you
think the local grader is wrong, or you want the failed attempt on the
record. What --force cannot do is submit with no local run at all: since
the official grade is computed from the run your machine makes, a machine
that cannot run sboot test cannot submit either.
sboot courses
The catalog and your progress across it — started courses first, then the rest with their live-lab and free-lab counts. Cached on every run, so it keeps answering offline after one sync; it says when what you are reading is the cached copy.
sboot login
Connects this machine to your account. It asks the platform for a pairing
code, prints the code and an /account/connect link (and opens it if a
browser is available), and waits; one click in the browser you are already
signed in with hands the CLI a token, which it stores 0600 in its own config
directory. Nothing is copied and pasted.
SBOOT_TOKEN in your environment always overrides the stored login — that is
the fallback path for CI and headless machines, and sboot login says so if
the variable is set. A stored login for a different platform than the one you
are talking to is reported and not used.
sboot logout
Removes the stored login from this machine. If SBOOT_TOKEN is also set it
says so — that one still works until you unset it.
sboot whoami
Asks the platform who the current credential resolves to, and prints the
handle plus which source the token came from (SBOOT_TOKEN, the stored login,
or the local development default). It is a live check, not a read of the file:
a revoked token answers honestly.
sboot repo [--name NAME]
The remote, and only the remote — sboot start has already made the local repo
and its first commit. The default repo name is the folder's:
word-game-sb, kernel-sb; a course that names no artifact still gets the
signature on GitHub (os-rust-sb), and --name overrides. It is
GitHub's own CLI that does the work, so we hold no
credential at any point, and nothing in the course depends on running this.
What it does depends on what your machine has:
ghinstalled and signed in — it names the account and the repo, asks once, and runsgh repo create <name> --private --source . --push. On success:✓ pushed → github.com/<you>/<name>.ghinstalled, not signed in — it runsgh auth loginfor you, as a child of this terminal (that is gh's own browser flow, storing gh's own credential), then carries straight on to the create and push.- no
gh— two ways forward. Either the install line for this machine — checked, not assumed:brew install ghwhere brew really is installed (Homebrew's installer first where it is not),sudo apt install gh/sudo dnf install ghby which one you have,winget install --id GitHub.clion Windows — which you run yourself, since it wants an admin password we should never be prompting for; or the manual path: a prefilledgithub.com/newlink, thencdinto the workspace first,git remote add origin …,git push -u origin main, plus the sentence that decides whether that push works — GitHub will ask for a token, not your password — and a link to GitHub's own page about making one.
It never touches an existing remote: with one set it says so and stops. Nothing here is a prerequisite for anything, and declining costs nothing.
While a workspace has no remote, one line points at this command — after a
passing sboot submit, when there is finally something worth showing, and on
sboot test at most once a day:
your work is committed here but has no home on GitHub yet — sboot repo
It is silent when git is missing, when there is no repo, and forever once a remote exists.
sboot resume <path|git-url>
Picking a course back up on a machine that has never seen it — a new laptop, a
rebuilt one, a clone of the repo you pushed. Given a git URL it clones first
(plain git clone, with whatever credential your machine already uses), into a
folder named after the repo; given a path it takes the workspace as it is.
Then, in order: check that it really is a workspace (sboot.toml), fetch the
course's tests and the grading engine — the half a clone is always missing,
because they were never in the repo — and grade the first lab. That last
step is the point: the question on a rebuilt machine is not "are the files
here" but "does this still work", and only a verdict answers it. The command's
exit code is that lab's.
It ends by naming your workspace and sboot test, and offers sboot repo if
this copy has no remote. A directory with no sboot.toml is refused — and if
exactly one workspace sits one level down, the refusal names it.
sboot where
Prints where everything lives: your repo, the cached tests and their spec version, the grading engine (a separate, checksum-verified program fetched with the tests), the course's build tooling, the scratch directory grading runs in, and your state file. Reads only local state — works offline.
sboot debug [stage]
Boots the stage under QEMU, frozen, waiting for a debugger on :1234 —
attach gdb and go. Interactive and long-running; nothing is graded or
recorded. It hands off to the course's own build tooling, so it works on the
courses whose tooling has a debug mode (os-rust, x86-64-essentials) and
not on those that don't (kernel-in-rust).
sboot fetch [course]
Downloads the current lab's tests deliberately — to refresh them, or to warm
the cache before losing your connection. sboot test already fetches what it
needs on its own; fetch exists for doing it on purpose. Prints the spec
version, how many labs are cached, and where.
sboot version
Prints the version (also --version).
Flags
| Flag | Where | What it does |
|---|---|---|
--force, -f |
submit |
Submit even though the local check failed. |
--here |
explain |
Answer in this terminal instead of opening the lab page's chat. |
--message, -m |
explain |
Your own question, instead of the one the CLI composes. |
--json |
bare sboot, test, submit |
Machine-readable object on stdout; the human rendering moves to stderr. |
--yes, -y |
start, repo, reveal |
Answer yes to every prompt. (Never the identity question: an email cannot be guessed.) |
--dir |
start |
Unpack into this folder instead of the course's own. |
--name |
repo |
Create a repo of this name instead of the default. |
--no-color |
anywhere | No ANSI colour (NO_COLOR is honored too). |
--help, -h |
anywhere | The short help; sboot help --all for everything. |
-- |
anywhere | End of options — everything after it is positional. |
Environment variables
| Variable | What it does |
|---|---|
SBOOT_API_URL |
Platform URL. Default https://sourceboot.com. |
SBOOT_TOKEN |
Your API token. Overrides a stored sboot login. The built-in default is a local development value that works against no real deployment. |
SBOOT_COURSE |
Course id for sboot start/fetch outside a repo. Inside a repo the course comes from sboot.toml. |
SBOOT_COURSE_DIR |
Your course repo, if you don't want the walk-up-from-cwd lookup. |
SBOOT_CACHE_DIR |
Where tests + grading engine are cached. Default: the OS data dir (~/Library/Application Support/sboot on macOS, ~/.local/share/sboot on Linux). |
SBOOT_STATE_DIR |
Where the stored login and state.json (how deep you are in each check's hints, last verdicts) live. Default: the OS config dir. |
SBOOT_OFFLINE |
Set to anything to skip every network call. Cached tests still grade. |
SBOOT_NO_UPDATE_CHECK |
Suppress the new-version notice line. |
SBOOT_DEBUG |
Print the CLI's own diagnostics on stderr. |
The install script additionally reads SBOOT_INSTALL_DIR (default
~/.local/bin) and SBOOT_VERSION (install a specific release — see
Troubleshooting).
Exit codes
- 0 — success: full marks on a graded run, or the command did its job.
- 1 — a graded run that ran and came up short: the build failed, or one
or more checks failed (including an official grade below full marks). Also
the smaller "asked and answered no" cases —
whoamion a credential the platform rejects,hinton a check nobody has written one for. - 2 — the run never got to a verdict: usage error, no workspace, missing
tool, a lab that is not open to you, a refused submission,
hintbefore anytest, or a platform problem.
The distinction that matters: sboot separates your code failed (exit 1)
from something else is wrong (exit 2), so a toolchain problem is never
recorded as a bad practice run.
Offline
sboot test and sboot hint work with no network at all once a lab's tests
are cached — grading is local. Bare sboot and sboot courses answer from
the last sync and label it. What you lose offline: fetching new labs, the
practice-run record (the run still grades; it is just not recorded — that's
fine, it's practice), the one-line hint-usage record sboot hint sends,
sboot login/whoami, sboot explain --here and
sboot reveal (both run on our side by definition), and sboot submit, which
by definition needs the server. SBOOT_OFFLINE=1 makes the no-network promise
explicit: every network call is skipped, including the update check.
Update notices
When a newer release exists, sboot prints one line after the verdict, at
most once a day:
sboot 0.11.0 → 0.12.0 available · update: curl -fsSL https://sourceboot.com/install.sh | sh · notes: github.com/sourceboot/sboot/releases
Updating is re-running that installer, which replaces the binary in place.
There is no sboot upgrade verb — the install command is the whole mechanism.
SBOOT_NO_UPDATE_CHECK silences the line; SBOOT_OFFLINE silences it too.
On Windows the line names the Windows way instead, because there is no shell
script to pipe: download sboot-windows-amd64.exe from
github.com/sourceboot/sboot/releases and replace your sboot.exe.
A second kind of line shares that channel: a notice from the platform — a renamed course, say. Each distinct notice prints once and then waits a day before repeating, so a standing announcement never rides along on every command; one you have not seen yet is never held back.
"Your CLI is too old for this course's tests"
Lab tests are versioned, and a course can declare a minimum CLI version for
them. If your binary is older, sboot refuses to grade rather than guess:
02-glass-cockpit's tests need sboot 0.3.0 or newer — this is sboot 0.2.0.
Update: curl -fsSL https://sourceboot.com/install.sh | sh · notes: github.com/sourceboot/sboot/releases
Nothing is wrong with your code. This binary would have to guess at part of
these tests, and a guessed grade is worse than none
The fix is the printed install command. Offline this rule cannot fire: an old binary keeps grading against the spec it already has, which is a matched pair. To install a specific version instead of the latest, see Troubleshooting.
One thing an old binary gets wrong, and cannot be patched to stop getting
wrong: a sboot from 0.3.x or earlier prints stage complete! on a
passing submit. It isn't — since the review flow landed, a pass is submitted
for review and the lab is recorded when you press Complete on the review
page. If you see that line, update; either way the site is the truth about
what you have completed.