User guide
For invited testers. We ran every command on this page against a live server before we wrote it down. If something here does not match what you see, that is a bug. Please tell us.
What this is
Faultline is a place to practise backend and systems engineering. You build real building blocks by hand, such as an LRU cache, a rate limiter, a connection pool, a task queue or a bloom filter. You write them in Go, Python, JavaScript or Rust, with nothing but the standard library. There is no framework to learn and no SDK to import. Faultline runs your solution as its own process and grades it from the outside. It watches what your code really does (a stream of calls, or real sockets) and checks rules over what it saw.
The challenges are in four tracks: Data structures, Load and timing, Data consistency, and Protocols and sockets. The catalog shows a short "Start here" list if you do not know where to begin. Nothing is locked, so you can start anywhere.
Each challenge has a list of stages. Every stage belongs to one of four levels: L1 Correctness (does it give the right answers), L2 Performance (is it close to the speed of our solution in your language), L3 Resilience (does it survive faults such as dropped connections or slow replies), and L4 Observability (do its own numbers match what it really did). Stages run in order. A check stops at the first stage that does not pass and tells you which rule broke.
Getting access
Anyone can browse the catalog, the problem statements and the starters. To check your code and see your results, you need an account. Sign up is open to people with an invite code. The person who invited you sends a sign up link with the code in it (/app/signup?code=…).
- Open the sign up link, choose a name and a password, and create the account. You are logged in right away. We send you an email with a link. Open it to verify your email.
- Your account page shows three set up steps. After you verify your email, create a CLI token there. We show the token only once, so copy it right away. Create one token per computer, and revoke any token you no longer use. Lost a token? Revoke it and create a new one.
You need a verified email to create a CLI token and to run checks. The email comes from no-reply@faultline.build. No email after a few minutes? Check your spam folder, then send a new link from your account page.
Forgot your password? On the log in page, choose "Forgot your password?". We email you a reset link. Using it signs you out on every device. Your CLI tokens stop working too. Run faultline login again.
Old invite tokens still work in the CLI. To use the web pages, turn the token into an account: on the log in page, open "Have an old invite token?". It adds an email and a password to the same account. Your history stays, and the old token keeps working.
In this guide, https://faultline.example.com stands for the real server address. On the web, this page shows the address you are using.
Getting the CLI
The CLI is one program, faultline, for macOS, Linux and Windows (amd64 and arm64). This command installs the macOS or Linux build to ~/.local/bin and checks its SHA-256. You do not need sudo. To install somewhere else, set FAULTLINE_INSTALL_DIR.
$ curl --proto '=https' --tlsv1.2 -fsSL https://faultline.example.com/install.sh | sh
On Windows, run this in PowerShell:
irm https://faultline.build/install.ps1 | iex
It checks SHA-256, installs to %LOCALAPPDATA%\faultline\bin and adds that folder to your user PATH. The learner CLI sends code to the server for grading.
Do you want to see what you run, or download by hand? The current version is in /dl/latest.txt. The files are at /dl/<version>/faultline_<version>_<os>_<arch>.tar.gz (or .zip for Windows), with a SHA256SUMS file next to them. Download a file, compare its SHA-256 with SHA256SUMS (shasum -a 256 or sha256sum), unpack it, and put faultline on your PATH.
Updating faultline
Run faultline upgrade to install the newest version from your server. faultline upgrade --check only checks and installs nothing.
faultline upgrade and the update prompt download from the server you are logged in to. They verify the download with that server's SHA256SUMS file. Only use --api with servers you trust.
Every command that talks to the server also checks for a newer version. In a terminal, faultline asks you once. Press Enter or type y to update, or type n to keep your version. In scripts, it prints a short notice and does not ask. The update checks the download before it replaces the file. Updates need HTTPS, except for a server on localhost. On Windows, the old program is kept as faultline.exe.old until the next start.
Other ways to install
- Build it yourself from the repo, with a recent Go toolchain:
go build ./cmd/faultline - A release archive someone sent you. These are the same files. Check them against
SHA256SUMSin the same way.
macOS: files you get with curl (also through the installer) run as they are. If you download a file in a browser and macOS blocks it, run xattr -d com.apple.quarantine faultline.
Windows: use the native PowerShell installer above. WSL still works with the Linux installer. The -tags dev build with local grading is Unix-only.
You do not need a language toolchain to run checks. Your code builds and runs on our server. A toolchain only helps your editor on your own computer.
One-time setup
$ faultline login --api https://faultline.example.com
get a token at https://faultline.example.com/app/account (create one there, then paste it below).
token: ████████████████████████████████████████
logged in as alice (92af86821b2c8867ebd766d3d06c9704). Saved to ~/.config/faultline/config.json
login tells you where to get a token, reads it without saving it in your shell history, checks it with the server, and then saves the address and the token in your config file. Only you can read that file. After this, no other command needs --api or --token:
$ faultline whoami
alice (92af86821b2c8867ebd766d3d06c9704), joined 2026-07-05
Flags beat environment variables (FAULTLINE_API, FAULTLINE_TOKEN), and both beat the config file. faultline config show prints what is saved, with the token hidden.
The core loop
See what is there:
$ faultline list
Start here: ring-buffer, lru-cache, min-heap, rate-limiter
Data structures (7) Useful, less-known data structures, built by hand.
min-heap in-process 6 stages go, python, js, rust
...
Pull a starter folder, open it, and edit solution/:
faultline pull lru-cache --lang python
cd lru-cache-python
# edit solution/solution.py
faultline check --stage lc1
faultline check
faultline check is the only command that grades your code. It runs the stages in order, from stage 1, and stops at the first stage that does not pass. With --stage lc1 it stops after that stage. Every check is saved. Checks without --seed count toward your progress.
The folder name has the language in it, so lru-cache-go/ and lru-cache-python/ can sit side by side. Use --out my-folder to choose another name. pull does not change files that are already there, unless you add --force. Even with --force, pull never replaces your files in solution/. It saves the new starter file next to yours as <file>.new, so you can compare them.
What is in the folder. README.md, solution/ with your one starter file, and .faultline.json (the challenge and language, so the CLI remembers them). Go also gets a go.mod. Rust, JavaScript and Python also get a small Cargo.toml, package.json or pyproject.toml, so your editor understands the file. The server never reads these files. It always uses its own build setup. Edit only solution/. The code that runs your solution is on the server.
What check sends. Only the source files directly in solution/. A file your .gitignore lists stays on your computer, and check prints a note with its name. It reads the .gitignore files from your git folder down to solution/ and .git/info/exclude. Outside git it reads the one in the pulled folder and the one in solution/. To send an ignored file anyway, add a line like !solution/helper.py. The whole request can be at most 128 KiB.
Stuck? faultline notes <challenge> prints the concept notes, and the challenge's wiki page on the web has the same text. faultline hint (inside a pulled folder) prints one hint for the stage you are on. Add --level 2 or --level 3 for the next, closer hint. Use faultline glossary [term] to look up a word, or leave out the term to list this folder's words (all words outside a pulled folder).
The CLI remembers your folder. Inside a pulled folder (or in solution/, or up to three folders below), faultline check needs no arguments. From somewhere else, give the folder. From the folder that holds lru-cache-python/, you can also give the challenge and the language:
faultline check ./lru-cache-python --stage lc1
faultline check lru-cache --lang python
If you pulled only one language, you can leave out --lang.
You can also write your own tests next to your solution.
Run a check:
$ faultline check
Submitted lru-cache as f055dcea2cadfff348720846f955e2fc
watch it: https://faultline.example.com/app/submissions/f055dcea2cadfff348720846f955e2fc
check f055dcea2cadfff348720846f955e2fc: queued, 0 ahead of you
...
Result: PASS. All 6 stages passed.
The check id and a link come first. Then one status line updates in place: queued, N ahead of you while you wait, then grading…. When the server has timed a few checks, the line also gives a rough wait, for example queued, 6 ahead of you, starts in about 4 min. If others are ahead of you, the CLI says once that the grader is busy. The CLI waits up to 30 minutes (--wait changes this). Ctrl-C stops waiting, and the check goes on on the server. It keeps its place in the queue:
^C
faultline: stopped waiting. Grading continues on the server. Check later with: faultline status f055dcea2cadfff348720846f955e2fc
$ faultline status f055dcea2cadfff348720846f955e2fc # look once
$ faultline status <id> --wait 5m # or wait for the result
The web History page lists every check, with filters and links to each result.
A failed stage looks like this:
✓ L1 lc1 Store and read values PASS
✓ L1 lc2 Make room for a new key PASS
✗ L1 lc3 Keep recently read keys FAIL (0 of 3 runs passed)
✗ Correct answers
should be call #5 Get(2) → not found
yours call #5 Get(2) → value 2, found
try next Count a successful Get as a use. In this example, Put(3, 3) must remove key 2, not key 1.
Result: FAIL. 2 of 3 stages passed. Stopped at lc3.
Run this exact check again: faultline check lru-cache --api https://faultline.example.com --lang go --stage lc3 --seed 703715
This repeats the failing case. It does not update your progress.
Stuck? Try these, smallest help first:
A small nudge: faultline hint lru-cache --stage lc3
The idea behind it: faultline notes lru-cache
A shorter failing case: faultline shrink lru-cache --stage lc3 --seed 703715
Run the same workload again. faultline check --seed N replays one fixed workload and does not count toward progress. The "Run this exact check again" line at the end of a failed result has the full command. When you checked a folder by its path, the line names that folder, so the replay grades the same code. Copy it to help find the bug. The replay is saved in History. When the replay passes, run faultline check with no --seed to check fresh cases and save your progress.
faultline shrink asks the server for a shorter failing example. Use the --stage and --seed from the "Run this exact check again" line. The web result page has the same thing as a button: "Find a shorter failing sequence".
The web UI
The same data, in a browser, at the server address. Log in once (top right). When you are logged in, the menu shows History and your account.
- Home (
/app/): what Faultline is, the stages and levels, an example, and the way in. - Challenges (
/app/challenges): a "Start here" list, then the four tracks. Each row shows a one-line summary, the number of stages, the difficulty, the levels and the languages. You can filter by name, type, language and level. - A challenge (
/app/challenges/lru-cache): the problem, every stage, hints, the concept notes, and the commands to get started, each with a Copy button. - A result (
/app/submissions/<check-id>): the result of one check. While it is still grading, the page updates every 2 seconds, then every 10 seconds after
10 minutes. It also shows a rough wait when the server has one. Only the account that ran the check can see it.
- History (
/app/history): every check you ran, with filters and sorting. - Account (
/app/account): the set up steps, your email status (and a way to send the link again), CLI tokens (create, see when each was last used, revoke), your password, and log out.
Reading a result
Start with the first stage that did not pass. Compare "should be" with "yours", then fix one thing. Read Reading a verdict for the result names and what to do next. Read How grading works for the kinds of stages.
CLI exit codes, for scripts: 0 pass (or a status look at a check that is still grading), 1 a graded result that is not a pass, 2 a network, usage or grading machine problem (or a --wait that ran out of time), 130 you pressed Ctrl-C while a check was still grading.
Languages
You can solve challenges in Go, Python, JavaScript or Rust (go, python, js, rust. Names like javascript and py work too). Most challenges support all four. A challenge's page and faultline list show which ones.
Every language gets every stage, also L2 (you race our solution written in your language). If a challenge does not support your language, pull says so and lists the ones it does support:
$ faultline pull connection-pool --lang rust
faultline: api 400 Bad Request: language "rust" is not supported by connection-pool (supported: go, python, js)
FAQ
Can I use packages, crates or modules from the internet? No. Use the standard library only, in every challenge. Building it yourself is the exercise. Your code builds on our server with no network, so an import that needs the network does not build.
Can I print debug output? Use stderr. For most challenges, stdout is how your program talks to the grader. Stderr never changes your grade, and the last lines of it show in your result under every failing stage (in the CLI and on the web), with a crash's stack trace. Both outputs have a size limit of a few MB. A program that prints without end gets stopped.
My check seems stuck. faultline status <id> shows queued or grading… and how many tries it took. A check usually takes seconds. L2 stages take tens of seconds. If something breaks on our side while we grade, we try again. That never counts against you. If it stays stuck for minutes, tell us. That problem is ours.
I lost a check id. The History page lists all of them. The CLI also prints the id right away, before it starts waiting.
Why did L2 fail when my answers are right? L2 compares your speed with our solution in the same language. The result shows your time and the limit, for example "2.4× our solution's time (limit 2.0×)". If your code got faster by skipping work, L2 notices that too.
What does "(2 of 3 runs passed)" mean? We run some stages several times. If every run passes, the stage passes. If every run fails, it fails. If some pass and some fail, the result is FLAKY. That almost always means a real bug, such as a race.
Windows? Use the PowerShell installer above. WSL with the Linux build also works.
Can I check as often as I like? Yes. A check stops at the first stage that does not pass, so a broken solution is quick to check.
Feedback
Tell us what was hard or confusing: a statement, a result that seems wrong, or this guide. Use the Feedback link at the bottom of every page. If it is about a check, add the check id, so we can see what you saw.