Use the binary cache
Every merge to main pushes built binaries for x86_64-linux and
aarch64-linux to the public Cachix cache scoot-sh. Without it, Nix
compiles Smithay and scoot’s crates on your machine (minutes); with it,
you download. Opt in once:
Use it
Section titled “Use it”NixOS (configuration.):
nix.settings = { extra-substituters = [ "https://scoot-sh.cachix.org" ]; extra-trusted-public-keys = [ "scoot-sh.cachix.org-1:QMj7CMw8uqZxrvqqm6SggdxTHz6Q4prt30ydDcXJXCo=" ];};Home Manager (your home config; it writes the same nix. for your user):
nix.settings = { extra-substituters = [ "https://scoot-sh.cachix.org" ]; extra-trusted-public-keys = [ "scoot-sh.cachix.org-1:QMj7CMw8uqZxrvqqm6SggdxTHz6Q4prt30ydDcXJXCo=" ];};Per user without the modules (~/.), the same two lines:
extra-substituters = https://scoot-sh.cachix.orgextra-trusted-public-keys = scoot-sh.cachix.org-1:QMj7CMw8uqZxrvqqm6SggdxTHz6Q4prt30ydDcXJXCo=Then check it worked:
nix show-config | grep -E "substituters|trusted-public-keys"Non-interactive (scripts, containers): the flake itself carries these two
values as nixConfig, and Nix asks whether to accept them on first use.
Answer once, or pass the flag (--accept-flake-config skips the prompt):
nix run --accept-flake-config github:scoot-sh/scoot#scootbar -- --versionThe flake’s nixConfig applies only to trusted users. The system-level
nix. lines above are the reliable path. A per-user nix. is
not enough on a multi-user (daemon) install: Nix ignores
trusted-public-keys from an untrusted user (“ignoring the client-specified
setting … you are not a trusted user”), so add the key to the system
config (or make yourself a trusted-users entry), whichever you prefer
to trust.
What you are trusting
Section titled “What you are trusting”Binaries built by CI from reviewed merges to main, verified by this
key:
curl -s https://app.cachix.org/api/v1/cache/scoot-shThe publicSigningKeys it reports must equal the key in the config
above. A substituter can serve any store path your Nix asks for, so this trusts
CI’s builds the way installing the flake already trusts its source. The
signing key is what stops a cache-only compromise from doing more than serve
stale or signed paths or go away; whoever holds the key can make your Nix
accept arbitrary binaries. Cachix holds the signing key; the project holds
no private key, and the only CI secret is the push auth token. That is why
you check the public key above and why the rotation runbook below exists.
Only trusted-users (root, and NixOS’s trusted-users list) may add
substituters through the flake’s nixConfig; everyone else opts in
through the system config above, which is the auditable path.
What the cache holds
Section titled “What the cache holds”All four installable outputs on both Linux systems, as of main:
| Package | x86_64-linux | aarch64-linux | Download |
|---|---|---|---|
scoot |
cached | cached | ~3.7 / 3.4 MB |
scootbg |
cached | cached | ~0.9 / 0.8 MB |
scootbar |
cached | cached | ~1.2 / 1.1 MB |
scootbar-demo |
cached (tiny wrapper) | cached (tiny wrapper) | under 1 KB over scootbar |
The CPU-only minimal build is cargo-only
(cargo build --no-default-features) and is not cached: there is no
published minimal package to push.
Not cached: the docs site (built where it deploys, never
installed), the dev shell (contributors realise it from nixpkgs plus the
toolchain), and macOS builds (macOS CI is check-only, so the Darwin
client is never pushed — install it from source or take the Linux
binaries). The bar’s own closure stays small: seven paths, 48.5 MB total
on x86_64-linux, with no font inside (only scootbar-demo carries
one).
One follows warning: the flake pins its own nixpkgs revision, and
Cachix holds binaries built from exactly that revision. With
scoot., every store path differs,
the cache misses, and you compile locally. Leave follows off unless
you mean to pay that cost. (More in the desktop
page.)
Cold versus cached
Section titled “Cold versus cached”A fresh empty store on an Asahi M2, asking only for the bar:
nix --store ~/fx/cache-demo/store run github:scoot-sh/scoot#scootbar --accept-flake-config -- --versionprints scootbar 0. in 14.6 seconds with 7 paths fetched (1.1 MB
download, 58.6 MB unpacked); the bar itself came from
scoot-sh. and nothing compiled. Current CI on a
code-change push takes ~19–21 minutes per architecture (317
dependencies substituted, then the changed crates compiled), while a
push with nothing to rebuild substitutes everything in about a minute
per architecture (58–76 seconds per job, 3m18s end to end).
Symptom: it is compiling anyway (Smithay scrolls past for minutes). The cache only has
main’s exact store paths, so any difference misses. Diagnose in order: is the cache configured at all, did the flake prompt get refused, isfollowson, is your tree ahead ofmain, or is the package one CI never pushes.
Check the config is active:
nix show-config | grep -E "substituters|trusted-public-keys"- Empty, or missing
scoot-sh: the two config lines are not active. Re-check which file you edited and rebuild or restart the shell. - The flake asked about
nixConfigand you said no: re-run with--accept-flake-config, or use thenix.lines so no prompt is needed.settings followsis on:scoot.changes every store path (see above). Turn it off or accept the compile.inputs. nixpkgs. follows = "nixpkgs" - A local change, or a commit ahead of
main: your tree’s store paths do not exist yet — they land on the cache with the next merge tomain. - macOS, or the docs site: not pushed by CI (see the table), so they build locally.
To confirm a single path is cached without downloading it:
curl -s https://scoot-sh.cachix.org/HASH.narinfo | head -5A StorePath: reply means cached; 404 - Not Found means it is not.
Replace HASH with the store path’s hash (the first component of
/nix/store/HASH-name).
For maintainers: pushes, trust boundary, rotation
Section titled “For maintainers: pushes, trust boundary, rotation”Pushes come only from trusted CI. .
triggers on push to main and manual workflow_dispatch only —
never on pull_request, so fork runs never reach it. The authenticated
Cachix step runs only when the event is a push to refs/heads/main
and is the sole step that receives secrets. (an org
secret scoped to this repo); every other run takes the pull-only step,
which passes no token and references no secret, so a dispatch run never
receives it. Signing is Cachix-managed: the project holds no private
key. The FlakeHub publish job runs only after both architectures push,
on push to main, over GitHub OIDC with no secret, and publishes
the flake only, never binaries (include-output-paths: false).
What a poisoned entry would require: a push to main (a reviewed merge,
or a writer pushing directly) containing the malicious build, since
only that path holds the token. The residual boundary is GitHub’s
standard writer-trust one: a writer could push a branch with the
workflow’s own gates edited — but dispatching that branch still takes
the token-less step, so it verifies without publishing. Closing the
writer path further would take a protected environment with required
reviewers on the push path; deliberately not added.
Rotation runbook (no rotation has happened yet):
curl -s https://scoot-sh.cachix.org/nix-cache-info- Auth token suspected or staff changed: rotate it in the Cachix
dashboard (cache settings), then replace the
CACHIX_AUTH_TOKENorg secret. The next merge tomainproves the new token; a manual dispatch before that proves the build without pushing. - Signing key rotation: rotate in the Cachix dashboard, then update
the public key in
flake.(nix nixConfig), this page, and Install in one PR. Keep the old key inextra-trusted-public-keysalongside the new one until every still-used path is re-pushed, then remove it in a follow-up.
Retention and size: Cachix garbage-collects per its plan; no manual GC is configured and the dashboard totals are not publicly visible. What each merge adds is what changed: a code-only merge re-pushes only the rebuilt packages (each a few MB compressed, see the table); a lockfile or fork-rev change rebuilds the five compiled-dependency derivations (~420 MB each in the store, ~130 MB compressed; about 690 MB per architecture). Packaging notes for distro builders live under Package scoot offline.
