flintstackbuild-cache daemon · a hosted project
Project status: activeLicense: Apache-2.0Latest: 0.11.3 (2026-09-28)releases feed

Flintstack › CLI reference

CLI reference

Two binaries. flintd is the daemon and takes only startup flags; flint is the client and does everything else. Both accept --help on any subcommand and print the same text that is reproduced here.

Global flags

These work on every flint subcommand and are read before the configuration file, so a flag always wins over a file value.

FlagDefaultMeaning
--socket PATH/run/flint/flintd.sockwhere the daemon listens locally
--config PATH/etc/flint/flint.tomlconfiguration file; missing file is not an error for the client
--timeout SEC5how long to wait for the daemon before falling through
--jsonoffmachine-readable output on stdout, one object per line
-v, --verboseoffrepeatable; twice prints every key input
-q, --quietofferrors only; overrides verbose

flintd

Startup flags only - there is no runtime admin socket, by choice. Everything mutable lives in the configuration file and is re-read on SIGHUP.

FlagDefaultMeaning
--root DIR/var/lib/flintstore root; taken exclusively with a lock file
--listen ADDRdisabledbind the shared cache, conventionally on port 7914
--token-file PATHnoneshared secret for --listen; required with it
--workers Ncores, max 16store writers; reads are not limited by this
--no-evictoffnever delete; the store will fill and then refuse writes
--dry-runoffparse the config, check the store, exit 0 without serving

flint wrap

The only command that belongs in a build. Everything after -- is the real command line, executed verbatim on a miss.

flint wrap [--no-store] [--label TEXT] -- cc -c -O2 -o obj/parse.o src/parse.c

--no-store looks entries up but never writes them, which is what you want on a developer laptop that should consume a shared cache without polluting it. --label attaches a free-form string to stored entries so that flint stats --by-label can attribute hits later; it is not part of the key.

The wrapper is deliberately boring about failure. If the daemon is unreachable, if the key cannot be computed, or if an input disappears mid-run, it runs the real command and exits with the real command's status. The only case where it fails on its own is a malformed invocation, which it reports before starting anything.

flint stats

flint stats [--window DAYS] [--by-label] [--json]

Counters are kept in memory and flushed to the store every 30 seconds, so a hard kill loses at most half a minute of accounting. The rolling window defaults to seven days; values older than the window are aggregated into a single bucket and lose their label breakdown.

flint prune

flint prune [--older-than DURATION] [--to SIZE] [--label TEXT] [--yes]

Eviction normally happens on its own, driven by max_size. Use prune when you need it to happen now - most often after a toolchain change that invalidated a large slice of the store in one go:

$ flint prune --older-than 30d --yes
scanned 18402 entries, removed 2611, freed 3.8 GiB in 4.2 s

Without --yes the command prints exactly the same summary prefixed with would remove and touches nothing.

flint get and flint put

Low-level access by key, for debugging and for scripts that cache something the wrapper does not understand - a linked archive, a generated header, a test fixture.

flint put --key 9f3c1b2e8a7d4056 --file dist/libparse.a
flint get --key 9f3c1b2e8a7d4056 --out /tmp/libparse.a

Keys here are opaque 16-hex-digit strings and are not validated against anything. If you invent your own namespace, make sure it cannot collide with a wrapper key; prefixing your keys with a fixed byte is the cheap way out.

flint key

Prints the key the wrapper would compute, and with --explain every component that went into it. This is the first thing to run when a build misses and you cannot see why.

$ flint key --explain -- cc -c -O2 -o obj/lex.o src/lex.c
toolchain  cc 13.2.0 x86_64-linux  (digest 4e1a…)
argv       -c -O2 -o <OUT> <IN>     (output path normalized away)
inputs     src/lex.c 1f7b…, include/tok.h 93aa…
env        CFLAGS="-O2 -pipe", SOURCE_DATE_EPOCH=1760000000
key        9f3c1b2e8a7d4056

Diffing two --explain outputs explains almost every surprise miss. The usual culprits are an absolute path that leaked into the argument vector and a build timestamp that was never on the allowlist in the first place.

flint doctor

Environment check: binary version, daemon reachability, filesystem semantics under the store root, lock behaviour, clock offset and backend health. It writes nothing and is safe on a busy builder. The install page shows a clean run.

Exit codes

CodeMeans
0success; for wrap, the wrapped command also exited 0
1the wrapped command failed - its status is passed through unchanged
2usage error: unknown flag, missing --, bad duration
3daemon unreachable and --require-daemon was given
4store unusable: root lock held, no space, or a failed semantics check
5authentication rejected by a remote backend

Note that a plain cache miss is not an error condition and never shows up here. If you want CI to fail when the cache is cold, test the hit rate from flint stats --json instead of hoping for a non-zero status.

Last updated: 2026-09-28