Replace the stale Bun-only project-scaffold spec with a Nix-aware packaging spec, and record the flake-parts + bun2nix + pinned-Bun decision as ADR 0007.
4.5 KiB
ADR 0007 — Nix Flake Packaging
Status: Accepted
Context
The repository was scaffolded as a Bun workspaces monorepo before the project moved to Nix.
The developer wants to install the kitchen CLI declaratively and develop in a reproducible shell.
Bun is a first-class citizen for development and native-binary compilation, but it is the least turnkey JavaScript toolchain to build reproducibly under Nix.
Nix builds are sandboxed with no network, while bun install fetches from npm.
The design commits core to the remark/unified ecosystem (see ADR 0002) plus chalk in the CLI, so a real dependency closure must be vendored rather than wished away.
Runtime alternatives were weighed and rejected.
Switching to pnpm or npm on Node would unwind a Bun-shaped spec set and lose the self-contained single binary that satisfies the "no Node.js on target" goal.
Deno keeps the single binary but adds an npm-publishing step (dnt) for core, half of which is a library destined for an Obsidian plugin.
Three axes had to be decided: the flake's structure, how Bun dependencies are vendored in the sandbox, and how the Bun version is managed.
Flake structure — three options:
Plain flake with a hand-rolled per-system helper.
flake-utils with eachDefaultSystem.
flake-parts with the module system.
Dependency vendoring — two viable options:
Manual fixed-output derivation running bun install inside one FOD.
bun2nix generating per-package fixed-output derivations from the lockfile.
Decision
Package the repository as a flake-parts flake that builds the Bun-compiled kitchen binary as an installable package, with dependencies vendored via bun2nix, and the Bun version pinned and shared between the dev shell and the build.
- Outputs: a default package (the compiled binary), an app, a dev shell, and a checks set running the Bun tests and biome (the latter via treefmt-nix).
- Systems:
x86_64-linuxonly initially, extensible through the flake-parts systems list. - nixpkgs tracks
nixpkgs-unstable.
Rationale
flake-parts is chosen over the alternatives because the flake already has four per-system output categories and will compose tooling as modules — treefmt-nix for lint now, git-hooks later. The module system pays for itself at the second integration, and the outputs it organises are real, not speculative. flake-utils is rejected as the least future-proof: it is effectively in maintenance and handles system-independent outputs awkwardly. Plain flake is the fallback for maximum transparency, but it leaves the per-system plumbing hand-maintained.
bun2nix is chosen over a manual fixed-output derivation because its per-package derivations are content-addressed fetches rather than a byte-reproduced install. That is more robust across Bun versions and avoids the per-platform hash multiplication a single install-FOD incurs. It has first-class workspace support, which matters because this is a monorepo, and a postinstall hook that keeps the generated expression in sync with the lockfile automatically. It is maintained under nix-community, which answers the bus-factor concern. The manual FOD's only advantage — zero third-party inputs — is not worth its more fragile, hand-maintained, per-platform hash story.
Pinning Bun is a correctness constraint, not hygiene. Native compilation has produced empty binaries inside the Nix sandbox on specific Bun point releases, so the version that develops must be the version that compiles, and both must avoid known-bad releases.
Consequences
- bun2nix is a flake input, and a generated vendoring expression is checked in and kept current by a postinstall hook on lockfile changes.
- The dev shell and the build derivation share one pinned Bun version; bumping Bun is a deliberate, single-point change that must be validated against the sandbox compilation behaviour.
nix build,nix run,nix develop, andnix flake checkare the supported entry points;nix flake checkruns the Bun test tiers and biome and is CI-callable.- The compiled binary embeds its dependencies, so the installed CLI has no Node.js or Bun runtime requirement; the vendored closure is build-time only.
- Adding a target platform is a systems-list addition, each carrying its own per-platform vendoring and compilation.
- Biome runs as a reproducible check via treefmt-nix; a future pre-commit integration can be added as another flake-parts module.
- The packaging decision is documented here; the Bun monorepo layout it wraps is unchanged.