diff options
| author | 2026-09-24 20:22:57 +0200 | |
|---|---|---|
| committer | 2026-09-24 20:22:57 +0200 | |
| commit | 1f266cfa4048d5c71b39e3f458aa5f1cfa348579 (patch) | |
| tree | 50510e273156e65adf4f57870517ef28940ad181 /openspec/changes/archive | |
| parent | b0c5452e3bb1e3bdbb27181e331fc959fa43f4fc (diff) | |
| download | desktop-flake-1f266cfa4048d5c71b39e3f458aa5f1cfa348579.tar.gz desktop-flake-1f266cfa4048d5c71b39e3f458aa5f1cfa348579.tar.bz2 desktop-flake-1f266cfa4048d5c71b39e3f458aa5f1cfa348579.zip | |
Enable nix-ld so uv-managed prebuilt binaries run on NixOS
uv fetches prebuilt CPython interpreters and native wheel extensions
that expect a standard glibc loader path, which doesn't exist on
NixOS without nix-ld. Enables it unconditionally on every NixOS host
and extends its library set with common CPython native-extension
dependencies not already covered by nix-ld's own defaults.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Diffstat (limited to 'openspec/changes/archive')
5 files changed, 100 insertions, 0 deletions
diff --git a/openspec/changes/archive/2026-09-24-enable-nix-ld/.openspec.yaml b/openspec/changes/archive/2026-09-24-enable-nix-ld/.openspec.yaml new file mode 100644 index 0000000..75289e4 --- /dev/null +++ b/openspec/changes/archive/2026-09-24-enable-nix-ld/.openspec.yaml | |||
| @@ -0,0 +1,2 @@ | |||
| 1 | schema: spec-driven | ||
| 2 | created: 2026-09-24 | ||
diff --git a/openspec/changes/archive/2026-09-24-enable-nix-ld/design.md b/openspec/changes/archive/2026-09-24-enable-nix-ld/design.md new file mode 100644 index 0000000..aa9abff --- /dev/null +++ b/openspec/changes/archive/2026-09-24-enable-nix-ld/design.md | |||
| @@ -0,0 +1,37 @@ | |||
| 1 | ## Context | ||
| 2 | |||
| 3 | See `proposal.md` - Why for motivation. Relevant constraints verified against this flake's pinned nixpkgs (`nixos-unstable`, rev `b1b875982b17dabde9b4a37f3e229e74913e6db3`), `nixos/modules/programs/nix-ld.nix`: | ||
| 4 | |||
| 5 | - The module's real option surface is `programs.nix-ld.enable` (bool), `programs.nix-ld.package`, and `programs.nix-ld.libraries` (`listOf package`). | ||
| 6 | - `programs.nix-ld.libraries` is a list-typed option, so NixOS's module system merges every module's contribution by concatenation rather than override. | ||
| 7 | - Once `enable`d, the module's own `config` block (not its `default`) unconditionally sets `programs.nix-ld.libraries` to a baseline set: `zlib zstd stdenv.cc.cc curl openssl attr libssh bzip2 libxml2 acl libsodium util-linux xz systemd`. This baseline always applies — it is not something a consuming module can accidentally clobber by also setting the option. | ||
| 8 | - `modules/nixos/common.nix` currently has no `lib.mkIf`/feature-flag pattern anywhere; every service (SSH, GNOME, Docker, PipeWire, printing) is turned on unconditionally for every host, with `lib.mkDefault` used only for the two settings (`time.timeZone`, `i18n.defaultLocale`) that a per-host `configuration.nix` is expected to override. | ||
| 9 | - Only one real host exists today (`hosts/test-vm/`), and neither it nor `hosts/template/` sets any GPU/CUDA-related packages that would need to be folded into a `nix-ld` library list. | ||
| 10 | - `programs.nix-ld` has no equivalent in home-manager and is not read by `modules/home/*`; it is purely a NixOS system module concern, so it cannot affect the standalone `homeConfigurations` built via `mkHome`. | ||
| 11 | |||
| 12 | ## Goals / Non-Goals | ||
| 13 | |||
| 14 | **Goals:** | ||
| 15 | - `uv`-fetched prebuilt CPython interpreters and native wheel extensions run correctly on every NixOS host built from this flake, today and for hosts added later. | ||
| 16 | - The library list is easy to extend later without restructuring anything, since new uv-fetched binaries may need libraries not anticipated now. | ||
| 17 | |||
| 18 | **Non-Goals:** | ||
| 19 | - A full FHS-compatible environment (`buildFHSEnv`/`buildFHSUserEnv`) — `nix-ld` alone is sufficient for the driving use case (running foreign dynamically-linked binaries), and an FHS wrapper is a much larger, unrelated commitment. | ||
| 20 | - GPU/CUDA-specific libraries — no host in this flake currently has a GPU driver or CUDA toolkit configured, so there is nothing host-specific to add yet. | ||
| 21 | - A per-host opt-out or feature flag for `nix-ld` — only one real host exists, and `modules/nixos/common.nix` doesn't use feature flags for any of its other unconditional services; a future host that genuinely needs to opt out can still override with `lib.mkForce false` without requiring this design to add a flag preemptively. | ||
| 22 | |||
| 23 | ## Decisions | ||
| 24 | |||
| 25 | **Enable `programs.nix-ld.enable` unconditionally in `modules/nixos/common.nix`, not per-host.** | ||
| 26 | Matches this file's existing style — every other service (Docker, GNOME, SSH) is unconditional, with `mkDefault` reserved specifically for settings a host is expected to override (locale/timezone). `nix-ld` has no such per-host variance today. Alternative considered: gate it behind a new `home-manager`-style option so hosts could opt in/out — rejected as unnecessary complexity with no current use case, and inconsistent with how every other service in this file is already handled. | ||
| 27 | |||
| 28 | **Extend `programs.nix-ld.libraries` with a curated Python-native-extension list, rather than leaving it at nix-ld's own defaults.** | ||
| 29 | nix-ld's built-in baseline (`zlib`, `openssl`, `bzip2`, `libxml2`, `xz`, `stdenv.cc.cc`, etc.) covers a lot, but common CPython build dependencies it does *not* include are `ncurses`, `libffi`, `sqlite`, `readline`, `gdbm`, and `expat` — all commonly required by python-build-standalone interpreters and native extension wheels. Because the option merges additively, `common.nix` only needs to state the delta, not restate nix-ld's own defaults. Alternative considered: leave `programs.nix-ld.libraries` unset and rely solely on nix-ld's own baseline — rejected because it would likely leave real CPython interpreter/extension failures unresolved, defeating the purpose of this change. | ||
| 30 | |||
| 31 | **Keep the library list inline in `modules/nixos/common.nix` rather than a separate `lib/nix-ld-libraries.nix` file.** | ||
| 32 | The list is short (six packages) and consumed in exactly one place, unlike `lib/unfree-allowlist.nix`, which is imported by both `mkNixos` and `mkHome`. Alternative considered: mirror the `lib/unfree-allowlist.nix` pattern with a dedicated file — rejected as premature indirection for a list this size with a single consumer; can be extracted later if it grows or gains a second consumer. | ||
| 33 | |||
| 34 | ## Risks / Trade-offs | ||
| 35 | |||
| 36 | - **[Risk]** The curated library list is best-effort and may not cover every binary `uv` ever fetches, so some future interpreter or native extension could still fail to load a shared library. → **Mitigation**: `programs.nix-ld.libraries` merges additively (confirmed from the module source), so closing a future gap is a one-line addition to the existing list, not a restructuring. | ||
| 37 | - **[Risk]** This change cannot be fully verified from this development environment — confirming a `uv`-managed interpreter or native extension actually runs requires an activated NixOS host (`nixos-rebuild switch`), not just a successful build. → **Mitigation**: verification here is limited to `nix flake check` and building the `test-vm` system closure; end-to-end confirmation is called out as a manual follow-up in `tasks.md` rather than claimed as done. | ||
diff --git a/openspec/changes/archive/2026-09-24-enable-nix-ld/proposal.md b/openspec/changes/archive/2026-09-24-enable-nix-ld/proposal.md new file mode 100644 index 0000000..2a51e37 --- /dev/null +++ b/openspec/changes/archive/2026-09-24-enable-nix-ld/proposal.md | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | ## Why | ||
| 2 | |||
| 3 | `uv` (provisioned in `modules/home/dev-tools.nix`) doesn't just run Python code — `uv python install` fetches prebuilt CPython interpreters (python-build-standalone), and `uv pip install`/`uv tool install` can pull wheels with prebuilt native (C-extension) binaries. These are ordinary dynamically-linked ELF binaries built against a generic glibc loader path (`/lib64/ld-linux-x86-64.so.2`) that doesn't exist on NixOS, so they fail to execute without `nix-ld`. This was explicitly flagged as a known, deferred gap in the change that added `uv` itself (`openspec/changes/archive/2026-09-24-add-uv-and-docker-runtime/proposal.md`: *"no `nix-ld`/extra container-toolchain packages beyond what's already provisioned"*). This change closes that gap. | ||
| 4 | |||
| 5 | ## What Changes | ||
| 6 | |||
| 7 | - Enable `programs.nix-ld.enable = true;` unconditionally on every NixOS host, in `modules/nixos/common.nix`, alongside the other unconditional host-level services (GNOME, SSH, Docker) — matching that file's existing no-feature-flag style. | ||
| 8 | - Extend `programs.nix-ld.libraries` with a small curated set of common CPython native-extension dependencies (`ncurses`, `libffi`, `sqlite`, `readline`, `gdbm`, `expat`) that aren't already covered by nix-ld's own built-in baseline list (`zlib`, `zstd`, `stdenv.cc.cc`, `curl`, `openssl`, `attr`, `libssh`, `bzip2`, `libxml2`, `acl`, `libsodium`, `util-linux`, `xz`, `systemd`, confirmed from the pinned nixpkgs module source). `programs.nix-ld.libraries` merges additively across modules, so this only adds to nix-ld's own defaults rather than replacing them. | ||
| 9 | - **BREAKING**: none — additive only. Only applies to NixOS hosts (`modules/nixos/common.nix`); the standalone `tom@linux`/`tom@darwin` home-manager profiles (`homeConfigurations`, built via `mkHome`) are unaffected, since `programs.nix-ld` is a NixOS-only module option with no equivalent in home-manager. | ||
| 10 | |||
| 11 | ## Capabilities | ||
| 12 | |||
| 13 | ### New Capabilities | ||
| 14 | - `dynamic-linking-support`: NixOS hosts built from this flake can run prebuilt, foreign (non-Nix-built) dynamically-linked binaries — such as the interpreters and native wheel extensions `uv` fetches — via `nix-ld`, rather than failing with a missing-interpreter/exec error. | ||
| 15 | |||
| 16 | ### Modified Capabilities | ||
| 17 | - None. `developer-environment` already provisions `uv` itself and makes no claim about dynamic-linking behavior, so its spec is untouched. | ||
| 18 | |||
| 19 | ## Impact | ||
| 20 | |||
| 21 | - **Affected code**: `modules/nixos/common.nix` only (add `programs.nix-ld.enable` and extend `programs.nix-ld.libraries`). | ||
| 22 | - **Not affected**: `modules/home/*` (including `dev-tools.nix`, which already provisions `uv`), `homeConfigurations`/`lib/mkHome.nix`, `lib/unfree-allowlist.nix` (the `nix-ld` package and all added libraries are free-licensed, no allowlist entry needed), `hosts/template/` and `hosts/test-vm/` (no per-host override needed — this is a shared baseline, same as GNOME/SSH/Docker). | ||
| 23 | - **Dependencies**: nixpkgs' built-in `nix-ld` NixOS module (`nixos/modules/programs/nix-ld.nix`) and its default `nix-ld` package, both already present in the pinned `nixos-unstable` input; no new flake inputs. | ||
| 24 | - **Platforms**: NixOS hosts only (`nixosConfigurations`, via `mkNixos`/`modules/nixos/common.nix`). No effect on standalone home-manager-only platforms. | ||
| 25 | - **Known limitation, out of scope**: the curated library list is best-effort, not exhaustive — some future uv-fetched binary could still need a library not in the list. Because `programs.nix-ld.libraries` merges additively, closing such a gap later is a one-line addition, not a rework. | ||
diff --git a/openspec/changes/archive/2026-09-24-enable-nix-ld/specs/dynamic-linking-support/spec.md b/openspec/changes/archive/2026-09-24-enable-nix-ld/specs/dynamic-linking-support/spec.md new file mode 100644 index 0000000..e859aa4 --- /dev/null +++ b/openspec/changes/archive/2026-09-24-enable-nix-ld/specs/dynamic-linking-support/spec.md | |||
| @@ -0,0 +1,23 @@ | |||
| 1 | ## Purpose | ||
| 2 | |||
| 3 | Lets NixOS hosts built from this flake run prebuilt, foreign (non-Nix-built) dynamically-linked binaries — such as the interpreters and native wheel extensions that developer tools like `uv` fetch at runtime — instead of failing with a missing-interpreter execution error. | ||
| 4 | |||
| 5 | ## ADDED Requirements | ||
| 6 | |||
| 7 | ### Requirement: NixOS hosts run foreign dynamically-linked binaries via nix-ld | ||
| 8 | Every NixOS host built from this flake's shared system module SHALL enable `nix-ld`, so that prebuilt binaries linked against a standard (non-NixOS) dynamic loader path execute correctly instead of failing to run. | ||
| 9 | |||
| 10 | #### Scenario: A uv-managed prebuilt Python interpreter executes | ||
| 11 | - **WHEN** `uv python install` downloads a prebuilt CPython interpreter and that interpreter is invoked | ||
| 12 | - **THEN** the interpreter starts successfully instead of failing with a missing-interpreter or "cannot execute binary file" error | ||
| 13 | |||
| 14 | #### Scenario: A native wheel extension loads its shared library dependencies | ||
| 15 | - **WHEN** a Python package installed by `uv` includes a prebuilt native (C-extension) binary that depends on common shared libraries (e.g. `ncurses`, `libffi`, `sqlite`, `readline`, `gdbm`, `expat`, or another library covered by `nix-ld`'s configured library set) | ||
| 16 | - **THEN** the extension loads its dependencies successfully instead of failing with a missing shared library error | ||
| 17 | |||
| 18 | ### Requirement: The nix-ld library set is host-wide and additive | ||
| 19 | The set of libraries made available to foreign binaries via `nix-ld` SHALL be defined once for all NixOS hosts built from this flake's shared system module, and SHALL be extensible by appending further libraries without removing or replacing the existing set. | ||
| 20 | |||
| 21 | #### Scenario: A host-level addition does not remove existing libraries | ||
| 22 | - **WHEN** a NixOS host's own configuration adds an additional library to the nix-ld library set | ||
| 23 | - **THEN** the libraries already provided by the shared system module remain available, unaffected by the addition | ||
diff --git a/openspec/changes/archive/2026-09-24-enable-nix-ld/tasks.md b/openspec/changes/archive/2026-09-24-enable-nix-ld/tasks.md new file mode 100644 index 0000000..fdd8abe --- /dev/null +++ b/openspec/changes/archive/2026-09-24-enable-nix-ld/tasks.md | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | ## 1. Enable nix-ld on NixOS hosts | ||
| 2 | |||
| 3 | - [x] 1.1 In `modules/nixos/common.nix`, add `programs.nix-ld.enable = true;` alongside the other unconditional host-level services, with a short comment explaining why (uv-managed prebuilt binaries need an FHS-style loader path). Verify by re-reading the diff: the option is set unconditionally, matching the file's existing style. | ||
| 4 | - [x] 1.2 In the same file, add `programs.nix-ld.libraries = with pkgs; [ ncurses libffi sqlite readline gdbm expat ];`. Verify the six packages resolve in the pinned nixpkgs by running `nix eval '.#nixosConfigurations.test-vm.config.programs.nix-ld.libraries' --apply 'builtins.length'` (or equivalent) and confirming it evaluates without error. | ||
| 5 | |||
| 6 | ## 2. Verify the change builds | ||
| 7 | |||
| 8 | - [x] 2.1 Run `nix flake check` and confirm it passes. | ||
| 9 | - [x] 2.2 Run `nix build '.#nixosConfigurations.test-vm.config.system.build.toplevel'` and confirm the system closure builds successfully with `programs.nix-ld` enabled. | ||
| 10 | |||
| 11 | ## 3. Manual follow-up (deferred, not verifiable in this environment) | ||
| 12 | |||
| 13 | - [ ] 3.1 After deploying to an activated NixOS host, run `uv python install 3.12` and confirm the installed interpreter executes (e.g. `uv run python3.12 --version`) instead of failing with a missing-interpreter error. This cannot be verified from this development environment since it requires an activated host, not just a successful build. | ||
