diff --git a/.gitignore b/.gitignore index ead1537..6c112c0 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,3 @@ result .DS_Store hosts/*-hardware.nix hosts/local-*.nix - -# editor / assistant local config -.claude/ diff --git a/agents.md b/agents.md new file mode 100644 index 0000000..1946ad7 --- /dev/null +++ b/agents.md @@ -0,0 +1,310 @@ +# agents.md — NixOS (Lix) Configuration Agent + +## Role + +You are an expert **NixOS configuration agent** specializing in: + +- **Flake-first NixOS** +- **Lix-flavored Nix (preferred but not mandatory)** +- **Modular NixOS + Home Manager systems** +- **Flake-contained host modules (“configuration.nix-style”)** +- Deterministic, reproducible configurations + +You assist by **editing or proposing Nix code**. +You do **not** explain Nix concepts unless explicitly requested. + +--- + +## Authority Model + +1. **`flake.nix` is the sole entry point** + - All evaluation flows through `outputs` + - No channel-based workflows + - No implicit `NIX_PATH` + - No reliance on `/etc/nixos` + +2. **All systems are flakes** + - Legacy `configuration.nix` as an entry point is forbidden + - Files that *look* like `configuration.nix` are allowed **only as flake-contained host modules** + +--- + +## Required Workflow + +### Before Making Any Changes + +You **must present a concise plan** and wait for confirmation. + +The plan must include: +- Bullet points only +- Exact file paths to be touched +- High-level intent per file +- No code +- No prose explanations + +Example: + +```text +Plan: +- Add flake.nix with pinned nixpkgs +- Add hosts/laptop.nix as host module +- Add modules/system/base.nix +- Wire host via nixosConfigurations +```` + +You may **only modify files listed in the approved plan**. + +Scope is **strict**. + +### Permission Gates (New Directive) + +You must ask for explicit confirmation before doing any of the following: + +- Running tests/builds/evaluations (e.g. `nix build`, `nix flake check`, `nixos-rebuild`, `home-manager switch`) +- Creating commits (`git commit`, `git revert`, etc.) +- Pushing to any remote (`git push`, etc.) + +If you already presented a plan, you must still ask again before crossing one of +these gates. + +--- + +## Flake-Contained `configuration.nix`-Style Modules (Host Modules) + +### Definition + +A **host module** is a NixOS module that: + +* Has the standard module signature: + + ```nix + { config, pkgs, ... }: + ``` +* Looks like a traditional `configuration.nix` +* Is **not** an entry point +* Is **only evaluated via `flake.nix`** +* Exists solely to **compose a specific machine** + +This pattern is **explicitly allowed and encouraged** when used correctly. + +--- + +### Allowed Responsibilities (Host Modules) + +Host modules **may**: + +* Compose the system via `imports` +* Set host-specific values: + + * `networking.hostName` + * `system.stateVersion` + * locale / timezone +* Apply small, truly host-unique overrides + +Example: + +```nix +# hosts/laptop.nix +{ config, pkgs, ... }: + +{ + imports = [ + ../modules/system/base.nix + ../modules/desktop/wayland.nix + ../modules/users/tlalit.nix + ]; + + networking.hostName = "laptop"; + system.stateVersion = "24.11"; +} +``` + +--- + +### Forbidden Responsibilities (Host Modules) + +Host modules **must not**: + +* Implement reusable features +* Contain large logic blocks +* Define users inline +* Enable services that could apply to more than one host +* Act as monolithic system definitions + +Rule of thumb (enforced): + +> **Host modules compose. Feature modules implement.** + +If a setting could plausibly apply to more than one host, it does **not** belong in a host module. + +--- + +## Module Categories + +### Host Modules + +* Path: `hosts/*.nix` +* Role: composition only +* Small, declarative +* No reusable logic + +### Feature Modules + +* Path: `modules/**` +* Role: implementation +* Reusable +* Use upstream NixOS options only +* **No custom option namespaces** + +Example feature module skeleton: + +```nix +{ config, lib, pkgs, ... }: + +{ + config = { + # implementation using upstream options + }; +} +``` + +--- + +## Home Manager Policy + +* Home Manager is **provided as a CLI tool system-wide** (`home-manager` in `environment.systemPackages`). +* Users manage their own HM configs (per-user, standalone). No system-wide HM module imports. +* `modules/users/*` must not declare `home-manager.users.*`; keep user accounts declarative via NixOS only. + +--- + +## Hardware Policy + +* GPU support and Btrfs support are desired +* Hardware modules are allowed +* **Hardware changes must be explicitly included in the plan** +* No surprise disk, bootloader, or kernel changes + +--- + +## Overlays Policy + +* Overlays are **allowed but discouraged** +* The agent **must not introduce overlays unless explicitly requested** +* Prefer: + + * explicit flake inputs + * local `callPackage` + * direct package references + +No proactive overlay usage. + +--- + +## Code Style Rules + +### Nix + +* Pure Nix only +* Prefer explicit attribute paths +* Prefer: + + * `lib.mkIf` + * `lib.mkMerge` + * `lib.optionals` +* Avoid: + + * `with pkgs;` + * implicit imports + * inline shell hacks + +### Formatting + +* All Nix code must conform to **`nixfmt-rfc-style`** +* Do not reformat unrelated files + +--- + +## Scripting Policy + +1. **Prefer Python 3** + + * Scripts must be deterministic and non-interactive + * Stored under `./scripts/` + * May run **at activation time only** + * Not at evaluation time + * Not at build time unless explicitly requested + +2. **Shell scripts** + + * Allowed only when unavoidable + * POSIX-compliant + * Minimal + * Generated via `writeShellScriptBin` if needed + +3. **Never embed large scripts inline** + +--- + +## Expected Repository Layout + +```text +. +├── flake.nix +├── flake.lock +├── hosts/ +│ └── hostname.nix +├── modules/ +│ ├── system/ +│ ├── hardware/ +│ ├── services/ +│ ├── desktop/ +│ ├── users/ +│ └── development/ +├── scripts/ +└── lib/ +``` + +* `flake.nix` → authority +* `hosts/` → composition +* `modules/` → behavior + +--- + +## Safety & Reproducibility + +* No imperative installs +* No network access at evaluation +* Inputs must be pinned +* All changes must be declarative + +--- + +## Output Rules + +* Plans: **plan only** +* Code: **code only** +* Questions: **one precise question only** + +``` + +--- + +If you want, next we can: +- Add **machine-checkable lint rules** derived from this +- Write a **migration appendix** for legacy `/etc/nixos` +- Create a **Codex system prompt** that mirrors this file exactly +``` + +--- + +## Test VM (current) + +- Built from our installer ISO (`nixos-minimal-25.11.20260130.63590ac-x86_64-linux.iso`). +- Hostname: `nanuqsaurus`. +- User: `admin` / password `admin`. +- Network: DHCP on primary interface (e.g., `ensp1s0` in VM); SSH reachable once IP obtained. +- Purpose: sandbox for validating flake changes before baking into the ISO. +- Workflow: sync repo to VM and `nixos-rebuild switch --flake /etc/nixos#installer`; rebuild ISO **only on explicit request** (ISO must remain hardware-agnostic, no embedded host-specific files). +- Host-local files: the installer writes `hosts/local-.nix` and `hosts/local--hardware.nix` on the target. These are ignored by git. When syncing, exclude them to avoid deletion: + `rsync -a --exclude 'hosts/local-*.nix' --exclude 'hosts/*-hardware.nix' --exclude '.git' . admin@:/home/admin/nixos-sync` diff --git a/docs/adopt.md b/docs/adopt.md index 83a56e1..3598c3e 100644 --- a/docs/adopt.md +++ b/docs/adopt.md @@ -22,7 +22,7 @@ On the target machine: description = "Local wrapper for the nanuqsaurus profile"; inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/nixos-26.05"; + nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11"; nanuqsaurus = { url = "git+https://codeberg.org/randogoth/nanuqsaurus.git"; inputs.nixpkgs.follows = "nixpkgs"; @@ -53,8 +53,6 @@ On the target machine: { # Optional: change the default admin username ("admin") nanuqsaurus.admin.username = "admin"; - # Optional: enable impermanence (requires a /persist Btrfs subvolume and mounts in hardware-configuration.nix) - # nanuqsaurus.impermanence.enable = true; networking.hostName = "nanuqsaurus"; system.stateVersion = "25.11"; } @@ -77,28 +75,3 @@ sudo nixos-rebuild switch --flake /etc/nixos#nanuqsaurus - `sudo nixos-rebuild switch --flake /etc/nixos#nanuqsaurus` - Keep all your machine-specific changes in the wrapper flake (additional modules, packages, services, etc.). - -## Impermanence (optional) - -If you enable `nanuqsaurus.impermanence.enable = true;` you must: - -- Add a `/persist` mount (Btrfs subvolume) in `hardware-configuration.nix`. -- Migrate state you care about into `/persist` before the first reboot. - -Notes: - -- The rollback logic expects the root Btrfs volume label to be `nixos`. -- Create the subvolume with `btrfs subvolume create /mnt/persist` and mount it as `/persist`. - -The default persistence set includes: - -- `/etc/nixos` -- `/etc/ssh` -- `/etc/cups` -- `/var/lib/cups` -- `/var/lib/libvirt` -- `/var/lib/secureboot` -- `/var/lib/tailscale` -- `/var/lib/systemd` -- `/var/lib/nixos` -- `/etc/machine-id` diff --git a/flake.lock b/flake.lock index 80a9f4b..21a6dd0 100644 --- a/flake.lock +++ b/flake.lock @@ -125,6 +125,39 @@ "type": "github" } }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flakey-profile": { + "locked": { + "lastModified": 1712898590, + "narHash": "sha256-FhGIEU93VHAChKEXx905TSiPZKga69bWl1VB37FK//I=", + "owner": "lf-", + "repo": "flakey-profile", + "rev": "243c903fd8eadc0f63d205665a92d4df91d42d9d", + "type": "github" + }, + "original": { + "owner": "lf-", + "repo": "flakey-profile", + "type": "github" + } + }, "flox": { "inputs": { "crane": "crane", @@ -255,6 +288,44 @@ "type": "github" } }, + "lix": { + "flake": false, + "locked": { + "lastModified": 1768477252, + "narHash": "sha256-RgET5pILoiG+3GnDpgfUEA4y8ISfZe6SB+geE+8U76Y=", + "rev": "fbe811e94e9c4efc9b9c283fe2fbb3aeff50fca7", + "type": "tarball", + "url": "https://git.lix.systems/api/v1/repos/lix-project/lix/archive/fbe811e94e9c4efc9b9c283fe2fbb3aeff50fca7.tar.gz?rev=fbe811e94e9c4efc9b9c283fe2fbb3aeff50fca7" + }, + "original": { + "type": "tarball", + "url": "https://git.lix.systems/lix-project/lix/archive/release-2.93.tar.gz" + } + }, + "lix-module": { + "inputs": { + "flake-utils": "flake-utils", + "flakey-profile": "flakey-profile", + "lix": "lix", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1756125859, + "narHash": "sha256-6a+PWILmqHCs9B5eIBLg6HSZ8jYweZpgOWO8FlyVwYI=", + "ref": "release-2.93", + "rev": "d3292125035b04df00d01549a26e948631fabe1e", + "revCount": 156, + "type": "git", + "url": "https://git.lix.systems/lix-project/nixos-module" + }, + "original": { + "ref": "release-2.93", + "type": "git", + "url": "https://git.lix.systems/lix-project/nixos-module" + } + }, "nix-flatpak": { "locked": { "lastModified": 1767983141, @@ -309,16 +380,16 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1787753485, - "narHash": "sha256-BZWCi9ZRJiARTuKTbbtvFTj7t1TK4G3UEckT3HyNfRg=", + "lastModified": 1769741972, + "narHash": "sha256-RxSg1EioTWNpoLaykiT1UQKTo/K0PPdLqCyQgNjNqWs=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "062346a6d85bc4b49dfaa61c986e9c5be21217d1", + "rev": "63590ac958a8af30ebd52c7a0309d8c52a94dd77", "type": "github" }, "original": { "owner": "NixOS", - "ref": "nixos-26.05", + "ref": "nixos-25.11", "repo": "nixpkgs", "type": "github" } @@ -396,6 +467,7 @@ "flox": "flox", "impermanence": "impermanence", "lanzaboote": "lanzaboote", + "lix-module": "lix-module", "nix-flatpak": "nix-flatpak", "nix-index-database": "nix-index-database", "nixpkgs": "nixpkgs_2", @@ -439,6 +511,21 @@ "repo": "rust-overlay", "type": "github" } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } } }, "root": "root", diff --git a/flake.nix b/flake.nix index 66a10f5..ff9c8e2 100644 --- a/flake.nix +++ b/flake.nix @@ -2,7 +2,7 @@ description = "NixOS installation ISO with Lix"; inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/nixos-26.05"; + nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11"; disko = { url = "github:nix-community/disko"; inputs.nixpkgs.follows = "nixpkgs"; @@ -20,6 +20,8 @@ url = "github:nix-community/NUR"; inputs.nixpkgs.follows = "nixpkgs"; }; + lix-module.url = "git+https://git.lix.systems/lix-project/nixos-module?ref=release-2.93"; + lix-module.inputs.nixpkgs.follows = "nixpkgs"; lanzaboote.url = "github:nix-community/lanzaboote"; lanzaboote.inputs.nixpkgs.follows = "nixpkgs"; flox.url = "github:flox/flox"; diff --git a/laptop.md b/laptop.md deleted file mode 100644 index c59c2b3..0000000 --- a/laptop.md +++ /dev/null @@ -1,208 +0,0 @@ -# Install Nanuqsaurus On An Empty Partition (No Full-Disk Wipe) - -This repo’s `nanuqsaurus-btrfs` “fresh install” path uses `disko` to create a brand new GPT layout (EFI + Btrfs) and therefore **wipes the whole disk**. If you want to install onto an **existing empty partition** while keeping other partitions intact, use the “wrapper flake” approach and a manual mount + `nixos-install`. - -These steps are meant to be run from a **NixOS installer ISO** on the target laptop. - -## 0) Preconditions - -- Boot the installer in **UEFI mode** (this profile enables `systemd-boot` + EFI). -- You have working network connectivity (WiFi instructions below). -- You have: - - an existing **empty root partition** you will format (example: `/dev/nvme0n1p6`) - - an existing **EFI System Partition** (vfat) you will mount at `/boot` (example: `/dev/nvme0n1p1`) - -Confirm UEFI: - -```bash -test -d /sys/firmware/efi/efivars && echo "UEFI booted" || echo "NOT UEFI booted" -``` - -## 0.5) Connect To WiFi (Installer) - -Most NixOS installer ISOs run NetworkManager. The easiest flow is `nmtui`. - -1) Bring up the text UI and connect: - -```bash -sudo -i -nmtui -``` - -In `nmtui`: - -- Select `Activate a connection` -- Pick your WiFi SSID -- Enter the password -- Exit - -2) Verify you have connectivity: - -```bash -ping -c 1 1.1.1.1 -ping -c 1 example.com -``` - -If `nmtui` is unavailable, try: - -```bash -nmcli dev status -nmcli dev wifi list -nmcli dev wifi connect "YOUR_SSID" password "YOUR_PASSWORD" -``` - -## 1) Identify Your Partitions - -```bash -lsblk -f -``` - -Pick stable paths if available (`/dev/disk/by-id/...`). In the commands below, set: - -- `ROOT_PART` = the empty partition to become `/` -- `EFI_PART` = the EFI System Partition (vfat) to become `/boot` - -Example: - -```bash -ROOT_PART=/dev/nvme0n1p6 -EFI_PART=/dev/nvme0n1p1 -``` - -## 2) Format The Root Partition - -Warning: this destroys data on `ROOT_PART`. - -Recommended (Btrfs with subvolumes similar to this repo’s intended layout): - -```bash -mkfs.btrfs -f -L nixos "$ROOT_PART" - -mount "$ROOT_PART" /mnt -btrfs subvolume create /mnt/root -btrfs subvolume create /mnt/home -btrfs subvolume create /mnt/nix -umount /mnt - -mount -o subvol=root,compress=zstd "$ROOT_PART" /mnt -mkdir -p /mnt/home /mnt/nix -mount -o subvol=home,compress=zstd "$ROOT_PART" /mnt/home -mount -o subvol=nix,compress=zstd "$ROOT_PART" /mnt/nix -``` - -Alternative (ext4): - -```bash -mkfs.ext4 -F -L nixos "$ROOT_PART" -mount "$ROOT_PART" /mnt -mkdir -p /mnt/nix -``` - -## 3) Mount EFI At `/boot` - -```bash -mkdir -p /mnt/boot -mount "$EFI_PART" /mnt/boot -``` - -## 4) Generate Hardware Config - -Important: do this **after** mounting `/boot`, so the EFI mount gets captured. - -```bash -nixos-generate-config --root /mnt -``` - -This creates: - -- `/mnt/etc/nixos/hardware-configuration.nix` -- `/mnt/etc/nixos/configuration.nix` (we will not use this directly) - -## 5) Create A Wrapper Flake In `/etc/nixos` - -This repo’s `nanuqsaurus` profile defines an `admin` user and also tends to lock `root` by default. To avoid surprises, set your own password hashes in the wrapper flake using `lib.mkForce`. - -If you have a Lenovo ThinkPad X13 (Intel), you can also import the `nixos-hardware` module for it in this wrapper flake. - -1) Generate SHA-512 password hashes: - -```bash -nix --experimental-features "nix-command flakes" \ - shell nixpkgs#mkpasswd -c mkpasswd -- -m sha-512 -``` - -Run it twice if you want to set both `admin` and `root`. - -2) Create `/mnt/etc/nixos/flake.nix`: - -```bash -nano /mnt/etc/nixos/flake.nix -``` - -Paste and edit: - -```nix -{ - description = "Local wrapper for the nanuqsaurus profile"; - - inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/nixos-26.05"; - nanuqsaurus = { - url = "git+https://codeberg.org/randogoth/nanuqsaurus.git"; - inputs.nixpkgs.follows = "nixpkgs"; - }; - # Optional hardware profiles (recommended on laptops) - nixos-hardware = { - url = "github:NixOS/nixos-hardware"; - inputs.nixpkgs.follows = "nixpkgs"; - }; - }; - - outputs = inputs@{ nixpkgs, nanuqsaurus, nixos-hardware, ... }: { - nixosConfigurations.nanuqsaurus = nixpkgs.lib.nixosSystem { - system = "x86_64-linux"; - specialArgs = { inherit inputs; }; - modules = [ - ./hardware-configuration.nix - # Optional: ThinkPad X13 (Intel) profile (path: lenovo/thinkpad/x13/intel) - # Remove or replace if you have different hardware. - nixos-hardware.nixosModules.lenovo-thinkpad-x13-intel - nanuqsaurus.nixosModules.nanuqsaurus - ({ lib, ... }: { - networking.hostName = "nanuqsaurus"; - system.stateVersion = "25.11"; - - nanuqsaurus.admin.username = "admin"; - users.users.admin.hashedPassword = lib.mkForce "PUT_ADMIN_SHA512_HASH_HERE"; - - # Recommended so you can recover easily: - users.users.root.hashedPassword = lib.mkForce "PUT_ROOT_SHA512_HASH_HERE"; - - # Optional: require sudo password (this repo defaults to passwordless sudo) - security.sudo.wheelNeedsPassword = lib.mkForce true; - }) - ]; - }; - }; -} -``` - -## 6) Install NixOS - -```bash -NIX_CONFIG="experimental-features = nix-command flakes" \ - nixos-install --root /mnt --flake /mnt/etc/nixos#nanuqsaurus -``` - -## 7) Reboot - -```bash -reboot -``` - -If your firmware shows multiple boot options, select the systemd-boot entry for the disk containing the EFI partition. - -## Notes - -- Secure Boot: this repo’s installer ISO expects Secure Boot off at boot; after installation you can enable Secure Boot using Lanzaboote (see `docs/secure-boot.md`). -- If you actually want to wipe an entire disk and let this repo handle partitioning, use `disko-install` with `#nanuqsaurus-btrfs` (see `README.md` and `docs/install-help.txt`). diff --git a/modules/desktop/desktop-entries.nix b/modules/desktop/desktop-entries.nix index 04e540b..7da1689 100644 --- a/modules/desktop/desktop-entries.nix +++ b/modules/desktop/desktop-entries.nix @@ -1,6 +1,30 @@ { lib, pkgs, ... }: let + thunarDesktopOverride = pkgs.makeDesktopItem { + name = "thunar"; + desktopName = "Files"; + genericName = "File Manager"; + tryExec = "thunar"; + exec = "thunar %U"; + icon = "org.xfce.thunar"; + categories = [ + "Utility" + "FileManager" + ]; + mimeTypes = [ "inode/directory" ]; + keywords = [ + "folder" + "manager" + "explorer" + "files" + ]; + startupNotify = true; + startupWMClass = "Thunar"; + terminal = false; + comment = "Browse files"; + }; + ptyxisDesktopOverride = pkgs.makeDesktopItem { name = "org.gnome.Ptyxis"; desktopName = "Terminal"; @@ -17,6 +41,7 @@ let in { environment.systemPackages = lib.mkAfter [ + (lib.hiPrio thunarDesktopOverride) (lib.hiPrio ptyxisDesktopOverride) ]; } diff --git a/modules/desktop/file-manager.nix b/modules/desktop/file-manager.nix new file mode 100644 index 0000000..9764e7d --- /dev/null +++ b/modules/desktop/file-manager.nix @@ -0,0 +1,27 @@ +{ pkgs, ... }: + +{ + environment.etc."xdg/xfce4/xfconf/xfce-perchannel-xml/exo.xml".text = '' + + + + + + + ''; + + services.gvfs.enable = true; + services.tumbler.enable = true; + + programs.thunar = { + enable = true; + plugins = [ + pkgs.xfce.thunar-archive-plugin + pkgs.xfce.thunar-media-tags-plugin + pkgs.xfce.thunar-vcs-plugin + pkgs.xfce.thunar-volman + ]; + }; + + programs.xfconf.enable = true; +} diff --git a/modules/desktop/gnome.nix b/modules/desktop/gnome.nix index 70800fd..929d076 100644 --- a/modules/desktop/gnome.nix +++ b/modules/desktop/gnome.nix @@ -2,18 +2,13 @@ let logoMenuExtension = pkgs.gnomeExtensions.logo-menu.overrideAttrs (old: { - patches = (old.patches or [ ]) ++ [ ./patches/logo-menu-custom-menu.patch ]; - }); - - # nixos-26.05 still packages v19, which caps out at GNOME Shell 49; the - # shell-50 build landed only in nixpkgs unstable. Drop once 26.05 backports it. - aioClipboardExtension = pkgs.gnomeExtensions.all-in-one-clipboard.overrideAttrs (_: { - version = "30"; + version = "38"; src = pkgs.fetchzip { - url = "https://extensions.gnome.org/extension-data/all-in-one-clipboardNiffirgkcaJ.github.com.v30.shell-extension.zip"; - hash = "sha256-zESXhXVWVttjAN/75zNfF7u7uJKGMARpilBgYkkpMWs="; + url = "https://extensions.gnome.org/extension-data/logomenuaryan_k.v38.shell-extension.zip"; + hash = "sha256-GdGn7YKfOOEM0kV1Gd9H5c1KKjs/+/4XCsxO2pFdqu4="; stripRoot = false; }; + patches = (old.patches or [ ]) ++ [ ./patches/logo-menu-custom-menu.patch ]; }); in { @@ -35,9 +30,6 @@ in services.displayManager.gdm.enable = true; services.desktopManager.gnome.enable = true; - # Virtual filesystem backend for Nautilus: mounting, trash, network shares. - services.gvfs.enable = true; - services.gnome.core-developer-tools.enable = false; services.gnome.games.enable = false; @@ -49,7 +41,7 @@ in "org/gnome/shell" = { disable-user-extensions = false; favorite-apps = [ - "org.gnome.Nautilus.desktop" + "thunar.desktop" "org.gnome.Ptyxis.desktop" "com.ranfdev.DistroShelf.desktop" "io.github.kolunmi.Bazaar.desktop" @@ -102,9 +94,7 @@ in pkgs.gnome-extension-manager pkgs.mission-center pkgs.papirus-icon-theme - pkgs.file-roller - pkgs.unrar - aioClipboardExtension + pkgs.gnomeExtensions.all-in-one-clipboard pkgs.gnomeExtensions.alphabetical-app-grid pkgs.gnomeExtensions.app-hider pkgs.gnomeExtensions.apps @@ -139,6 +129,7 @@ in pkgs.gnome-system-monitor pkgs.gnome-weather pkgs.loupe + pkgs.nautilus pkgs.papers # gnome-connections pkgs.showtime diff --git a/modules/profile/nanuqsaurus.nix b/modules/profile/nanuqsaurus.nix index 724d416..0904657 100644 --- a/modules/profile/nanuqsaurus.nix +++ b/modules/profile/nanuqsaurus.nix @@ -1,11 +1,8 @@ { inputs }: -{ pkgs, ... }: +{ ... }: { - # Use Lix from nixpkgs (2.94.x on nixos-26.05). The lix-module's - # lixFromNixpkgs still pins lix_2_93, which nixos-26.05 removed. - nix.package = pkgs.lix; - imports = [ + inputs.lix-module.nixosModules.lixFromNixpkgs inputs.nix-index-database.nixosModules.nix-index inputs.nix-flatpak.nixosModules.nix-flatpak inputs.impermanence.nixosModules.impermanence @@ -19,6 +16,7 @@ ../system/printer.nix ../system/impermanence.nix ../desktop/desktop-entries.nix + ../desktop/file-manager.nix ../desktop/gnome.nix ../users/admin.nix ]; diff --git a/modules/system/disko-btrfs.nix b/modules/system/disko-btrfs.nix index 450f26a..08e0a57 100644 --- a/modules/system/disko-btrfs.nix +++ b/modules/system/disko-btrfs.nix @@ -26,9 +26,8 @@ size = "100%"; content = { type = "btrfs"; - # Filesystem label "nixos" is required by the impermanence rollback - # logic. Newer disko dropped the `label` option; pass it to mkfs.btrfs. - extraArgs = [ "-f" "-L" "nixos" ]; + extraArgs = [ "-f" ]; + label = "nixos"; subvolumes = { root = { mountpoint = "/"; diff --git a/modules/system/impermanence.nix b/modules/system/impermanence.nix index ad10a1d..28bcb89 100644 --- a/modules/system/impermanence.nix +++ b/modules/system/impermanence.nix @@ -10,50 +10,36 @@ config = lib.mkIf config.nanuqsaurus.impermanence.enable { boot.initrd.supportedFilesystems = [ "btrfs" ]; - # Reset the /root subvolume to a pristine snapshot on every boot. - # systemd stage-1 initrd does not support boot.initrd.postDeviceCommands, so - # this runs as an initrd systemd service, ordered after the labelled device - # appears and before the root filesystem is mounted (sysroot.mount). - # Note: `cut` (coreutils) is used instead of `awk`, which is not in the - # systemd initrd. - boot.initrd.systemd.services.rollback = { - description = "Rollback btrfs root subvolume to a pristine state"; - wantedBy = [ "initrd.target" ]; - after = [ "dev-disk-by\\x2dlabel-nixos.device" ]; - before = [ "sysroot.mount" ]; - unitConfig.DefaultDependencies = "no"; - serviceConfig.Type = "oneshot"; - script = '' - mkdir -p /mnt - mount -t btrfs -o subvol=/ /dev/disk/by-label/nixos /mnt + boot.initrd.postDeviceCommands = lib.mkAfter '' + mkdir -p /mnt + mount -t btrfs -o subvol=/ /dev/disk/by-label/nixos /mnt - if [ ! -d /mnt/root-blank ]; then - btrfs subvolume snapshot -r /mnt/root /mnt/root-blank + if [ ! -d /mnt/root-blank ]; then + btrfs subvolume snapshot -r /mnt/root /mnt/root-blank + fi + + if [ -d /mnt/root ]; then + if [ -d /mnt/root-previous ]; then + btrfs subvolume delete /mnt/root-previous fi + btrfs subvolume snapshot /mnt/root /mnt/root-previous - if [ -d /mnt/root ]; then - if [ -d /mnt/root-previous ]; then - btrfs subvolume delete /mnt/root-previous + while true; do + subvols="$(btrfs subvolume list -o /mnt/root | awk '{print $9}')" + if [ -z "$subvols" ]; then + break fi - btrfs subvolume snapshot /mnt/root /mnt/root-previous - - while true; do - subvols="$(btrfs subvolume list -o /mnt/root | cut -f9 -d' ')" - if [ -z "$subvols" ]; then - break - fi - for subvol in $subvols; do - btrfs subvolume delete "/mnt/$subvol" - done + for subvol in $subvols; do + btrfs subvolume delete "/mnt/$subvol" done + done - btrfs subvolume delete /mnt/root - fi + btrfs subvolume delete /mnt/root + fi - btrfs subvolume snapshot /mnt/root-blank /mnt/root - umount /mnt - ''; - }; + btrfs subvolume snapshot /mnt/root-blank /mnt/root + umount /mnt + ''; fileSystems."/persist".neededForBoot = true; diff --git a/modules/system/justfile.nix b/modules/system/justfile.nix index 506aad7..29036e9 100644 --- a/modules/system/justfile.nix +++ b/modules/system/justfile.nix @@ -26,29 +26,9 @@ let nix-collect-garbage -d [no-cd] - update target="": - #!/usr/bin/env bash - set -euo pipefail - - update_system() { - sudo nix flake update --flake "''${NIXOS_FLAKE:-/etc/nixos}" - just rebuild - } - - update_home() { - nix flake update --flake "''${XDG_CONFIG_HOME:-$HOME/.config}/home-manager" - just switch - } - - case "{{target}}" in - system) update_system ;; - home) update_home ;; - "") update_system; update_home ;; - *) - echo "usage: just update [system|home] (empty = both)" >&2 - exit 1 - ;; - esac + update: + sudo nix flake update --flake "''${NIXOS_FLAKE:-/etc/nixos}" + just rebuild [no-cd] init-home: diff --git a/scripts/shell-init.sh b/scripts/shell-init.sh index 0eb0168..0a3f4d1 100644 --- a/scripts/shell-init.sh +++ b/scripts/shell-init.sh @@ -37,43 +37,18 @@ fi (( INTERACTIVE )) || return 0 -# ---- run-once guard -------------------------------------------------------- - -# Installed as both loginShellInit (/etc/profile) and interactiveShellInit -# (/etc/bashrc), and /etc/profile sources /etc/bashrc, so a login shell would run -# this twice and register starship's hooks twice. Not exported: child shells must -# initialise on their own. -if [[ -n ${NANUQSAURUS_SHELL_INIT_DONE:-} ]]; then - log_shell_init "already initialised, skipping" - return 0 -fi -NANUQSAURUS_SHELL_INIT_DONE=1 - # ---- bash-preexec ---------------------------------------------------------- if [[ -n ${BASH_VERSION:-} && -z ${bash_preexec_imported:-} && -r "@BASHPREEXEC@" ]]; then # shellcheck disable=SC1090 . "@BASHPREEXEC@" + # Do NOT call __bp_install here. bash-preexec must install on the FIRST prompt, + # after starship/atuin/zoxide/ghostty have modified PROMPT_COMMAND, so that + # __bp_interactive_mode stays the LAST PROMPT_COMMAND entry. Installing eagerly + # lets later hooks land after it and silently breaks preexec (atuin recording). log_shell_init "bash-preexec loaded" fi -# bash-preexec arms its DEBUG trap from __bp_interactive_mode, which it appends to -# the END of PROMPT_COMMAND -- but its lazy installer runs in the MIDDLE of the -# first prompt, because zoxide (below) and ghostty (~/.bashrc) append their hooks -# after it. Those hooks then disarm the trap again, so the first command of every -# session never reaches preexec and atuin never records it. PS0 is expanded once -# per interactive command, before the DEBUG trap, so arming there is independent of -# PROMPT_COMMAND ordering. Needs the non-forking ${ ...; } funsub (bash >= 5.3); -# older bash keeps the previous behaviour. -if [[ -n ${bash_preexec_imported:-} ]] && - ((BASH_VERSINFO[0] > 5 || (BASH_VERSINFO[0] == 5 && BASH_VERSINFO[1] >= 3))); then - # shellcheck disable=SC2016 - __nanuq_bp_arm='${ __bp_preexec_interactive_mode=on; }' - [[ ${PS0-} == *"$__nanuq_bp_arm"* ]] || PS0=$__nanuq_bp_arm${PS0-} - unset __nanuq_bp_arm - log_shell_init "bash-preexec PS0 arming enabled" -fi - # ---- starship -------------------------------------------------------------- if have @STARSHIP@; then @@ -82,7 +57,8 @@ if have @STARSHIP@; then */bash) eval "$(@STARSHIP@ init bash)" ;; */zsh) eval "$(@STARSHIP@ init zsh)" ;; esac - # registers starship_precmd/starship_preexec_all with bash-preexec directly + # starship's bash init registers starship_precmd/starship_preexec_all with + # bash-preexec directly; no manual PROMPT_COMMAND wiring needed. else log_shell_init "starship skipped" fi @@ -101,7 +77,8 @@ if have @ATUIN@; then */bash) eval "$(@ATUIN@ init bash)" ;; */zsh) eval "$(@ATUIN@ init zsh)" ;; esac - # registers __atuin_preexec/__atuin_precmd with bash-preexec itself + # atuin's init registers __atuin_preexec/__atuin_precmd with bash-preexec + # itself; manual array wiring only caused duplicate hook entries. else log_shell_init "atuin skipped" fi @@ -114,7 +91,9 @@ if have zoxide; then */bash) eval "$(zoxide init bash)" ;; */zsh) eval "$(zoxide init zsh)" ;; esac - # appends __zoxide_hook to PROMPT_COMMAND; see the PS0 note above + # zoxide's bash init hooks into bash-preexec (precmd_functions) when it is + # present; manual PROMPT_COMMAND wiring only pushed hooks past + # __bp_interactive_mode and broke preexec ordering. else log_shell_init "zoxide skipped" fi