Root flake with two outputs: devShells.default (pinned 1.95.0 toolchain via rust-toolchain.toml + rust-overlay, plus cargo-sweep) and packages.default (rustPlatform.buildRustPackage from the committed Cargo.lock; doCheck=false). flake.lock pins nixpkgs nixos-26.05 / rust-overlay / flake-utils. .envrc (use flake) for direnv parity. Single source of toolchain for dev and the upcoming CI, so they can't drift. Verified through the flake: nix build yields a working binary, clippy clean, 2424 tests pass / 0 fail / 1 intentional ignored doctest. First step toward requirements.md TT5 + D1/D2/D3.
6.4 KiB
ADR-0049: Nix flake for a reproducible dev + build environment
Status
Accepted (2026-06-12). Implemented the same day on the ci branch:
flake.nix, flake.lock, rust-toolchain.toml, .envrc. Verified
end-to-end before acceptance — nix develop provides the pinned
toolchain; nix build .#default produces a working binary; cargo clippy --all-targets -- -D warnings is clean and cargo test is
2424 passed / 0 failed / 1 ignored (the ignored item is the
intentional ```ignore doctest at src/friendly/mod.rs:21),
all run through the flake. This ADR is the dev/build-environment
half of the CI work; the CI pipeline itself (runner wiring,
release matrix) is decided separately as it settles.
Context
The project is near feature-complete and CI is finally being set up
(requirements.md TT5, CI in the deferred list). CI must not
depend on whatever Rust/toolchain happens to be installed on the build
machine — that is neither reproducible nor honest about what the build
needs.
The sibling project datamage already solved this with a Nix flake (its ADR 0046): the flake is the single, version-pinned declaration of the toolchain, and both the dev shell and CI go through it so they cannot drift. We adopt the same pattern here. Ours is dramatically simpler than datamage's — this is a pure-Rust TUI with no Tauri / WebKitGTK / Node / WASM surface — so the flake carries almost no system dependencies.
Two build facts drove the (tiny) dependency set, confirmed from
Cargo.lock:
libsqlite3-sysis built withbundled→ SQLite is compiled from vendored C, which needs a C compiler.nixpkgs'stdenvprovides one automatically; nothing is declared for it.arboard's clipboard backend isx11rb— a pure-Rust socket XCB client that links no C X11 libraries. So no X11/pkg-configsystem inputs are needed to build or test. A live X server is only required at runtime to actually copy; headless sessions fall back to OSC 52.
Decision
Adopt a Nix flake at the repository root as the canonical declaration of the dev and build environment.
flake.nixexposes two outputs (user-chosen 2026-06-12 over a dev-shell-only variant):devShells.default— the pinned Rust toolchain (fromrust-toolchain.tomlviarust-overlay) pluscargo-sweepfor thetarget/build-hygiene discipline (CLAUDE.md / the datamage ADR 0050 equivalent).packages.default(=packages.rdbms-playground) — arustPlatform.buildRustPackagethat produces the binary reproducibly from the pinned toolchain and the committedCargo.lock(cargoLock.lockFile→importCargoLock, which fetches each dependency by its lockfile checksum: offline, deterministic, nocargoHashto churn).nix buildyields the artifact CI's gate/release can consume.
rust-toolchain.tomlpins an exact stable release (1.95.0), not the floatingstablechannel, sonix flake updatecannot surprise-bump Rust into new clippy lints that would fail the-D warningsgate (same reasoning as datamage ADR 0046). Components:rustfmt+clippy. No coverage/WASM tooling and no cross-compilation targets yet — those are added when the release matrix needs them, not before.flake.lockpins every input (nixpkgsnixos-26.05,rust-overlay,flake-utils) to a commit, making the env bit-reproducible..envrccontainsuse flakefor direnv auto-activation, kept for parity with datamage even though direnv is not installed on the current dev VM (entry is vianix develop).packages.defaultsetsdoCheck = false. The test suite is not run duringnix build— the Nix build sandbox has noHOMEand no X server, which fights the project-directory / clipboard paths the tests touch. Tests run as their own CI stage vianix develop -c cargo test, keeping "build the artifact" and "run the suite" cleanly separate.- The package version is read from
Cargo.tomlviabuiltins.fromTOML, so it never drifts from the crate metadata.
Consequences
- One toolchain definition. Dev and CI share the exact pinned
toolchain; they cannot drift. New contributors run
nix develop(or get auto-activation via direnv) and have the same Rust as CI. - D2 (static binary) is unaffected and still pending. The
nix buildartifact links the Nix-store glibc dynamically — it is a reproducible build/test artifact, not the single static release binary D2 calls for. Release binaries will target a static toolchain (e.g.x86_64-unknown-linux-musl) in the forthcoming CI release work; that is a release-step concern, not a dev-shell one. fmtis deliberately not gated yet. The tree is not clean under stockrustfmt(~100 files would change; norustfmt.tomlis committed and the code was shaped by something other than defaultrustfmt). Reformatting churns blame across every file and would conflict with the in-flight website branch and ongoingmainwork, so — user decision 2026-06-12 — thefmtgate is left out for now and revisited onmain. The CI gate isclippy+test.- Engine-name posture (CLAUDE.md) is respected. The flake's
comments may name SQLite/
rusqlitewhere technically necessary (build-input rationale); no user-facing string is affected.
Alternatives considered
- Dev-shell only (no build package). Matches datamage exactly; CI
would
cargo buildinsidenix develop -c. Rejected (user choice): anix buildpackage gives a reproducible release artifact straight from the pinned toolchain, which the release job wants. - A standard
rust:1.95image in CI, flake for dev only. Simpler in CI (no nix-in-CI caching to solve), but it is a second place that defines the toolchain — exactly the drift this ADR exists to prevent. Rejected for the unified-env goal; the nix-in-CI caching cost is solved in the CI pipeline work instead. rustupon the build machine. The status quo CI would replace — non-reproducible, machine-dependent, the thing we are eliminating.
Relationship to other decisions
- Mirrors datamage ADR 0046 (nix flake dev env) and its build hygiene companion. This is the rdbms-playground analogue, scoped to a pure-Rust project.
- Feeds the CI pipeline work for
requirements.mdTT5 (CI runs the tiers) and the D1/D2/D3 distribution items (the release matrix consumesnix build/ a static target).