Compare commits

..

No commits in common. "eb9d7fb9b89742ca50197fd26f87b15c131d4887" and "1565c4cb5c7dca01a3ad2c985e75661baab1d0d8" have entirely different histories.

14 changed files with 506 additions and 360 deletions

3
.gitignore vendored
View file

@ -4,6 +4,3 @@ result
.DS_Store
hosts/*-hardware.nix
hosts/local-*.nix
# editor / assistant local config
.claude/

310
agents.md Normal file
View file

@ -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-<hostname>.nix` and `hosts/local-<hostname>-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@<vm>:/home/admin/nixos-sync`

View file

@ -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`

95
flake.lock generated
View file

@ -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",

View file

@ -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";

208
laptop.md
View file

@ -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`).

View file

@ -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)
];
}

View file

@ -0,0 +1,27 @@
{ pkgs, ... }:
{
environment.etc."xdg/xfce4/xfconf/xfce-perchannel-xml/exo.xml".text = ''
<?xml version="1.0" encoding="UTF-8"?>
<channel name="exo" version="1.0">
<property name="helpers" type="empty">
<property name="TerminalEmulator" type="string" value="${pkgs.ptyxis}/bin/ptyxis"/>
</property>
</channel>
'';
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;
}

View file

@ -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

View file

@ -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
];

View file

@ -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 = "/";

View file

@ -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;

View file

@ -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:

View file

@ -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