Locked Nix Flake DevShell for Clean-Host Toolchain #1

Closed
opened 2026-09-22 17:58:52 +00:00 by yuris · 0 comments
Owner

Summary

Provide a hermetic, project-local Nix flake and devShell that supplies pinned Rust tooling (rustc, cargo, rustfmt, clippy, rust-analyzer, pkg-config) without relying on global host installations or ad-hoc SDKs, establishing the clean-host contract across macOS Apple Silicon and Linux deployment targets.

Background & Technical Rationale

The core architectural premise of this repository is a clean-host development contract: never globally install rustup, Node.js, Python, or application SDK tooling on developer workstations or CI environments. Global toolchain installations cause non-reproducible builds, compiler drift, and subtle cross-platform incompatibilities between macOS (aarch64-darwin) and production Linux containers (x86_64-linux and aarch64-linux).

Instead, a project-local Nix flake (flake.nix) backed by a pinned nixpkgs revision acts as the single source of truth for the compiler, linter, language server, and build utilities. nix develop is the canonical entry point into the development environment. direnv (via .envrc) provides optional shell convenience, not a secondary or diverging toolchain source. Updates to the compiler or toolchain packages must be version-controlled, explicit lockfile changes in flake.lock.

Furthermore, build caches and dependency artifacts must remain strictly separated from production runtime state, and no host virtualization or container runtime is permitted to install language toolchains globally.

Exact Technical References & Specifications

Normative Toolchain Contract

From docs/technical/development.md#toolchain-contract:

"Use a project-local Nix flake with locked nixpkgs supplying Rust. nix develop is canonical; direnv is optional convenience, not a second toolchain source. Never globally install Rust/rustup, Node, Python or application SDK tooling for this project. Do not combine rustup, an independent Rust overlay pin and nixpkgs Rust. Choose the locked nixpkgs Rust toolchain; updates are reviewed lockfile changes.

Future devShell tools: rustc, cargo, rustfmt, clippy, rust-analyzer, pkg-config and isolated fixture/test utilities. Support aarch64-darwin for Apple Silicon development and the chosen Linux production architecture; do not assume a macOS build runs in a Linux container. Container runtime availability is an operator prerequisite, not an excuse to globally install language toolchains. Nix itself and any required host virtualization runtime are separately managed host facilities.

During implementation commit Cargo.lock and flake.lock. Package/library versions are selected and pinned then; this document does not claim an existing lock or successful build. Rust HTTP adapters avoid runtime SDK sidecars. Build caches and downloaded dependency artifacts are separate from production data, never restored as application state."

Dependency Responsibilities

From docs/technical/development.md#dependency-responsibilities:

Dependency/tool Owns Does not prove
Locked nixpkgs Rust tools Compiler, formatter, linting, IDE support Correct application behavior
Tokio / Axum / Clap / Serde Async runtime, HTTP/CLI interface, typed JSON Authorization or temporal policy automatically
reqwest + rustls Pooled HTTPS transport Safe arbitrary redirects or URLs
rusqlite / SQLite Local transactional journal storage Distributed transactions or network-filesystem WAL safety
quick-xml Namespace-aware WebDAV parsing Trustworthiness of external XML
ical Parsing component structure Lossless editing or full recurrence expansion
icalendar Newly generated resources Safe round-trip of external objects
chrono / chrono-tz Application time/zone calculations Arbitrary VTIMEZONE support or recurrence correctness
tracing Structured diagnostics Permission to log private payloads
Isolated Radicale container Disposable integration fixture Production deployment compatibility
HTTP fixtures Repeatable Hindsight/Jev wire scenarios Live account/model availability

Canonical Development Workflow

From docs/technical/development.md#future-development-workflow:

nix develop
cargo fmt --check
cargo clippy --all-targets -- -D warnings
cargo test
cargo build --release

Deployment Scenario D01 Requirement

From docs/technical/verification.md#deployment-scenarios:

ID Input / operation Observable acceptance
D01 Clean macOS arm64 and Linux environment through locked Nix shell Required tools available without global rustup/SDKs; committed lockfiles reproduce selected dependencies

Formal Section Citations

  • docs/technical/development.md#toolchain-contract: Defines the clean-host principle, platform coverage (aarch64-darwin, x86_64-linux), and lockfile ownership.
  • docs/technical/development.md#dependency-responsibilities: Establishes ownership boundaries for compiler and build tooling.
  • docs/technical/development.md#future-development-workflow: Specifies the standard shell invocation commands.
  • docs/technical/verification.md#deployment-scenarios: Formulates normative scenario D01 for environment reproducibility.

What to Build (Step-by-Step)

  1. Create flake.nix:

    • Declare inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; (or a stable nixpkgs release pin).
    • Target systems: ["x86_64-linux", "aarch64-linux", "aarch64-darwin"].
    • Provide a devShells.<system>.default containing:
      • rustc
      • cargo
      • rustfmt
      • clippy
      • rust-analyzer
      • pkg-config
      • Platform-specific dependencies (e.g., Apple SDK frameworks Security, CoreFoundation, SystemConfiguration on Darwin; openssl or native stubs on Linux).
    • Set environment variables if needed (RUST_BACKTRACE = "1", RUST_SRC_PATH = "${pkgs.rustPlatform.rustLibSource}").
  2. Generate flake.lock:

    • Run nix flake lock to lock dependencies.
    • Commit flake.lock to git tracking.
  3. Configure Direnv Integration (.envrc):

    • Create .envrc containing:
      use flake
      
    • Ensure developers with direnv can automatically enter the devShell, while leaving nix develop as the canonical standalone entry point.
  4. Verify Environment Isolation:

    • Confirm that running nix develop --command cargo --version succeeds in a clean shell without ~/.cargo/bin or global rustup present on PATH.
    • Verify toolchain version consistency across aarch64-darwin and Linux environments.

Acceptance Criteria

  • Running nix develop creates a subshell providing cargo, rustc, clippy, rustfmt, and rust-analyzer on PATH.
  • No global rustup, global Node.js, or global Python SDKs are referenced or required.
  • flake.lock exists and is tracked in version control, locking nixpkgs revisions deterministically.
  • Running cargo --version inside the devShell matches the exact version supplied by the locked nixpkgs.
  • .envrc is provided with use flake for direnv users.
  • Fully satisfies Scenario D01 from docs/technical/verification.md: "Required tools available without global rustup/SDKs; committed lockfiles reproduce selected dependencies".

Blocked by

None - can start immediately.

## Summary Provide a hermetic, project-local Nix flake and devShell that supplies pinned Rust tooling (`rustc`, `cargo`, `rustfmt`, `clippy`, `rust-analyzer`, `pkg-config`) without relying on global host installations or ad-hoc SDKs, establishing the clean-host contract across macOS Apple Silicon and Linux deployment targets. ## Background & Technical Rationale The core architectural premise of this repository is a clean-host development contract: never globally install `rustup`, Node.js, Python, or application SDK tooling on developer workstations or CI environments. Global toolchain installations cause non-reproducible builds, compiler drift, and subtle cross-platform incompatibilities between macOS (`aarch64-darwin`) and production Linux containers (`x86_64-linux` and `aarch64-linux`). Instead, a project-local Nix flake (`flake.nix`) backed by a pinned `nixpkgs` revision acts as the single source of truth for the compiler, linter, language server, and build utilities. `nix develop` is the canonical entry point into the development environment. `direnv` (via `.envrc`) provides optional shell convenience, not a secondary or diverging toolchain source. Updates to the compiler or toolchain packages must be version-controlled, explicit lockfile changes in `flake.lock`. Furthermore, build caches and dependency artifacts must remain strictly separated from production runtime state, and no host virtualization or container runtime is permitted to install language toolchains globally. ## Exact Technical References & Specifications ### Normative Toolchain Contract From `docs/technical/development.md#toolchain-contract`: > "Use a project-local Nix flake with locked nixpkgs supplying Rust. `nix develop` is canonical; direnv is optional convenience, not a second toolchain source. Never globally install Rust/rustup, Node, Python or application SDK tooling for this project. Do not combine rustup, an independent Rust overlay pin and nixpkgs Rust. Choose the locked nixpkgs Rust toolchain; updates are reviewed lockfile changes. > > Future devShell tools: rustc, cargo, rustfmt, clippy, rust-analyzer, pkg-config and isolated fixture/test utilities. Support aarch64-darwin for Apple Silicon development and the chosen Linux production architecture; do not assume a macOS build runs in a Linux container. Container runtime availability is an operator prerequisite, not an excuse to globally install language toolchains. Nix itself and any required host virtualization runtime are separately managed host facilities. > > During implementation commit Cargo.lock and flake.lock. Package/library versions are selected and pinned then; this document does not claim an existing lock or successful build. Rust HTTP adapters avoid runtime SDK sidecars. Build caches and downloaded dependency artifacts are separate from production data, never restored as application state." ### Dependency Responsibilities From `docs/technical/development.md#dependency-responsibilities`: | Dependency/tool | Owns | Does not prove | |---|---|---| | Locked nixpkgs Rust tools | Compiler, formatter, linting, IDE support | Correct application behavior | | Tokio / Axum / Clap / Serde | Async runtime, HTTP/CLI interface, typed JSON | Authorization or temporal policy automatically | | reqwest + rustls | Pooled HTTPS transport | Safe arbitrary redirects or URLs | | rusqlite / SQLite | Local transactional journal storage | Distributed transactions or network-filesystem WAL safety | | quick-xml | Namespace-aware WebDAV parsing | Trustworthiness of external XML | | ical | Parsing component structure | Lossless editing or full recurrence expansion | | icalendar | Newly generated resources | Safe round-trip of external objects | | chrono / chrono-tz | Application time/zone calculations | Arbitrary VTIMEZONE support or recurrence correctness | | tracing | Structured diagnostics | Permission to log private payloads | | Isolated Radicale container | Disposable integration fixture | Production deployment compatibility | | HTTP fixtures | Repeatable Hindsight/Jev wire scenarios | Live account/model availability | ### Canonical Development Workflow From `docs/technical/development.md#future-development-workflow`: ```sh nix develop cargo fmt --check cargo clippy --all-targets -- -D warnings cargo test cargo build --release ``` ### Deployment Scenario D01 Requirement From `docs/technical/verification.md#deployment-scenarios`: | ID | Input / operation | Observable acceptance | |---|---|---| | D01 | Clean macOS arm64 and Linux environment through locked Nix shell | Required tools available without global rustup/SDKs; committed lockfiles reproduce selected dependencies | ### Formal Section Citations - `docs/technical/development.md#toolchain-contract`: Defines the clean-host principle, platform coverage (`aarch64-darwin`, `x86_64-linux`), and lockfile ownership. - `docs/technical/development.md#dependency-responsibilities`: Establishes ownership boundaries for compiler and build tooling. - `docs/technical/development.md#future-development-workflow`: Specifies the standard shell invocation commands. - `docs/technical/verification.md#deployment-scenarios`: Formulates normative scenario D01 for environment reproducibility. ## What to Build (Step-by-Step) 1. **Create `flake.nix`**: - Declare `inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";` (or a stable nixpkgs release pin). - Target systems: `["x86_64-linux", "aarch64-linux", "aarch64-darwin"]`. - Provide a `devShells.<system>.default` containing: - `rustc` - `cargo` - `rustfmt` - `clippy` - `rust-analyzer` - `pkg-config` - Platform-specific dependencies (e.g., Apple SDK frameworks `Security`, `CoreFoundation`, `SystemConfiguration` on Darwin; `openssl` or native stubs on Linux). - Set environment variables if needed (`RUST_BACKTRACE = "1"`, `RUST_SRC_PATH = "${pkgs.rustPlatform.rustLibSource}"`). 2. **Generate `flake.lock`**: - Run `nix flake lock` to lock dependencies. - Commit `flake.lock` to git tracking. 3. **Configure Direnv Integration (`.envrc`)**: - Create `.envrc` containing: ```sh use flake ``` - Ensure developers with `direnv` can automatically enter the devShell, while leaving `nix develop` as the canonical standalone entry point. 4. **Verify Environment Isolation**: - Confirm that running `nix develop --command cargo --version` succeeds in a clean shell without `~/.cargo/bin` or global `rustup` present on `PATH`. - Verify toolchain version consistency across `aarch64-darwin` and Linux environments. ## Acceptance Criteria - [x] Running `nix develop` creates a subshell providing `cargo`, `rustc`, `clippy`, `rustfmt`, and `rust-analyzer` on `PATH`. - [x] No global `rustup`, global Node.js, or global Python SDKs are referenced or required. - [x] `flake.lock` exists and is tracked in version control, locking nixpkgs revisions deterministically. - [x] Running `cargo --version` inside the devShell matches the exact version supplied by the locked `nixpkgs`. - [x] `.envrc` is provided with `use flake` for direnv users. - [x] Fully satisfies Scenario D01 from `docs/technical/verification.md`: "Required tools available without global rustup/SDKs; committed lockfiles reproduce selected dependencies". ## Blocked by None - can start immediately.
yuris closed this issue 2026-09-22 18:14:27 +00:00
yuris changed title from Issue #001: Locked Nix Flake DevShell for Clean-Host Toolchain to Locked Nix Flake DevShell for Clean-Host Toolchain 2026-09-22 20:02:41 +00:00
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
yuris/life-service#1
No description provided.