From 946ee75b274252af5e1a2a4a8ad3e6f4932005a8 Mon Sep 17 00:00:00 2001 From: Michael Heller <21163552+mdheller@users.noreply.github.com> Date: Sun, 2 Aug 2026 19:33:25 -0400 Subject: [PATCH] guix: migration spike scaffold (channels + workstation profile + plan) Additive Guix spike (does not touch the Nix build). Decision: migrate the SourceOS substrate Nix -> Guix (Nix governance instability). nonguix = the same nonfree posture as nixpkgs allowUnfree (real kernel/firmware/microcode); NOT the FSDG zero-blob badge. Includes channels.scm (guix + nonguix, commits to be pinned on first runner) and system/workstation.scm (x86_64 baseline using nonguix kernel/firmware/microcode). README = phased prove-then-cut plan + the two hard targets (Asahi, CUDA) the spike must prove. Adversarial review: fixed a keyboard-layout field-vs-variable bug (define once, reuse), dropped an unused service module, verified balanced forms + import->symbol coverage. NOT build-validated here (no guix on macOS); first on a Linux runner is the real gate. --- guix/README.md | 48 ++++++++++++++++++++++++++ guix/channels.scm | 30 +++++++++++++++++ guix/system/workstation.scm | 67 +++++++++++++++++++++++++++++++++++++ 3 files changed, 145 insertions(+) create mode 100644 guix/README.md create mode 100644 guix/channels.scm create mode 100644 guix/system/workstation.scm diff --git a/guix/README.md b/guix/README.md new file mode 100644 index 0000000..adbc3d1 --- /dev/null +++ b/guix/README.md @@ -0,0 +1,48 @@ +# SourceOS on Guix — migration spike + +Status: **spike** (additive; does not touch the Nix build). Decision recorded: +migrate the SourceOS substrate from Nix → Guix, driven by Nix-project governance +instability. `nonguix` gives us the same nonfree posture we already run under +nixpkgs `allowUnfree` — the freedom stance is unchanged; we are **not** chasing +the FSF/FSDG zero-blob badge. + +## What's here +- `channels.scm` — upstream Guix + `nonguix` (the `allowUnfree` analog), with + nonguix's published signing introduction. **Pin the commits** via + `guix pull && guix describe` on the first runner (offline hashes can't be real). +- `system/workstation.scm` — the first parity target: a plain x86_64 workstation + using the real Linux kernel + firmware + CPU microcode from nonguix. + +## Build (on a Linux runner — NOT macOS) +Requires the `guix-daemon` on Linux; it cannot run from a macOS session. + +```bash +guix time-machine -C guix/channels.scm -- system build guix/system/workstation.scm +guix time-machine -C guix/channels.scm -- system vm guix/system/workstation.scm # boot-test +``` + +## Why the spike is scoped this way +The philosophy is settled; the only real risk is **nonguix hardware-enablement +maturity** for our two hardest targets. The spike proves them in order: + +1. **x86_64 workstation** (`system/workstation.scm`) — baseline parity. +2. **Apple Silicon / Asahi on Guix** — exists but far less trodden than Asahi-on-Nix. +3. **CUDA / GPU model-serving node** — packaged via nonguix; smaller/less + battle-tested than nixpkgs. + +If 2 and 3 build and boot with working hardware, the migration path is real. If +one hits a wall, we've learned it cheaply — before touching the estate. + +## Phased plan (prove-then-cut, same discipline as the board parity) +1. **Spike** — this directory: prove the three targets build/boot. +2. **Parity** — re-express `modules/nixos/*` as Guix services/system config, the + base-OS profiles first (SourceOS territory). +3. **Cutover** — a Guix image-build workflow alongside `nix-build-images`; run + both; flip the default once parity holds. Nix stays until then. + +## Honest limits +- These files are authored and reviewed but **not build-validated** here (no guix + on macOS). First `guix system build` on a Linux runner is the real gate. +- Reproducibility is not achieved until `channels.scm` commits are pinned. +- Lix (drop-in Nix fork) remains the cheaper stability-only hedge if the Guix + spike's hardware targets prove too immature; keep it in reserve. diff --git a/guix/channels.scm b/guix/channels.scm new file mode 100644 index 0000000..2120120 --- /dev/null +++ b/guix/channels.scm @@ -0,0 +1,30 @@ +;;; SourceOS Guix channels — the Nix -> Guix migration spike. +;;; +;;; Two channels: upstream Guix, plus `nonguix` — Guix's analog of nixpkgs' +;;; `allowUnfree`. nonguix provides the real Linux kernel + firmware + CPU +;;; microcode + proprietary drivers/CUDA the estate's hardware needs. This is the +;;; SAME nonfree posture the estate already runs under nixpkgs + allowUnfree; the +;;; freedom stance is unchanged (we are NOT chasing the FSF/FSDG zero-blob badge). +;;; +;;; Reproducibility: PIN `commit` on both channels via `guix pull && guix describe` +;;; on the first Linux build runner, then commit the pinned result here. Real +;;; commit hashes cannot be fabricated offline — the branch pins below are only +;;; the spike starting point, not a reproducible pin. +;;; +;;; The nonguix `introduction` below is its PUBLISHED security bootstrap (the +;;; signing anchor). VERIFY it against the current nonguix README before the first +;;; pull — a wrong commit/fingerprint fails channel authentication by design. + +(list (channel + (name 'guix) + (url "https://git.savannah.gnu.org/git/guix.git") + (branch "master")) + (channel + (name 'nonguix) + (url "https://gitlab.com/nonguix/nonguix") + (branch "master") + (introduction + (make-channel-introduction + "897c1a470da759236cc11798f4e0a5f7d4d59fbc" + (openpgp-fingerprint + "2A39 3FFF 68F4 EF7A 3D29 12AF 6F51 20A0 22FB B2D5"))))) diff --git a/guix/system/workstation.scm b/guix/system/workstation.scm new file mode 100644 index 0000000..30a890d --- /dev/null +++ b/guix/system/workstation.scm @@ -0,0 +1,67 @@ +;;; SourceOS Guix workstation profile (x86_64) — Nix -> Guix migration spike. +;;; +;;; Demonstrates the nonfree posture: the real Linux kernel + `linux-firmware` +;;; + CPU `microcode-initrd` from nonguix (the `allowUnfree` equivalent), not +;;; `linux-libre`. This is the first parity target — a plain workstation image — +;;; before the two hard ones (Asahi/Apple-Silicon, CUDA model-serving node). +;;; +;;; Build on a LINUX runner with the channels in ../channels.scm applied: +;;; guix time-machine -C guix/channels.scm -- \ +;;; system build guix/system/workstation.scm # realize the closure +;;; guix time-machine -C guix/channels.scm -- \ +;;; system vm guix/system/workstation.scm # boot-test in a VM +;;; NOT buildable from macOS (needs the guix-daemon on Linux). + +(use-modules (gnu) + (nongnu packages linux) + (nongnu system linux-initrd)) +(use-service-modules desktop ssh) +(use-package-modules certs ssh version-control) + +;; Define the layout once so the OS and the bootloader share the same object. +(define %keyboard-layout (keyboard-layout "us")) + +(operating-system + (host-name "sourceos-workstation") + (timezone "UTC") + (locale "en_US.utf8") + (keyboard-layout %keyboard-layout) + + ;; nonguix: real Linux + firmware + CPU microcode (the allowUnfree equivalent). + (kernel linux) + (firmware (list linux-firmware)) + (initrd microcode-initrd) + + (bootloader (bootloader-configuration + (bootloader grub-efi-bootloader) + (targets '("/boot/efi")) + (keyboard-layout %keyboard-layout))) + + (file-systems (append + (list (file-system + (mount-point "/") + (device (file-system-label "SOURCEOS_ROOT")) + (type "ext4")) + (file-system + (mount-point "/boot/efi") + (device (file-system-label "SOURCEOS_EFI")) + (type "vfat"))) + %base-file-systems)) + + (users (cons (user-account + (name "sourceos") + (comment "SourceOS operator") + (group "users") + (supplementary-groups '("wheel" "netdev" "audio" "video"))) + %base-user-accounts)) + + (packages (append (list nss-certs git) + %base-packages)) + + ;; openssh (keys only) prepended to the desktop stack (which already provides + ;; NetworkManager / wpa-supplicant — do not re-declare them here). + (services (cons* (service openssh-service-type + (openssh-configuration + (openssh openssh-sans-x) + (password-authentication? #f))) + %desktop-services)))