Compare commits

..

8 Commits

39 changed files with 1342 additions and 299 deletions
+29 -7
View File
@@ -47,7 +47,7 @@ Leave upstream `services.*`, `networking.*`, `home.*`, etc. alone.
Aggregates: `nixosModules.system` and `nixosModules.desktop` import their leaves. Host configs import those stacks and set options — they shouldn't reimplement whole subsystems. Aggregates: `nixosModules.system` and `nixosModules.desktop` import their leaves. Host configs import those stacks and set options — they shouldn't reimplement whole subsystems.
**Home** — files under `modules/wisdom/`. Baseline is `homeManagerModules.wisdom` (`chiasson.home.enable`). Everything else is separate exports (`wisdomBrowsersZen`, …); enable on a host via `chiasson.users.extraModules.<user>` and the matching `chiasson.home.*.enable`. **Home** — files under `modules/wisdom/`. Baseline is `homeManagerModules.wisdom` (`chiasson.home.enable`, wired via `chiasson.desktop.homeManager.bundleWisdom`). Other `wisdom*` slices auto-wire once per user via `lib.wisdomCatalogExtraModules self` (`modules/lib/wisdom-catalog.nix`); hosts only set matching `chiasson.home.*.enable` toggles — no re-import in `home.nix`.
User apps / dotfiles → wisdom. Daemons, firewall, kernel → NixOS. Sometimes both (LocalSend: HM installs, `systemLocalsend` opens the firewall). User apps / dotfiles → wisdom. Daemons, firewall, kernel → NixOS. Sometimes both (LocalSend: HM installs, `systemLocalsend` opens the firewall).
@@ -72,14 +72,36 @@ Passwords aren't in the repo. They're in `secrets/secrets.yaml` (encrypted with
On a host: On a host:
```nix ```nix
# configuration.nix — machine policy
chiasson.users.enabled = [ "olivier" ]; chiasson.users.enabled = [ "olivier" ];
chiasson.users.hostOverrides.<name> = { /* optional */ }; chiasson.users.hostOverrides.<name> = { /* optional */ };
chiasson.users.extraModules.olivier = [
self.homeManagerModules.wisdomTerminalsKitty
# …
];
``` ```
Desktop hosts also have `home.nix` exporting `flake.nixosModules.<host>Home`, wired from `default.nix` alongside `*Configuration`:
```nix
# default.nix
modules = [
self.nixosModules."14900kConfiguration"
self.nixosModules."14900kHome"
];
# home.nix — flake fragment, per-host `chiasson.home.*` toggles
{ self, inputs, ... }: {
flake.nixosModules."14900kHome" = { self, pkgs, ... }: {
imports = [ self.nixosModules.desktopHomeBase ];
chiasson.users.extraModules.olivier = [
{
chiasson.home.browsers.edge.enable = true;
# …
}
];
};
}
```
`flake.nixosModules.desktopHomeBase` expands `lib.wisdomCatalogExtraModules` plus shared desktop toggles. Host `*Home` modules append per-host `chiasson.home` overrides (and rare inline `home.packages` blocks). `chiasson.users.extraModules` concatenates lists from multiple modules (base + host), so both can set the same user key.
`usersHomeIntegration` turns that into `users.users` + HM. Don't hand-roll catalog users unless you're changing the users module itself. `usersHomeIntegration` turns that into `users.users` + HM. Don't hand-roll catalog users unless you're changing the users module itself.
SSH: `sshInbound` on NixOS, outbound/rbw under `modules/ssh/home-manager/`. SSH: `sshInbound` on NixOS, outbound/rbw under `modules/ssh/home-manager/`.
@@ -88,11 +110,11 @@ SSH: `sshInbound` on NixOS, outbound/rbw under `modules/ssh/home-manager/`.
**New NixOS leaf:** export `flake.nixosModules.whatever`, wire from `system/default.nix` or `desktop/default.nix` if it's global, or only from a host `configuration.nix` if it's not. `nix flake check`. Git-add new paths if eval uses the git tree. **New NixOS leaf:** export `flake.nixosModules.whatever`, wire from `system/default.nix` or `desktop/default.nix` if it's global, or only from a host `configuration.nix` if it's not. `nix flake check`. Git-add new paths if eval uses the git tree.
**New HM slice:** export `flake.homeManagerModules.wisdomFoo`, add the file to `imports` in `modules/wisdom/default.nix`, then wire `extraModules` + options on hosts that need it. **New HM slice:** add `modules/wisdom/.../foo.nix` exporting `flake.homeManagerModules.wisdomFoo` (import-tree picks it up; `wisdomCatalogExtraModules` includes every `wisdom*` export except `wisdom` / `wisdomShellBash`). Gate packages on `chiasson.home.*.enable`, set `mkDefault true` in `desktop-home-base.nix` if shared, or `enable = true` in a host `home.nix`. Upstream HM deps stay imported unconditionally — use `mkIf` on `cfg.enable` for config (never `config`-dependent `imports`; that recurses).
**Derivations:** `let` inside a fragment, or `flake.packages` / `flake.lib` — not a bare `mkDerivation` file import-tree will try to load. **Derivations:** `let` inside a fragment, or `flake.packages` / `flake.lib` — not a bare `mkDerivation` file import-tree will try to load.
**New host:** `hosts/<name>/default.nix` with `nixosSystem`, `configuration.nix` exporting `*Configuration`, hardware + `_private/` as needed, register in `modules/deploy/navi.nix` if it should be in the fleet. **New host:** `hosts/<name>/default.nix` with `nixosSystem` listing `*Configuration` (+ `*Home` for desktops), `configuration.nix` exporting `*Configuration`, hardware + `_private/` as needed. Desktop/laptop hosts also get `home.nix` exporting `*Home` (import `self.nixosModules.desktopHomeBase` unless it's a special case). Register in `modules/deploy/navi.nix` if it should be in the fleet.
## When editing ## When editing
Generated
+20
View File
@@ -706,6 +706,25 @@
"type": "github" "type": "github"
} }
}, },
"nixvirt": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1748140003,
"narHash": "sha256-DNBZmuk1YRM2PmwbHzVdXumRjCUzQkMarg4iI/37rOQ=",
"rev": "5dfe108fd859b122f9a96981cb6bc12297653d6c",
"revCount": 407,
"type": "tarball",
"url": "https://api.flakehub.com/f/pinned/AshleyYakeley/NixVirt/0.6.0/0197059a-e45f-7446-86b5-411ccc894ab0/source.tar.gz"
},
"original": {
"type": "tarball",
"url": "https://flakehub.com/f/AshleyYakeley/NixVirt/%2A.tar.gz"
}
},
"nur": { "nur": {
"inputs": { "inputs": {
"flake-parts": "flake-parts_5", "flake-parts": "flake-parts_5",
@@ -809,6 +828,7 @@
"nixcord": "nixcord", "nixcord": "nixcord",
"nixos-raspberrypi": "nixos-raspberrypi", "nixos-raspberrypi": "nixos-raspberrypi",
"nixpkgs": "nixpkgs_2", "nixpkgs": "nixpkgs_2",
"nixvirt": "nixvirt",
"nur": "nur", "nur": "nur",
"oom-hardware": "oom-hardware", "oom-hardware": "oom-hardware",
"personal-website": "personal-website", "personal-website": "personal-website",
+5
View File
@@ -105,6 +105,11 @@
}; };
navi.url = "github:cafkafk/navi"; navi.url = "github:cafkafk/navi";
nixvirt = {
url = "https://flakehub.com/f/AshleyYakeley/NixVirt/*.tar.gz";
inputs.nixpkgs.follows = "nixpkgs";
};
}; };
outputs = inputs: outputs = inputs:
+1 -1
View File
@@ -99,7 +99,7 @@
# Default keybinds # Default keybinds
bind = bind =
[ [
"SUPER,T,exec,kitty -e fish" "SUPER,T,exec,kitty"
"ControlSuper,T,exec,konsole" "ControlSuper,T,exec,konsole"
"SUPER,D,exec,rofi -show drun" "SUPER,D,exec,rofi -show drun"
"ControlSuper,D,exec,rofi -show window" "ControlSuper,D,exec,rofi -show window"
+19 -2
View File
@@ -29,7 +29,7 @@ let
]; ];
#TODO[epic=Binds] Go over binds again #TODO[epic=Binds] Go over binds again
binds = { binds = {
"Mod+T"."spawn-sh" = "${pkgs.lib.getExe pkgs.kitty} -e fish"; #TODO[epic=Binds] This should only be set if having kitty "Mod+T"."spawn-sh" = pkgs.lib.getExe pkgs.kitty; # shell from kitty HM config
"Mod+Control+T"."spawn-sh" = "konsole"; #TODO[epic=Binds] This should only be set if having konsole "Mod+Control+T"."spawn-sh" = "konsole"; #TODO[epic=Binds] This should only be set if having konsole
"Mod+D"."spawn-sh" = "rofi -show drun"; #TODO[epic=Binds] This should only be set if having rofi "Mod+D"."spawn-sh" = "rofi -show drun"; #TODO[epic=Binds] This should only be set if having rofi
"Mod+Space"."spawn-sh" = "dms ipc call spotlight toggle"; #TODO[epic=Binds] This should only be set if having dms "Mod+Space"."spawn-sh" = "dms ipc call spotlight toggle"; #TODO[epic=Binds] This should only be set if having dms
@@ -139,9 +139,17 @@ let
keyringExtra = lib.optionalString keyringEnable keyringNiriStartupKdl; keyringExtra = lib.optionalString keyringEnable keyringNiriStartupKdl;
extraConfigMerged = keyringExtra + rpi5Extra + (userExtra.extraConfig or ""); extraConfigMerged = keyringExtra + rpi5Extra + (userExtra.extraConfig or "");
windowRules = (base.window-rules or [ ]) ++ (userExtra.window-rules or [ ]); windowRules = (base.window-rules or [ ]) ++ (userExtra.window-rules or [ ]);
userExtraNoSpecial = lib.removeAttrs userExtra [ "window-rules" "extraConfig" "binds" ];
mergedBinds =
(base.binds or { })
// (userExtra.binds or { })
// (niriCfg.extraBinds or { });
in in
lib.recursiveUpdate base ( lib.recursiveUpdate base (
lib.removeAttrs userExtra [ "window-rules" "extraConfig" ] userExtraNoSpecial
// {
binds = mergedBinds;
}
// lib.optionalAttrs (windowRules != [ ]) { // lib.optionalAttrs (windowRules != [ ]) {
window-rules = windowRules; window-rules = windowRules;
} }
@@ -195,6 +203,15 @@ in
Merged into shared defaults `config.kdl` via wrapper-modules. Put raw KDL in `extraConfig`. Merged into shared defaults `config.kdl` via wrapper-modules. Put raw KDL in `extraConfig`.
''; '';
}; };
extraBinds = lib.mkOption {
type = lib.types.attrsOf lib.types.anything;
default = { };
description = ''
Keybind overrides merged with shared defaults and `extraSettings.binds`.
Use this when multiple modules need to add binds without overwriting each other.
'';
};
}; };
config = lib.mkMerge [ config = lib.mkMerge [
+1 -1
View File
@@ -153,7 +153,7 @@
type = lib.types.bool; type = lib.types.bool;
default = true; default = true;
description = '' description = ''
Add `wisdom` (baseline + bash) to HM `sharedModules`. Other slices still go in per-user `extraModules`. Add `wisdom` (baseline + bash) to HM `sharedModules`. Per-user `extraModules` should use `lib.wisdomCatalogExtraModules self` once and set `chiasson.home.*.enable`.
''; '';
}; };
}; };
@@ -123,7 +123,7 @@ in {
{ {
# Nix rebuild widget used by DankMaterialShell (via nix-monitor). # Nix rebuild widget used by DankMaterialShell (via nix-monitor).
# Per-host `rebuildCommand` is set in `hosts/clients/<host>/home.nix`. # Per-host `rebuildCommand` is set in `modules/hosts/<host>/configuration.nix` (`chiasson.desktop.shells.dms`).
programs.nix-monitor = { programs.nix-monitor = {
enable = true; enable = true;
generationsCommand = [ generationsCommand = [
@@ -1108,6 +1108,12 @@ in {
''; '';
} }
(lib.mkIf (lib.attrByPath [ "chiasson" "home" "shell" "ohMyPosh" "enable" ] false config) { (lib.mkIf (lib.attrByPath [ "chiasson" "home" "shell" "ohMyPosh" "enable" ] false config) {
# Matugen overwrites this at runtime; seed a stock theme so `oh-my-posh init fish` works before DMS runs matugen.
xdg.configFile."oh-my-posh/theme.omp.json".source =
let
ompTheme = config.chiasson.home.shell.ohMyPosh.builtinTheme or "jandedobbeleer";
in
"${config.programs.oh-my-posh.package}/themes/${ompTheme}.omp.json";
# Same path as matugen `[templates.ohmyposh]` output in `programs.dank-material-shell` config. # Same path as matugen `[templates.ohmyposh]` output in `programs.dank-material-shell` config.
programs.oh-my-posh = { programs.oh-my-posh = {
useTheme = lib.mkForce null; useTheme = lib.mkForce null;
+71 -33
View File
@@ -1,13 +1,32 @@
# Monitor layout for 14900k (ported from NixOS-New `hosts/clients/14900k/home.nix`). # Monitor layout for 14900k.
# Niri: `chiasson.desktop.niri.extraSettings` (`extraConfig` KDL + `binds` merged with defaults). # NVIDIA (default): DP-2 ultrawide, HDMI-A-3 + DP-4 side/top stack.
# Intel iGPU (gpu passthru): DP-1 ultrawide, HDMI-A-2 Samsung to the left.
# Niri: `extraSettings` (KDL via `extraConfig`, other wrapper-modules keys) + `extraBinds` (merged keybinds).
# Hyprland: `chiasson.desktop.hyprland.settings` (merged in HM when `chiasson.desktop.hyprland.enable`). # Hyprland: `chiasson.desktop.hyprland.settings` (merged in HM when `chiasson.desktop.hyprland.enable`).
#TODO[epic=Moderate] Clean this up, move to host's configuration.nix. #TODO[epic=Moderate] Clean this up, move to host's configuration.nix.
{ config, lib, ... }: { config, lib, ... }:
{ let
chiasson.desktop.niri.extraSettings = { gpuPassthru = config.chiasson.system.gpuPassthru.enable;
extraConfig = ''
output "DP-2" { niriOutputs =
if gpuPassthru then
''
output "DP-1" {
mode "2560x1080@144"
scale 1.0
position x=0 y=0
focus-at-startup
}
output "HDMI-A-2" {
mode "1920x1080@60"
scale 1.0
position x=-1920 y=0
}
''
else
''
output "DP-3" {
mode "2560x1080@144" mode "2560x1080@144"
scale 1.0 scale 1.0
position x=0 y=0 position x=0 y=0
@@ -23,39 +42,58 @@
scale 1.0 scale 1.0
position x=0 y=-1080 position x=0 y=-1080
} }
''; '';
binds."XF86Tools".spawn = [ hyprlandMonitors =
if gpuPassthru then
[
"DP-1, 2560x1080@144, 0x0, 1"
"HDMI-A-2, 1920x1080@60, -1920x0, 1"
]
else
[
"DP-3, 2560x1080@144, 0x0, 1"
"DP-4, 1920x1080@144, 0x-1080, 1"
"HDMI-A-3, 1920x1080@60, -1920x0, 1"
];
hyprlandWorkspaces =
if gpuPassthru then
[
"1, monitor:DP-1, default:true"
"2, monitor:DP-1"
"3, monitor:DP-1"
"4, monitor:HDMI-A-2, default:true"
"5, monitor:HDMI-A-2"
"6, monitor:HDMI-A-2"
]
else
[
"1, monitor:DP-3, default:true"
"2, monitor:DP-3"
"3, monitor:DP-3"
"4, monitor:HDMI-A-3, default:true"
"5, monitor:HDMI-A-3"
"6, monitor:HDMI-A-3"
"7, monitor:DP-4"
"8, monitor:DP-4"
"9, monitor:DP-4"
];
in
{
chiasson.desktop.niri.extraSettings = {
extraConfig = niriOutputs;
};
chiasson.desktop.niri.extraBinds."XF86Tools".spawn = [
"wpctl" "wpctl"
"set-mute" "set-mute"
"@DEFAULT_AUDIO_SOURCE@" "@DEFAULT_AUDIO_SOURCE@"
"toggle" "toggle"
]; ];
};
chiasson.desktop.hyprland.settings = lib.mkIf config.chiasson.desktop.hyprland.enable ( chiasson.desktop.hyprland.settings = lib.mkIf config.chiasson.desktop.hyprland.enable {
let monitor = lib.mkBefore hyprlandMonitors;
monitorList = [ workspace = hyprlandWorkspaces;
"DP-2, 2560x1080@144, 0x0, 1" };
"DP-4, 1920x1080@144, 0x-1080, 1"
"HDMI-A-3, 1920x1080@60, -1920x0, 1"
];
workspaceList = [
"1, monitor:DP-3, default:true"
"2, monitor:DP-3"
"3, monitor:DP-3"
"4, monitor:Unknown-2, default:true"
"5, monitor:Unknown-2"
"6, monitor:Unknown-2"
"7, monitor:DP-4"
"8, monitor:DP-4"
"9, monitor:DP-4"
];
in
{
monitor = lib.mkBefore monitorList;
workspace = workspaceList;
}
);
} }
@@ -0,0 +1,71 @@
# 14900k: RTX 2070 VFIO passthrough + Windows 11 gaming VM on Crucial MX500.
{ config, lib, ... }:
let
cfg = config.chiasson.system.gpuPassthru;
in
{
chiasson.system.gpuPassthru = {
enable = false;
pciIds = [
"10de:1f07" # RTX 2070 VGA
"10de:10f9" # HDMI audio
"10de:1ada" # USB 3.1
"10de:1adb" # USB-C UCSI
"14c3:7927" # MediaTek MT7927 WiFi (M.2, no Linux driver — Windows only)
];
windowsDisk = "/dev/disk/by-id/ata-CT1000MX500SSD1_1830E14B054A";
lookingGlass = {
enable = true;
shmSizeMiB = 32;
captureOnFocus = false;
};
vm = {
enable = true;
name = "win11";
memoryGiB = 16;
vcpus = 8;
installMode = false;
};
libvirtUsers = [ "olivier" ];
};
chiasson.desktop.niri.extraBinds = lib.mkIf (cfg.enable && cfg.lookingGlass.enable) {
"Mod+J"."spawn-sh" = "looking-glass-toggle";
};
# Applied last in configuration.nix imports — wins over system module defaults.
virtualisation.libvirtd.qemu.verbatimConfig = lib.mkIf (cfg.lookingGlass.enable) (
lib.mkForce ''
namespaces = []
seccomp_sandbox = 0
security_driver = "none"
security_default_confined = 0
security_require_confined = 0
cgroup_device_acl = [
"/dev/null", "/dev/full", "/dev/zero",
"/dev/random", "/dev/urandom",
"/dev/ptmx", "/dev/kvm", "/dev/kqemu",
"/dev/rtc", "/dev/hpet",
"/dev/vfio/", "/dev/vfio/vfio",
"/dev/kvmfr0", "c 234:* rwm",
"/dev/disk/", "/dev/disk/by-id/",
"/dev/bus/usb/",
]
''
);
virtualisation.libvirt = lib.mkIf (cfg.enable && cfg.vm.enable) {
enable = true;
swtpm.enable = true;
connections."qemu:///system".domains = [
{
definition =
if cfg.vm.installMode then
./win11-domain-install.xml
else
./win11-domain.xml;
active = false;
}
];
};
}
@@ -1,13 +1,13 @@
# Moonfin 2.0.0 Jellyfin client — upstream Flatpak bundle (not on Flathub yet). # Moonfin 2.1.0 Jellyfin client — upstream Flatpak bundle (not on Flathub yet).
# https://github.com/Moonfin-Client/Moonfin-Core/releases/tag/2.0.0 # https://github.com/Moonfin-Client/Moonfin-Core/releases/tag/2.1.0
{ pkgs, ... }: { pkgs, ... }:
{ {
chiasson.system.flatpak.bundles = [ chiasson.system.flatpak.bundles = [
{ {
appId = "org.moonfin.linux"; appId = "org.moonfin.linux";
bundle = pkgs.fetchurl { bundle = pkgs.fetchurl {
url = "https://github.com/Moonfin-Client/Moonfin-Core/releases/download/2.0.0/Moonfin_Linux_v2.0.0.flatpak"; url = "https://github.com/Moonfin-Client/Moonfin-Core/releases/download/2.1.0/Moonfin_Linux_v2.1.0.flatpak";
hash = "sha256-sLtrsqBaJ1wriTkIdLylqMc9ygNkHrNm4YS/816nIFQ="; hash = "sha256-dJcI/bzKS/+SCkHarSBesei5CKCm93SjRJBp09Jn810=";
}; };
} }
]; ];
+2 -2
View File
@@ -1,6 +1,6 @@
# NVIDIA for host desktop. # NVIDIA for host desktop (disabled when GPU passthrough binds the card to VFIO).
{ config, lib, pkgs, ... }: { config, lib, pkgs, ... }:
{ lib.mkIf (!config.chiasson.system.gpuPassthru.enable) {
boot.kernelParams = [ "snd_hda_core.gpu_bind=0" ]; boot.kernelParams = [ "snd_hda_core.gpu_bind=0" ];
boot.kernelPackages = lib.mkDefault pkgs.linuxPackages_latest; boot.kernelPackages = lib.mkDefault pkgs.linuxPackages_latest;
@@ -0,0 +1,91 @@
<!-- Windows 11 install profile: emulated display (virt-manager console), no GPU passthrough.
After install + NVIDIA driver: set vm.installMode = false and rebuild for gaming profile. -->
<domain type='kvm'>
<name>win11</name>
<uuid>a7b3c8d1-4e2f-5a6b-9c0d-1e2f3a4b5c6d</uuid>
<memory unit='GiB'>16</memory>
<currentMemory unit='GiB'>16</currentMemory>
<vcpu placement='static'>8</vcpu>
<os>
<type arch='x86_64' machine='pc-q35-9.0'>hvm</type>
<loader readonly='yes' secure='yes' type='pflash'>/run/libvirt/nix-ovmf/edk2-x86_64-secure-code.fd</loader>
<nvram template='/run/libvirt/nix-ovmf/edk2-i386-vars.fd'>/var/lib/libvirt/qemu/nvram/win11_VARS.fd</nvram>
<bootmenu enable='yes'/>
<boot dev='cdrom'/>
<boot dev='hd'/>
</os>
<features>
<acpi/>
<apic/>
</features>
<cpu mode='host-passthrough' check='none' migratable='on'/>
<clock offset='localtime'>
<timer name='rtc' tickpolicy='catchup'/>
<timer name='pit' tickpolicy='delay'/>
<timer name='hpet' present='no'/>
</clock>
<on_poweroff>destroy</on_poweroff>
<on_reboot>restart</on_reboot>
<on_crash>preserve</on_crash>
<devices>
<emulator>/run/libvirt/nix-emulators/qemu-system-x86_64</emulator>
<disk type='block' device='disk'>
<driver name='qemu' type='raw' cache='none' io='native' discard='unmap'/>
<source dev='/dev/disk/by-id/ata-CT1000MX500SSD1_1830E14B054A'/>
<target dev='sda' bus='sata'/>
<address type='drive' controller='0' bus='0' target='0' unit='0'/>
</disk>
<disk type='file' device='cdrom'>
<driver name='qemu' type='raw'/>
<source file='/var/lib/libvirt/boot/win11.iso'/>
<target dev='sdb' bus='sata'/>
<readonly/>
<address type='drive' controller='0' bus='0' target='0' unit='1'/>
</disk>
<controller type='sata' index='0'>
<address type='pci' domain='0x0000' bus='0x00' slot='0x1f' function='0x2'/>
</controller>
<controller type='pci' index='0' model='pcie-root'/>
<controller type='pci' index='1' model='pcie-root-port'>
<model name='pcie-root-port'/>
<target chassis='1' port='0x10'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/>
</controller>
<controller type='usb' index='0' model='qemu-xhci' ports='15'>
<address type='pci' domain='0x0000' bus='0x00' slot='0x07' function='0x0'/>
</controller>
<interface type='network'>
<source network='default'/>
<model type='e1000e'/>
<address type='pci' domain='0x0000' bus='0x01' slot='0x00' function='0x0'/>
</interface>
<rng model='virtio'>
<backend model='random'>/dev/urandom</backend>
<address type='pci' domain='0x0000' bus='0x05' slot='0x00' function='0x0'/>
</rng>
<memballoon model='none'/>
<graphics type='spice' port='5900' autoport='no' listen='127.0.0.1'>
<listen type='address' address='127.0.0.1'/>
</graphics>
<video>
<model type='vga'/>
</video>
<input type='mouse' bus='virtio'/>
<input type='keyboard' bus='virtio'/>
<sound model='ich9'>
<audio id='1'/>
</sound>
<audio id='1' type='spice'/>
<tpm model='tpm-crb'>
<backend type='emulator' version='2.0'/>
</tpm>
</devices>
</domain>
@@ -0,0 +1,177 @@
<!-- Gaming: GPU + Looking Glass (kvmfr) -->
<domain type='kvm' xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0' xmlns:libosinfo='http://libosinfo.org/xmlns/libvirt/domain/1.0'>
<name>win11</name>
<uuid>a7b3c8d1-4e2f-5a6b-9c0d-1e2f3a4b5c6d</uuid>
<memory unit='GiB'>16</memory>
<currentMemory unit='GiB'>16</currentMemory>
<vcpu placement='static'>8</vcpu>
<metadata>
<libosinfo:libosinfo>
<libosinfo:os id='http://microsoft.com/win/11'/>
</libosinfo:libosinfo>
</metadata>
<os>
<type arch='x86_64' machine='pc-q35-9.0'>hvm</type>
<loader readonly='yes' secure='yes' type='pflash'>/run/libvirt/nix-ovmf/edk2-x86_64-secure-code.fd</loader>
<nvram template='/run/libvirt/nix-ovmf/edk2-i386-vars.fd'>/var/lib/libvirt/qemu/nvram/win11_VARS.fd</nvram>
<smbios mode='host'/>
<boot dev='hd'/>
</os>
<features>
<acpi/>
<apic/>
<hyperv mode='custom'>
<relaxed state='on'/>
<vapic state='on'/>
<spinlocks state='on' retries='8191'/>
<vendor_id state='on' value='GenuineIntel'/>
</hyperv>
<kvm>
<hidden state='on'/>
</kvm>
<vmport state='off'/>
<smm state='on'/>
<ioapic driver='kvm'/>
</features>
<cpu mode='host-passthrough' check='none' migratable='off'>
<topology sockets='1' dies='1' cores='4' threads='2'/>
<maxphysaddr mode='passthrough' limit='40'/>
<feature policy='disable' name='hypervisor'/>
</cpu>
<clock offset='localtime'>
<timer name='rtc' tickpolicy='catchup'/>
<timer name='pit' tickpolicy='delay'/>
<timer name='hpet' present='no'/>
<timer name='hypervclock' present='yes'/>
</clock>
<on_poweroff>destroy</on_poweroff>
<on_reboot>restart</on_reboot>
<on_crash>preserve</on_crash>
<pm>
<suspend-to-mem enabled='no'/>
<suspend-to-disk enabled='no'/>
</pm>
<devices>
<emulator>/run/libvirt/nix-emulators/qemu-system-x86_64</emulator>
<disk type='block' device='disk'>
<driver name='qemu' type='raw' cache='none' io='native' discard='unmap'/>
<source dev='/dev/disk/by-id/ata-CT1000MX500SSD1_1830E14B054A'/>
<target dev='sda' bus='sata'/>
<address type='drive' controller='0' bus='0' target='0' unit='0'/>
</disk>
<controller type='sata' index='0'>
<address type='pci' domain='0x0000' bus='0x00' slot='0x1f' function='0x2'/>
</controller>
<controller type='pci' index='0' model='pcie-root'/>
<controller type='pci' index='1' model='pcie-root-port'>
<model name='pcie-root-port'/>
<target chassis='1' port='0x10'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/>
</controller>
<controller type='pci' index='2' model='pcie-root-port'>
<model name='pcie-root-port'/>
<target chassis='2' port='0x11'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x1'/>
</controller>
<controller type='pci' index='3' model='pcie-root-port'>
<model name='pcie-root-port'/>
<target chassis='3' port='0x12'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x2'/>
</controller>
<controller type='pci' index='4' model='pcie-root-port'>
<model name='pcie-root-port'/>
<target chassis='4' port='0x13'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x3'/>
</controller>
<controller type='pci' index='5' model='pcie-root-port'>
<model name='pcie-root-port'/>
<target chassis='5' port='0x14'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x4'/>
</controller>
<controller type='usb' index='0' model='qemu-xhci' ports='15'>
<address type='pci' domain='0x0000' bus='0x00' slot='0x07' function='0x0'/>
</controller>
<interface type='network'>
<mac address='04:7c:16:c9:48:a7'/>
<source network='default'/>
<model type='e1000e'/>
<address type='pci' domain='0x0000' bus='0x01' slot='0x00' function='0x0'/>
</interface>
<input type='keyboard' bus='ps2'/>
<input type='mouse' bus='ps2'/>
<memballoon model='none'/>
<graphics type='spice' port='5900' autoport='no' listen='127.0.0.1'>
<listen type='address' address='127.0.0.1'/>
<image compression='off'/>
</graphics>
<video>
<model type='none'/>
</video>
<sound model='ich9'>
<audio id='1'/>
</sound>
<audio id='1' type='spice'/>
<hostdev mode='subsystem' type='pci' managed='yes'>
<source>
<address domain='0x0000' bus='0x01' slot='0x00' function='0x0'/>
</source>
<address type='pci' domain='0x0000' bus='0x06' slot='0x00' function='0x0' multifunction='on'/>
</hostdev>
<hostdev mode='subsystem' type='pci' managed='yes'>
<source>
<address domain='0x0000' bus='0x01' slot='0x00' function='0x1'/>
</source>
<address type='pci' domain='0x0000' bus='0x06' slot='0x00' function='0x1'/>
</hostdev>
<hostdev mode='subsystem' type='pci' managed='yes'>
<source>
<address domain='0x0000' bus='0x01' slot='0x00' function='0x2'/>
</source>
<address type='pci' domain='0x0000' bus='0x06' slot='0x00' function='0x2'/>
</hostdev>
<hostdev mode='subsystem' type='pci' managed='yes'>
<source>
<address domain='0x0000' bus='0x01' slot='0x00' function='0x3'/>
</source>
<address type='pci' domain='0x0000' bus='0x06' slot='0x00' function='0x3'/>
</hostdev>
<hostdev mode='subsystem' type='pci' managed='yes'>
<source>
<address domain='0x0000' bus='0x03' slot='0x00' function='0x0'/>
</source>
<address type='pci' domain='0x0000' bus='0x04' slot='0x00' function='0x0'/>
</hostdev>
<hostdev mode='subsystem' type='usb' managed='yes'>
<source startupPolicy='optional'>
<vendor id='0x046d'/>
<product id='0xc08b'/>
</source>
</hostdev>
<hostdev mode='subsystem' type='usb' managed='yes'>
<source startupPolicy='optional'>
<vendor id='0x0489'/>
<product id='0xe10f'/>
</source>
</hostdev>
<tpm model='tpm-crb'>
<backend type='emulator' version='2.0'/>
</tpm>
</devices>
<qemu:commandline>
<qemu:arg value='-device'/>
<qemu:arg value='{"driver":"ivshmem-plain","id":"shmem0","memdev":"looking-glass"}'/>
<qemu:arg value='-object'/>
<qemu:arg value='{"qom-type":"memory-backend-file","id":"looking-glass","mem-path":"/dev/kvmfr0","size":33554432,"share":true}'/>
</qemu:commandline>
</domain>
+2 -75
View File
@@ -6,6 +6,7 @@
self.nixosModules."14900kHardware" self.nixosModules."14900kHardware"
inputs.home-manager.nixosModules.home-manager inputs.home-manager.nixosModules.home-manager
inputs.sops-nix.nixosModules.sops inputs.sops-nix.nixosModules.sops
inputs.nixvirt.nixosModules.default
self.nixosModules.system self.nixosModules.system
self.nixosModules.desktop self.nixosModules.desktop
@@ -14,6 +15,7 @@
self.nixosModules."client-services" self.nixosModules."client-services"
./_private/platform.nix ./_private/platform.nix
./_private/nvidia.nix ./_private/nvidia.nix
./_private/gpu-passthru.nix
./_private/peripherals.nix ./_private/peripherals.nix
# ./_private/printing-epson.nix # ./_private/printing-epson.nix
./_private/displays.nix ./_private/displays.nix
@@ -86,8 +88,6 @@ services.cloudflare-warp.enable = true;
}; };
}; };
chiasson.system.chromiumHevc.enable = true;
chiasson.system = { chiasson.system = {
ytDlpTelequebecPatch.enable = true; ytDlpTelequebecPatch.enable = true;
@@ -141,78 +141,5 @@ services.cloudflare-warp.enable = true;
}; };
chiasson.users.enabled = [ "olivier" ]; chiasson.users.enabled = [ "olivier" ];
chiasson.users.extraModules.olivier = [
self.homeManagerModules.wisdomFilebrowsersDolphin
self.homeManagerModules.wisdomTerminalsKitty
self.homeManagerModules.wisdomBrowsersEdge
self.homeManagerModules.wisdomBrowsersFlow
self.homeManagerModules.wisdomBrowsersOrion
self.homeManagerModules.wisdomBrowsersZen
self.homeManagerModules.wisdomBrowsersChromiumHevc
self.homeManagerModules.wisdomEditorsCursor
self.homeManagerModules.wisdomEditorsObsidian
self.homeManagerModules.wisdomShellYazi
self.homeManagerModules.wisdomShellFish
self.homeManagerModules.wisdomShellOhMyPosh
self.homeManagerModules.wisdomAppsDiscord
self.homeManagerModules.wisdomAppsSpotify
self.homeManagerModules.wisdomAppsLocalsend
self.homeManagerModules.wisdomAppsPokeclicker
self.homeManagerModules.wisdomDesktopScreenshot
self.homeManagerModules.wisdomDesktopGtkQtTheming
{
programs.git = {
enable = true;
settings.user = {
name = "OlivierChiasson";
email = "olivierchiasson@hotmail.fr";
};
};
chiasson.home = {
extraPackages = [ pkgs.parsec-bin ];
shell = {
fish.enable = true;
yazi.enable = true;
ohMyPosh.enable = true;
};
terminals.kitty.enable = true;
filebrowsers.dolphin.enable = true;
browsers.edge.enable = true;
browsers.flow.enable = false;
browsers.orion.enable = true;
browsers.zen.enable = true;
browsers.chromiumHevc = {
enable = true;
packages = [ "google-chrome" ];
vaapi.gpu = "intel"; # Chromium + NVIDIA VA-API → frame pool errors in Jellyfin cuz chrome is proprietary rats nests, gecko engine might support NVIDIA VA-API
};
editors.cursor.enable = true;
editors.obsidian.enable = true;
apps = {
discord.enable = true;
spotify.enable = true;
spotify.openDiscoveryFirewall = true;
localsend.enable = true;
pokeclicker.enable = true;
};
desktop = {
screenshot = {
enable = true;
swiftshareApiKeyFile = "/run/secrets/swiftshare/API_KEY"; #TODO[epic=sops] redo this by passing sops file output directly
};
theming.enable = true;
};
};
}
];
}; };
} }
+1
View File
@@ -9,6 +9,7 @@
}; };
modules = [ modules = [
self.nixosModules."14900kConfiguration" self.nixosModules."14900kConfiguration"
self.nixosModules."14900kHome"
]; ];
}; };
} }
+30
View File
@@ -0,0 +1,30 @@
{ self, inputs, ... }:
{
flake.nixosModules."14900kHome" =
{ self, pkgs, ... }:
{
imports = [ self.nixosModules.desktopHomeBase ];
chiasson.users.extraModules.olivier = [
{
chiasson.home = {
extraPackages = [ pkgs.parsec-bin ];
browsers = {
edge.enable = true;
orion.enable = true;
zen.enable = true;
};
apps = {
discord.enable = true;
spotify.enable = true;
spotify.openDiscoveryFirewall = true;
pokeclicker.enable = true;
};
};
}
];
};
}
+90
View File
@@ -0,0 +1,90 @@
# Shared Home Manager selection for all desktop hosts (14900k, t2mbp, ideapad, uConsole).
# Wisdom slices resolved here (NixOS has `self`); toggles select what actually installs.
{ self, inputs, ... }:
{
flake.nixosModules.desktopHomeBase =
{ self, lib, pkgs, ... }:
{
chiasson.system.chromiumHevc.enable = lib.mkDefault true;
chiasson.users.extraModules.olivier =
self.lib.wisdomCatalogExtraModules self
++ [
(
{ lib, pkgs, ... }:
let
aarch64 = pkgs.stdenv.hostPlatform.isAarch64;
in
{
programs.git = {
enable = lib.mkDefault true;
settings.user = {
name = "OlivierChiasson";
email = "olivierchiasson@hotmail.fr";
};
};
chiasson.home = {
# Catalog of `chiasson.home.*` toggles — host `home.nix` sets `enable = true` to override `mkDefault false`.
shell = {
fish.enable = lib.mkDefault true;
ohMyPosh.enable = lib.mkDefault true;
yazi.enable = lib.mkDefault true;
};
terminals.kitty.enable = lib.mkDefault true;
filebrowsers.dolphin.enable = lib.mkDefault true;
browsers = {
chrome.enable = lib.mkDefault false;
chromiumHevc = {
enable = lib.mkDefault true;
packages = lib.mkDefault (
if aarch64 then
[ "chromium" ]
else
[ "google-chrome" ]
);
} // lib.optionalAttrs (!aarch64) {
# Chromium + NVIDIA VA-API → frame pool errors in Jellyfin; gecko may fare better on NVIDIA.
vaapi.gpu = lib.mkDefault "intel";
};
edge.enable = lib.mkDefault false;
flow.enable = lib.mkDefault false;
orion.enable = lib.mkDefault false;
zen.enable = lib.mkDefault false;
};
editors = {
cursor.enable = lib.mkDefault true;
kate.enable = lib.mkDefault false;
obsidian.enable = lib.mkDefault true;
};
apps = {
discord.enable = lib.mkDefault false;
localsend.enable = lib.mkDefault true;
pokeclicker.enable = lib.mkDefault false;
spotify = {
enable = lib.mkDefault false;
openDiscoveryFirewall = lib.mkDefault false;
};
};
desktop = {
screenshot = {
enable = lib.mkDefault true;
swiftshareApiKeyFile = "/run/secrets/swiftshare/API_KEY"; #TODO[epic=sops] redo this by passing sops file output directly
};
# WhiteSur GTK/icons, Phinger cursor, Qt via KDE platform theme; imports DMS matugen `dank-colors.css`.
theming.enable = lib.mkDefault true;
};
hardware.uconsoleGamepad.enable = lib.mkDefault false;
};
}
)
];
};
}
-59
View File
@@ -127,67 +127,8 @@
}; };
# ─────────────────────── Users / HM ───────────────────────
chiasson.users.enabled = [ "olivier" ]; chiasson.users.enabled = [ "olivier" ];
# Touch-friendly application set, mirroring uConsole's selection (no heavy IDEs / gaming).
chiasson.users.extraModules.olivier = [
self.homeManagerModules.wisdomFilebrowsersDolphin
self.homeManagerModules.wisdomTerminalsKitty
self.homeManagerModules.wisdomBrowsersZen
self.homeManagerModules.wisdomEditorsKate
self.homeManagerModules.wisdomEditorsCursor
self.homeManagerModules.wisdomShellFish
self.homeManagerModules.wisdomShellOhMyPosh
self.homeManagerModules.wisdomAppsSpotify
self.homeManagerModules.wisdomAppsLocalsend
self.homeManagerModules.wisdomDesktopScreenshot
{
chiasson.home = {
shell = {
fish.enable = true;
ohMyPosh.enable = true;
};
terminals.kitty.enable = true;
filebrowsers.dolphin.enable = true;
browsers.zen.enable = true;
editors.kate.enable = true;
editors.cursor.enable = true;
apps.spotify.enable = true;
apps.localsend.enable = true;
desktop = {
screenshot = {
enable = true;
swiftshareApiKeyFile = "/run/secrets/swiftshare/API_KEY"; #TODO[epic=sops] redo this by passing sops file output directly
};
};
};
}
# Tablet-class apps: kept inline rather than promoting to wisdom modules — these aren't
# part of the broader catalog (no use on uConsole / 14900k / servers) and adding a wisdom
# module per single-host package would just be ceremony. If a second tablet host ever
# appears, factor them out then.
#
# NOTE on cameras: no v4l2/libcamera GUI is installed. The Mobile NixOS kernel for
# `lenovo-wormdingler` ships with `CONFIG_VIDEO_QCOM_CAMSS` disabled and no
# `VIDEO_OV*`/`VIDEO_HI*` sensor drivers, so `/dev/video0`-`/dev/video1` only expose
# the Qualcomm Venus codecs (h.264/h.265 enc/dec) and there is no camera source for
# PipeWire / libcamera to pick up. See `_private/CAMERA-TODO.md` for the steps that
# would (potentially) bring the front/rear cameras online — it's a kernel-rebuild +
# device-tree + libcamera project, not a config tweak.
(
{ pkgs, ... }:
{
home.packages = with pkgs; [
# PDF viewer — fits the existing KDE app set (Dolphin + Kate).
kdePackages.okular
# ePub reader, GTK4, large touch targets.
foliate
];
}
)
];
system.stateVersion = "26.05"; system.stateVersion = "26.05";
}; };
} }
+1
View File
@@ -9,6 +9,7 @@
}; };
modules = [ modules = [
self.nixosModules.ideapadConfiguration self.nixosModules.ideapadConfiguration
self.nixosModules.ideapadHome
]; ];
}; };
} }
+21
View File
@@ -0,0 +1,21 @@
{ self, inputs, ... }:
{
flake.nixosModules.ideapadHome =
{ self, ... }:
{
imports = [ self.nixosModules.desktopHomeBase ];
# Host-only HM overrides (tablet apps, toggles off from desktop-home-base, …).
chiasson.users.extraModules.olivier = [
# {
# chiasson.home = {
# # editors.kate.enable = true;
# };
# }
# (
# { pkgs, ... }:
# { home.packages = with pkgs; [ ]; }
# )
];
};
}
@@ -50,6 +50,9 @@
}; };
sops.secrets."users/server/hashedPassword".neededForUsers = true; sops.secrets."users/server/hashedPassword".neededForUsers = true;
sops.secrets."tailscale/auth-key" = {
mode = "0400";
};
security.sudo.wheelNeedsPassword = true; security.sudo.wheelNeedsPassword = true;
@@ -63,6 +66,11 @@
networking = { networking = {
hostName = "nix-server"; hostName = "nix-server";
networkManager.enable = true; networkManager.enable = true;
tailscale = {
enable = true;
authKeyFile = config.sops.secrets."tailscale/auth-key".path;
subnetRouter.enable = true;
};
}; };
caching.attic = { caching.attic = {
+3 -3
View File
@@ -1,5 +1,5 @@
swiftshare: swiftshare:
ghcr-token: ENC[AES256_GCM,data:V/5dLVLv4BbAxdMiBxXgmNbK17HAQkqzHJA2NWzOFfFlcy3dq8SnZQ==,iv:YTB3Bef+kZXunXVUCkFj/YZo1POdx2K+bNvzarSJ1Iw=,tag:HEBT4ZKMXTIy+ZEkNx3rHw==,type:str] ghcr-token: ENC[AES256_GCM,data:MFNjMrh3mlH2gGZlzp9lty3n/+6/SQSFbBowqrR3QFO4/t/afBtv2Q==,iv:3wZzjUU4uzctX5woHiAY4Y9QJIiBLeGdmrJzhmpVy9E=,tag:sK/Jy53jBsbdd1YWoOVT5A==,type:str]
database-password: ENC[AES256_GCM,data:r9GSaoQ7bS644ipb3kU=,iv:KYDTzYtjfz5meDb0nemY1lhSFEorKHL0hSRIcQaHg5c=,tag:RVjAfb8XGsybAgIc2/hH+g==,type:str] database-password: ENC[AES256_GCM,data:r9GSaoQ7bS644ipb3kU=,iv:KYDTzYtjfz5meDb0nemY1lhSFEorKHL0hSRIcQaHg5c=,tag:RVjAfb8XGsybAgIc2/hH+g==,type:str]
auth-secret: ENC[AES256_GCM,data:tTXLMWASBfF49gBFrf+CZ3R4oTt7hEGUhAqEdvoQtm0zbb2VUhTq7y4tH/c=,iv:Halfu9hBex4SEUMHLAicqApTxZP0NV9pJZTr+bBSek4=,tag:1WqN75zT+zoka9sIXOJGfQ==,type:str] auth-secret: ENC[AES256_GCM,data:tTXLMWASBfF49gBFrf+CZ3R4oTt7hEGUhAqEdvoQtm0zbb2VUhTq7y4tH/c=,iv:Halfu9hBex4SEUMHLAicqApTxZP0NV9pJZTr+bBSek4=,tag:1WqN75zT+zoka9sIXOJGfQ==,type:str]
oauth-discord-client-secret: ENC[AES256_GCM,data:a9Iarcpl1HOFXdsDMh3H662T8yqVvGtfguVICwWVrAg=,iv:LsUserWQcEDV0TiRWj1sHh5/ZiFQzyc1gRWg+Ewwjik=,tag:33Ml08oHVXl0ZMmiwQ2mig==,type:str] oauth-discord-client-secret: ENC[AES256_GCM,data:a9Iarcpl1HOFXdsDMh3H662T8yqVvGtfguVICwWVrAg=,iv:LsUserWQcEDV0TiRWj1sHh5/ZiFQzyc1gRWg+Ewwjik=,tag:33Ml08oHVXl0ZMmiwQ2mig==,type:str]
@@ -46,7 +46,7 @@ sops:
Nf0uOoSWPTJ/2SRNkSu7FMumATH4ldQ6TFSwKda3mBfBwhnFzLq10Q== Nf0uOoSWPTJ/2SRNkSu7FMumATH4ldQ6TFSwKda3mBfBwhnFzLq10Q==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
recipient: age1p05z980kdtngk9mw67hfev72h7xhslplpxfk9yskgmf0hl4lu3ls04zht9 recipient: age1p05z980kdtngk9mw67hfev72h7xhslplpxfk9yskgmf0hl4lu3ls04zht9
lastmodified: "2026-05-28T21:04:07Z" lastmodified: "2026-06-12T00:44:20Z"
mac: ENC[AES256_GCM,data:L53UYFh5xtuMx19GKAg3jW7U0/DlwJ2usy/pup+4t1HQN3KHxMwbc4BzLYkLnRBTwKMJdfKXiYmmYiYvfbbWzsPtfXLxPnF/5ROiCJ2NlxAe86SmRy2nI++eTHAXRgexIhYyL7SchsroGRvW2B3aL1jV+Eu11fD9trA9Ex1EfuI=,iv:XrRJCFSgwW2+N+4FnWrFFZz8UEVzhuhpRtHGtf8dyqc=,tag:LcO4+ilwKdU+JPyjyKaGNw==,type:str] mac: ENC[AES256_GCM,data:cyI5NH6+OR/Q4T3atFN+gXsJbRaR/Q5I+MyJHy5qXsShh8LnQvEKBnnIPKz2xpuDwFvKd4DabMOxcu5xe6IRH9TN7G411vp+vruM+TicoCJ4U2xAP/J9Tlh3YKT6kZJqj/zAhvP5qPCzvz7xkFTvluLrWgPlaHWn/rlCskh0t5M=,iv:WBeS1rqjJblNGMVa7hEn5MXbZJU8WKd6541BCxu2rGI=,tag:OfOzQI6ESUdUYCbBEq9GFw==,type:str]
unencrypted_suffix: _unencrypted unencrypted_suffix: _unencrypted
version: 3.13.1 version: 3.13.1
+8 -53
View File
@@ -38,6 +38,9 @@
group = "users"; group = "users";
mode = "0400"; mode = "0400";
}; };
sops.secrets."tailscale/auth-key" = {
mode = "0400";
};
chiasson.system.librepods.enable = true; chiasson.system.librepods.enable = true;
chiasson.system.palera1n.enable = true; chiasson.system.palera1n.enable = true;
@@ -106,63 +109,15 @@
networking = { networking = {
hostName = "t2mbp"; hostName = "t2mbp";
networkManager.enable = true; networkManager.enable = true;
tailscale = {
enable = true;
authKeyFile = config.sops.secrets."tailscale/auth-key".path;
acceptRoutes = true;
};
}; };
}; };
chiasson.users.enabled = [ "olivier" ]; chiasson.users.enabled = [ "olivier" ];
chiasson.users.extraModules.olivier = [
self.homeManagerModules.wisdomFilebrowsersDolphin
self.homeManagerModules.wisdomTerminalsKitty
self.homeManagerModules.wisdomBrowsersZen
self.homeManagerModules.wisdomBrowsersChrome
self.homeManagerModules.wisdomBrowsersEdge
self.homeManagerModules.wisdomEditorsCursor
self.homeManagerModules.wisdomEditorsKate
self.homeManagerModules.wisdomEditorsObsidian
self.homeManagerModules.wisdomShellYazi
self.homeManagerModules.wisdomShellFish
self.homeManagerModules.wisdomShellOhMyPosh
self.homeManagerModules.wisdomAppsDiscord
self.homeManagerModules.wisdomAppsSpotify
self.homeManagerModules.wisdomAppsLocalsend
self.homeManagerModules.wisdomAppsPokeclicker
self.homeManagerModules.wisdomDesktopScreenshot
{
chiasson.home = {
shell = {
fish.enable = true;
yazi.enable = true;
ohMyPosh.enable = true;
};
terminals.kitty.enable = true;
filebrowsers.dolphin.enable = true;
browsers = {
zen.enable = false;
chrome.enable = false;
edge.enable = true;
};
editors = {
cursor.enable = true;
kate.enable = false;
obsidian.enable = true;
};
apps = {
discord.enable = true;
spotify.enable = false;
localsend.enable = true;
pokeclicker.enable = true;
};
desktop = {
screenshot = {
enable = true;
swiftshareApiKeyFile = "/run/secrets/swiftshare/API_KEY"; #TODO[epic=sops] redo this by passing sops file output directly
};
};
};
}
];
}; };
} }
+1
View File
@@ -9,6 +9,7 @@
}; };
modules = [ modules = [
self.nixosModules.t2mbpConfiguration self.nixosModules.t2mbpConfiguration
self.nixosModules.t2mbpHome
]; ];
}; };
} }
+23
View File
@@ -0,0 +1,23 @@
{ self, inputs, ... }:
{
flake.nixosModules.t2mbpHome =
{ self, ... }:
{
imports = [ self.nixosModules.desktopHomeBase ];
chiasson.users.extraModules.olivier = [
{
chiasson.home = {
browsers = {
edge.enable = true;
};
apps = {
discord.enable = true;
pokeclicker.enable = true;
};
};
}
];
};
}
-34
View File
@@ -103,40 +103,6 @@
chiasson.users.enabled = [ "olivier" ]; chiasson.users.enabled = [ "olivier" ];
chiasson.users.extraModules.olivier = [
self.homeManagerModules.wisdomFilebrowsersDolphin
self.homeManagerModules.wisdomTerminalsKitty
self.homeManagerModules.wisdomBrowsersZen
self.homeManagerModules.wisdomEditorsKate
self.homeManagerModules.wisdomShellFish
self.homeManagerModules.wisdomShellOhMyPosh
self.homeManagerModules.wisdomAppsSpotify
self.homeManagerModules.wisdomAppsLocalsend
self.homeManagerModules.wisdomDesktopScreenshot
self.homeManagerModules.wisdomHardwareUconsoleGamepad
{
chiasson.home = {
shell = {
fish.enable = true;
ohMyPosh.enable = true;
};
terminals.kitty.enable = true;
filebrowsers.dolphin.enable = true;
browsers.zen.enable = true;
editors.kate.enable = true;
apps.spotify.enable = true;
apps.localsend.enable = true;
desktop = {
screenshot = {
enable = true;
swiftshareApiKeyFile = "/run/secrets/swiftshare/API_KEY"; #TODO[epic=sops] redo this by passing sops file output directly
};
};
hardware.uconsoleGamepad.enable = true;
};
}
];
console.packages = with pkgs; [ terminus_font ]; console.packages = with pkgs; [ terminus_font ];
}; };
} }
+1
View File
@@ -14,6 +14,7 @@
inputs.oom-hardware.nixosModules.uc.configtxt inputs.oom-hardware.nixosModules.uc.configtxt
inputs.oom-hardware.nixosModules.uc.base-cm5 inputs.oom-hardware.nixosModules.uc.base-cm5
self.nixosModules.uConsoleConfiguration self.nixosModules.uConsoleConfiguration
self.nixosModules.uConsoleHome
]; ];
}; };
} }
+16
View File
@@ -0,0 +1,16 @@
{ self, inputs, ... }:
{
flake.nixosModules.uConsoleHome =
{ self, ... }:
{
imports = [ self.nixosModules.desktopHomeBase ];
chiasson.users.extraModules.olivier = [
{
chiasson.home = {
hardware.uconsoleGamepad.enable = true;
};
}
];
};
}
+17
View File
@@ -0,0 +1,17 @@
# Resolve `wisdom*` HM slices for NixOS `extraModules` (`self` is not an HM specialArg).
# Slices are gated by `chiasson.home.*.enable`; hosts only flip toggles in `home.nix`.
{ lib, ... }: {
flake.lib.wisdomCatalogExtraModules =
self:
let
names = lib.sort builtins.lessThan (
lib.filter (
n:
lib.hasPrefix "wisdom" n
&& n != "wisdom"
&& n != "wisdomShellBash"
) (builtins.attrNames self.homeManagerModules)
);
in
map (name: self.homeManagerModules.${name}) names;
}
+3
View File
@@ -5,17 +5,20 @@
self.nixosModules.systemLocalization self.nixosModules.systemLocalization
self.nixosModules.systemFonts self.nixosModules.systemFonts
self.nixosModules.systemNetworking self.nixosModules.systemNetworking
self.nixosModules.systemTailscale
self.nixosModules.systemLocalsend self.nixosModules.systemLocalsend
self.nixosModules.systemChromiumHevcVaapi self.nixosModules.systemChromiumHevcVaapi
self.nixosModules.systemMonitorInput self.nixosModules.systemMonitorInput
self.nixosModules.systemSpotify self.nixosModules.systemSpotify
self.nixosModules.systemPackagesDefaults self.nixosModules.systemPackagesDefaults
self.nixosModules.systemDocker self.nixosModules.systemDocker
self.nixosModules.systemDockerRegistryAuth
self.nixosModules.systemFlatpak self.nixosModules.systemFlatpak
self.nixosModules.systemAudio self.nixosModules.systemAudio
self.nixosModules.systemIdeapadMrubyOverlay self.nixosModules.systemIdeapadMrubyOverlay
self.nixosModules.systemYtDlpTelequebecPatch self.nixosModules.systemYtDlpTelequebecPatch
self.nixosModules.systemGaming self.nixosModules.systemGaming
self.nixosModules.systemGpuPassthru
self.nixosModules.systemUconsoleKernelBuilder self.nixosModules.systemUconsoleKernelBuilder
self.nixosModules.systemLibrepods self.nixosModules.systemLibrepods
self.nixosModules.systemPalera1n self.nixosModules.systemPalera1n
+79
View File
@@ -0,0 +1,79 @@
# oci-containers runs `docker login` in ExecStartPre, which stores plaintext credentials
# in /root/.docker/config.json. For any container with `login` set, use in-memory
# DOCKER_AUTH_CONFIG instead (no per-service list to maintain).
{ ... }: {
flake.nixosModules.systemDockerRegistryAuth =
{ config, lib, pkgs, ... }:
let
cfg = config.chiasson.system.docker.ephemeralRegistryAuth;
ociCfg = lib.attrByPath [ "virtualisation" "oci-containers" ] null config;
backend = if ociCfg == null then "docker" else ociCfg.backend or "docker";
containerBackend =
if backend == "podman" then
lib.attrByPath [ "virtualisation" "podman" "package" ] pkgs.podman config
else
lib.attrByPath [ "virtualisation" "docker" "package" ] pkgs.docker config;
isValidLogin =
login: login.registry != null && login.username != null && login.passwordFile != null;
mkRegistryAuthPreStart =
login: lib.mkBefore ''
export DOCKER_AUTH_CONFIG="$(${pkgs.jq}/bin/jq -cn \
--arg registry ${lib.escapeShellArg login.registry} \
--arg user ${lib.escapeShellArg login.username} \
--arg pass "$(${pkgs.coreutils}/bin/tr -d '\n\r' < ${login.passwordFile})" \
'{auths: {($registry): {username: $user, password: $pass, auth: (($user + ":" + $pass) | @base64)}}}')"
'';
mkPreStartWithoutLogin =
name: container:
pkgs.writeShellScript "oci-pre-start-${name}" ''
${containerBackend}/bin/${backend} rm -f ${lib.escapeShellArg name} || true
${
lib.optionalString (container.imageFile != null) ''
${containerBackend}/bin/${backend} load -i ${container.imageFile}
''
}
${
lib.optionalString (container.imageStream != null) ''
${container.imageStream} | ${containerBackend}/bin/${backend} load
''
}
'';
in
{
options.chiasson.system.docker.ephemeralRegistryAuth = {
enable = lib.mkOption {
type = lib.types.bool;
default = true;
description = ''
Authenticate oci-containers registry pulls via in-memory `DOCKER_AUTH_CONFIG`
instead of `docker login`. Applies automatically to every container that sets
`virtualisation.oci-containers.containers.<name>.login`.
'';
};
};
# Read merged container config only when defining systemd overrides — never write back
# to `virtualisation.oci-containers.containers` (that causes infinite recursion).
config =
let
containers =
lib.attrByPath [ "virtualisation" "oci-containers" "containers" ] { } config;
containersWithLogin = lib.filterAttrs (_: c: isValidLogin (c.login or { })) containers;
in
lib.mkIf (cfg.enable && containersWithLogin != { }) {
systemd.services = lib.mapAttrs' (
name: container:
lib.nameValuePair (container.serviceName or "${backend}-${name}") {
preStart = mkRegistryAuthPreStart container.login;
serviceConfig.ExecStartPre = lib.mkForce [
"${mkPreStartWithoutLogin name container}"
];
}
) containersWithLogin;
};
};
}
+383
View File
@@ -0,0 +1,383 @@
{ ... }: {
flake.nixosModules.systemGpuPassthru =
{ config, lib, pkgs, ... }:
let
cfg = config.chiasson.system.gpuPassthru;
vfioIds = lib.concatStringsSep "," cfg.pciIds;
# Must match the Windows host installer build (B7-244-4bb2c58f).
lgRev = "4bb2c58fb6d0df9e863ad45924dd4decc7e9cf4e";
lgVersion = "B7-244-4bb2c58f";
# Tarball hashes omit submodules; fetchSubmodules needs the git tree hash.
lgHash = "sha256-Dc0sFnJVM0xJ0FfIQqGQKxFUj6mhp3qgB772h3Dgowc=";
# https://wiki.nixos.org/wiki/Looking_Glass — systemd 258+ needs namespaces=[]
# and an explicit ACL; seccomp can block mmap on /dev/kvmfr0.
lgQemuVerbatimConfig = ''
namespaces = []
seccomp_sandbox = 0
security_driver = "none"
security_default_confined = 0
security_require_confined = 0
cgroup_device_acl = [
"/dev/null", "/dev/full", "/dev/zero",
"/dev/random", "/dev/urandom",
"/dev/ptmx", "/dev/kvm", "/dev/kqemu",
"/dev/rtc", "/dev/hpet",
"/dev/vfio/", "/dev/vfio/vfio",
"/dev/kvmfr0", "c 234:* rwm",
"/dev/disk/", "/dev/disk/by-id/",
"/dev/bus/usb/",
]
'';
in
{
options.chiasson.system.gpuPassthru = {
enable = lib.mkEnableOption ''
Bind passthrough GPU(s) to VFIO, run the host on Intel iGPU, and enable libvirt +
Looking Glass for a Windows gaming VM.
Prerequisites (manual, one-time):
- BIOS: VT-d/IOMMU on, Above 4G Decoding / Resizable BAR on
- Move host monitors to motherboard (Intel) ports; dummy HDMI/DP plug on passthrough GPU
- Wipe the VM disk before first boot: `wipefs -a <windowsDisk>`
- Verify IOMMU groups after switch: GPU functions should be isolated
Rollout: leave `enable = false` until cabling and disk backup are done, then rebuild and switch.
'';
pciIds = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
example = [ "10de:1f07" "10de:10f9" "10de:1ada" "10de:1adb" ];
description = ''
PCI vendor:device IDs to bind to vfio-pci (all GPU functions: VGA, audio, USB, etc.).
'';
};
windowsDisk = lib.mkOption {
type = lib.types.str;
default = "";
example = "/dev/disk/by-id/ata-CT1000MX500SSD1_1830E14B054A";
description = ''
Whole-disk by-id path for the Windows VM (raw block passthrough). Must not be mounted by the host.
'';
};
lookingGlass = {
enable = lib.mkOption {
type = lib.types.bool;
default = true;
description = ''
Load the kvmfr kernel module and grant QEMU access to /dev/kvmfr0 for Looking Glass
(required for NVIDIA VFIO; plain /dev/shm IVSHMEM causes DMA mapping failures).
'';
};
shmSizeMiB = lib.mkOption {
type = lib.types.int;
default = 32;
description = ''
kvmfr shared memory size (MiB). Must match `kvmfr.static_size_mb` and the VM
`memory-backend-file` size in the domain XML.
'';
};
captureOnFocus = lib.mkOption {
type = lib.types.bool;
default = true;
description = ''
Grab keyboard/mouse when the Looking Glass window is focused. Set `false` to
keep input on the Linux host while viewing the guest.
'';
};
};
vm = {
enable = lib.mkOption {
type = lib.types.bool;
default = false;
description = ''
Declaratively define the Windows VM via NixVirt (host module must import NixVirt and set
`virtualisation.libvirt.connections`).
'';
};
name = lib.mkOption {
type = lib.types.str;
default = "win11";
description = "libvirt domain name.";
};
memoryGiB = lib.mkOption {
type = lib.types.int;
default = 16;
description = "VM RAM in GiB (informational; domain XML is authoritative).";
};
vcpus = lib.mkOption {
type = lib.types.int;
default = 8;
description = "vCPU count (informational; domain XML is authoritative).";
};
installMode = lib.mkOption {
type = lib.types.bool;
default = true;
description = ''
Use install domain (ISO, no GPU). Set `false` after Windows is installed.
'';
};
};
hugepages.enable = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Reserve 2 MiB hugepages for Looking Glass IVSHMEM (enable if frame drops).";
};
libvirtUsers = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
description = "Users added to libvirtd, kvm, and disk groups.";
};
};
config = lib.mkIf cfg.enable (lib.mkMerge [
{
assertions = [
{
assertion = cfg.pciIds != [ ];
message = "chiasson.system.gpuPassthru: pciIds must list every passthrough GPU function.";
}
{
assertion = cfg.windowsDisk != "";
message = "chiasson.system.gpuPassthru: windowsDisk must be set to a /dev/disk/by-id/ path.";
}
{
assertion = cfg.libvirtUsers != [ ];
message = "chiasson.system.gpuPassthru: libvirtUsers must name at least one user.";
}
];
boot.kernelParams = [
"intel_iommu=on"
"iommu=pt"
"kvm.ignore_msrs=1"
"vfio-pci.ids=${vfioIds}"
];
boot.initrd.kernelModules = [
"vfio_pci"
"vfio"
"vfio_iommu_type1"
];
boot.blacklistedKernelModules = [
"nouveau"
"nvidia"
"nvidia_drm"
"nvidia_modeset"
"i2c_nvidia_gpu"
"mt7921e"
"mt7925e"
];
boot.extraModprobeConfig = ''
softdep nvidia pre: vfio-pci
softdep nvidia_drm pre: vfio-pci
softdep nouveau pre: vfio-pci
options vfio_iommu_type1 allow_unsafe_interrupts=1
'';
services.xserver.videoDrivers = lib.mkForce [ "modesetting" ];
hardware.nvidia-container-toolkit.enable = lib.mkForce false;
chiasson.system.gaming.sunshine.cudaSupport = lib.mkForce false;
# libvirt enables ZFS storage pools on Linux by default, which pulls in
# zfs-user (currently broken to build on nixos-unstable). Raw block disk
# passthrough does not need libvirt ZFS support.
nixpkgs.overlays =
[
(final: prev: {
libvirt = prev.libvirt.override { enableZfs = false; };
})
]
++ lib.optionals cfg.lookingGlass.enable [
(final: prev: {
looking-glass-client = prev.looking-glass-client.overrideAttrs (old: {
version = lgVersion;
src = prev.fetchFromGitHub {
owner = "gnif";
repo = "LookingGlass";
rev = lgRev;
hash = lgHash;
fetchSubmodules = true;
};
patches = old.patches;
postUnpack = ''
echo ${lgRev} > $sourceRoot/VERSION
export sourceRoot="$sourceRoot/client"
'';
});
})
];
programs.virt-manager.enable = true;
virtualisation.spiceUSBRedirection.enable = true;
virtualisation.libvirtd = {
enable = true;
onBoot = "ignore";
onShutdown = "shutdown";
qemu = {
package = pkgs.qemu_kvm;
# GPU + raw block passthrough needs root (or fragile per-device ACLs).
runAsRoot = true;
swtpm.enable = true;
verbatimConfig = lib.mkIf cfg.lookingGlass.enable (lib.mkForce lgQemuVerbatimConfig);
};
};
systemd.services.libvirtd.restartTriggers = [
"${pkgs.qemu_kvm}/bin/qemu-system-x86_64"
];
environment.systemPackages =
with pkgs;
[
virt-manager
qemu_kvm
OVMF
edk2
looking-glass-client
swtpm
]
++ lib.optionals cfg.lookingGlass.enable [
(writeShellScriptBin "looking-glass-toggle" ''
set -euo pipefail
pkill -f 'looking-glass-client' || true
exec looking-glass-client -F -m 97
'')
];
# B7 defaults to /dev/kvmfr0; pin SPICE to the fixed port in the domain XMLs.
environment.etc."looking-glass-client.ini" = lib.mkIf cfg.lookingGlass.enable (lib.mkForce {
text = ''
[app]
shmFile=/dev/kvmfr0
allowDMA=yes
[win]
fullScreen=yes
[spice]
enable=yes
host=127.0.0.1
port=5900
input=yes
audio=yes
clipboard=yes
[input]
captureOnFocus=${lib.boolToString cfg.lookingGlass.captureOnFocus}
escapeKey=97
'';
});
users.groups.libvirtd.members = cfg.libvirtUsers;
users.users =
lib.genAttrs cfg.libvirtUsers (_: {
extraGroups = [
"libvirtd"
"kvm"
"disk"
];
})
// {
qemu-libvirtd.extraGroups = [
"kvm"
"disk"
];
};
}
{
systemd.tmpfiles.rules = [
"d /var/lib/libvirt/boot 0755 root root -"
];
}
(lib.mkIf cfg.lookingGlass.enable {
boot.extraModulePackages = lib.mkAfter [ config.boot.kernelPackages.kvmfr ];
boot.initrd.kernelModules = lib.mkAfter [ "kvmfr" ];
boot.kernelModules = lib.mkAfter [ "kvmfr" ];
boot.kernelParams = lib.mkAfter [
"kvmfr.static_size_mb=${toString cfg.lookingGlass.shmSizeMiB}"
];
boot.extraModprobeConfig = lib.mkAfter ''
options kvmfr static_size_mb=${toString cfg.lookingGlass.shmSizeMiB}
'';
services.udev.packages = [
(pkgs.writeTextFile {
name = "kvmfr-udev";
text = ''
SUBSYSTEM=="kvmfr", GROUP="kvm", MODE="0660", TAG+="uaccess"
'';
destination = "/etc/udev/rules.d/70-kvmfr.rules";
})
];
# insmod from the nix store: extraModulePackages only lands in
# /run/booted-system/kernel-modules after a reboot.
systemd.services.kvmfr = {
description = "Looking Glass kvmfr module";
wantedBy = [ "multi-user.target" ];
before = [
"libvirtd.service"
"libvirtd-config.service"
"libvirt-guests.service"
];
after = [ "systemd-modules-load.service" ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
ExecStart = pkgs.writeShellScript "kvmfr-start" (
let
kvmfrKo = "${config.boot.kernelPackages.kvmfr}/lib/modules/${config.boot.kernelPackages.kernel.modDirVersion}/kernel/drivers/misc/kvmfr.ko";
shmMiB = toString cfg.lookingGlass.shmSizeMiB;
in
''
set -euo pipefail
if [ -e /dev/kvmfr0 ] && [ ! -c /dev/kvmfr0 ]; then
rm -f /dev/kvmfr0
fi
if [ -c /dev/kvmfr0 ]; then
exit 0
fi
if ${pkgs.kmod}/bin/lsmod | ${pkgs.gnugrep}/bin/grep -q '^kvmfr '; then
${pkgs.kmod}/bin/rmmod kvmfr
fi
if [ ! -f ${kvmfrKo} ]; then
echo "kvmfr: module missing at ${kvmfrKo}" >&2
exit 1
fi
${pkgs.kmod}/bin/insmod ${kvmfrKo} static_size_mb=${shmMiB}
if [ ! -c /dev/kvmfr0 ]; then
echo "kvmfr: /dev/kvmfr0 not created (static_size_mb=${shmMiB})" >&2
exit 1
fi
''
);
};
};
systemd.services.libvirtd = lib.mkIf cfg.lookingGlass.enable {
after = [ "kvmfr.service" ];
requires = [ "kvmfr.service" ];
};
})
(lib.mkIf cfg.hugepages.enable {
boot.kernelParams = [ "hugepagesz=2M" "hugepages=64" ];
})
]);
};
}
+1
View File
@@ -18,6 +18,7 @@
wget wget
unzip unzip
jq jq
ripgrep
pfetch pfetch
]; ];
description = "Default packages to install on all hosts."; description = "Default packages to install on all hosts.";
+110
View File
@@ -0,0 +1,110 @@
{ ... }: {
flake.nixosModules.systemTailscale =
{ config, lib, ... }:
let
cfg = config.chiasson.system.networking.tailscale;
tsCfg = config.services.tailscale;
in
{
options.chiasson.system.networking.tailscale = {
enable = lib.mkEnableOption ''
Tailscale mesh VPN. Joins this host to your tailnet for encrypted access
from anywhere without opening inbound ports on your router.
'';
authKeyFile = lib.mkOption {
type = lib.types.nullOr lib.types.path;
default = null;
example = "/run/secrets/tailscale/auth-key";
description = ''
Reusable Tailscale auth key (sops path). Create one at
https://login.tailscale.com/admin/settings/keys enable Reusable and
Pre-approved. Store under `tailscale/auth-key` in secrets/secrets.yaml.
'';
};
openFirewall = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Allow Tailscale UDP 41641 through the host firewall.";
};
acceptRoutes = lib.mkOption {
type = lib.types.bool;
default = false;
description = ''
Accept subnet routes advertised by other tailnet nodes (e.g. home LAN
via nix-server). Enable on portable clients like t2mbp.
'';
};
subnetRouter = {
enable = lib.mkEnableOption ''
Advertise the home LAN through this node so other tailnet devices can
reach local IPs (SSH, Attic, Gitea, Jellyfin, etc.). Enable on an
always-on home host (nix-server). Approve the route in the Tailscale
admin console after first connect.
'';
routes = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ "192.168.2.0/24" ];
example = [ "192.168.2.0/24" ];
description = "Subnets to advertise to the tailnet.";
};
};
};
config = lib.mkIf cfg.enable (lib.mkMerge [
{
services.tailscale = {
enable = true;
openFirewall = cfg.openFirewall;
authKeyFile = cfg.authKeyFile;
};
networking.firewall.trustedInterfaces = lib.mkAfter [ "tailscale0" ];
}
(lib.mkIf cfg.acceptRoutes {
services.tailscale.useRoutingFeatures = "client";
services.tailscale.extraUpFlags = [ "--accept-routes" ];
})
(lib.mkIf cfg.subnetRouter.enable {
services.tailscale.useRoutingFeatures = "both";
services.tailscale.extraUpFlags =
map (route: "--advertise-routes=${route}") cfg.subnetRouter.routes;
})
(lib.mkIf (cfg.authKeyFile != null) {
# Upstream uses `$(cat $authKeyFile)` which breaks when sops leaves a trailing newline.
systemd.services.tailscaled-autoconnect.script = lib.mkOverride 50 ''
getState() {
tailscale status --json --peers=false | jq -r '.BackendState'
}
lastState=""
while state="$(getState)"; do
if [[ "$state" != "$lastState" ]]; then
case "$state" in
NeedsLogin|NeedsMachineAuth|Stopped)
echo "Server needs authentication, sending auth key"
tailscale up --auth-key "$(tr -d '\n\r' < ${cfg.authKeyFile})" ${lib.escapeShellArgs tsCfg.extraUpFlags}
;;
Running)
echo "Tailscale is running"
systemd-notify --ready
exit 0
;;
*)
echo "Waiting for Tailscale State = Running or systemd timeout"
;;
esac
echo "State = $state"
fi
lastState="$state"
sleep .5
done
'';
})
]);
};
}
+15 -2
View File
@@ -23,11 +23,24 @@
''; '';
}; };
extraModules = lib.mkOption { extraModules = lib.mkOption {
type = lib.types.attrsOf (lib.types.listOf lib.types.unspecified); type =
(lib.types.attrsOf (lib.types.listOf lib.types.unspecified))
// {
merge =
loc: defs:
let
values = map (d: d.value) defs;
names = lib.unique (lib.concatLists (map builtins.attrNames values));
in
lib.genAttrs names (
name: lib.concatLists (map (v: v.${name} or [ ]) values)
);
};
default = { }; default = { };
description = '' description = ''
Per-user Home Manager `extraModules` keyed by catalog user name. Per-user Home Manager `extraModules` keyed by catalog user name.
Keys must match `chiasson.users.enabled`. Keys must match `chiasson.users.enabled`. Lists from multiple modules
are concatenated (e.g. `desktop-home-base.nix` + host `home.nix`).
''; '';
}; };
homeManager = { homeManager = {
+4 -4
View File
@@ -10,8 +10,8 @@
./hardware/uconsole-gamepad.nix ./hardware/uconsole-gamepad.nix
]; ];
# Root module: chiasson.home.enable + bash. Everything else is separate `wisdom*` exports — # Root module: chiasson.home.enable + bash. Other `wisdom*` slices auto-wire via
# pull those into `home.users.<name>.extraModules` on each host as needed. # `lib.wisdomCatalogExtraModules`; hosts flip `chiasson.home.*.enable` rather than re-importing.
flake.homeManagerModules.wisdom = flake.homeManagerModules.wisdom =
{ config, lib, ... }: { config, lib, ... }:
let let
@@ -24,8 +24,8 @@
options.chiasson.home = { options.chiasson.home = {
enable = lib.mkEnableOption '' enable = lib.mkEnableOption ''
HM profile root for this flake (bash on by default). Wire other `wisdom*` modules in HM profile root for this flake (bash on by default). Desktop hosts use
`home.users.<name>.extraModules` and flip their `chiasson.home.*.enable` options on the host. `lib.wisdomCatalogExtraModules` once per user and flip `chiasson.home.*.enable` on the host.
'' // { '' // {
default = true; default = true;
}; };
+1 -2
View File
@@ -1,6 +1,5 @@
{ ... }: { { ... }: {
# Optional fragment: import from `home.users.<name>.extraModules` only on hosts that need Yazi so this # Gated by `chiasson.home.shell.yazi.enable` (desktop hosts wire slices via `desktopHomeBase`).
# module (and its `pkgs.yazi` wiring) is not evaluated when omitted.
flake.homeManagerModules.wisdomShellYazi = flake.homeManagerModules.wisdomShellYazi =
{ config, lib, pkgs, ... }: { config, lib, pkgs, ... }:
let let
+9 -1
View File
@@ -1,9 +1,16 @@
{ ... }: { { ... }: {
flake.homeManagerModules.wisdomTerminalsKitty = flake.homeManagerModules.wisdomTerminalsKitty =
{ config, lib, ... }: { config, lib, pkgs, ... }:
let let
root = config.chiasson.home; root = config.chiasson.home;
cfg = config.chiasson.home.terminals.kitty; cfg = config.chiasson.home.terminals.kitty;
kittyShellExe =
if config.programs.fish.enable or false then
lib.getExe config.programs.fish.package
else if config.programs.bash.enable or false then
lib.getExe config.programs.bash.package
else
"${pkgs.bash}/bin/bash";
in in
{ {
options.chiasson.home.terminals.kitty.enable = lib.mkEnableOption '' options.chiasson.home.terminals.kitty.enable = lib.mkEnableOption ''
@@ -14,6 +21,7 @@
programs.kitty = { programs.kitty = {
enable = true; enable = true;
extraConfig = '' extraConfig = ''
shell ${kittyShellExe}
include dank-tabs.conf include dank-tabs.conf
include dank-theme.conf include dank-theme.conf
allow_remote_control yes allow_remote_control yes
+5 -3
View File
@@ -10,6 +10,8 @@ cachix:
caching: caching:
attic: attic:
token: ENC[AES256_GCM,data:8omssG3GwCFIegfz+8IAGGhFGj01RB3dqqHeFpmZOzMJUshIDvSRuTTpGFhUBC7Xue8h09hAhpirIHmqzyG3I+e2Se/VZZoByXmpyIKesl3+NqOXDkJvgImqhvFVkTiSe5p/vSN3slWDylfkThQ0hZYw5mB9J13M5965iUnWcRbg+1fYFdTuSgrHY8Rxt4da0287A0YGnsN63k7j32XOJndxsRoOLoo+IQ+X+hiPOJkfGYxY0MglnxaxhPwH8SP1V+p78N75Z2npOtMdEikdHmj/NmKbqUXN2P0+IXthxV17WePCulZVsKC1Jw+clgbyAvHcQeVG/yyrcb1CRRQpszHtq1Pz7DHvfAG+gxyPNyP7D6oQNT8foX4C6CwuHgYQtM1x0D6oAL+lppQWJ0kEV/GDlJSXQnp/aBbVAqDmqS0TCx40nVmQ0PvMcjtsiZJigkRJRNLCg6n+qmhc5Rh9RhslPN5JXU0orWs9QYAoLXzdDDGP/R9tlEhwQBxwGrFAp016iilqPavMdI8txrWWdvezQuAh//eeW5LQSa6t363VCjX8phnXeJltOgXYlyuKnCCmv0a6XwhmT0PA+32/F0BxTf9lcZConpurlvOHdznaVeUXcFOEwKouDC7smPIZZqcRU8OIbWs7YXqMgatgb/bJVtB0P0Avsj9t9Uz8Dv8xBV+90U5qwM7HV16FIERorDquzgKFcvtb8/QfjTINoswpHZKNCbmQPxfJYPheJFwMQGFn+b+ecv+Z7qng9JEujJSNtEPv2CIuVmSxZJaU5g2CMu3rFGIA3qF81Bf1Ri8n+KYWgOKpQt11nClouv2XePO8JKI6fslF411zJ8zD4E/6Qg95UWhLh0RG2cXzYSXXvrpXDlIe9spc4OLuj4tFtXkiZZvfM5MgRTtoh93soypUpEbswTji2UprC3OPikjIIW49YysGVsH2100/67HbtinRoazM1M+DjaD2pMryx7kW/oVpyaW61wiqtHk9nq8vqROLWBhQxzGSh9157z/46AT+8PN89gFh5uNdFuhFz8e8/HIV3HtIrzrtR+flJfHJ1ZT5dhTDicSMiC/DhG/hupX4GHGX6zlaMgBqB8bKxxvs+v0iHfSkDIkuenZ+nTD72DP5yuIQVIwGV16CZA6rusjb1zLn6QYpvQtCuqlih+epsGNEYP6B3rvNMc/N7JcwY4YMTK+C46EC9mXhpfPn0a5OdD4kQ6s=,iv:+g9W5MzgtLppD1K3dZ/tCuaMxaa194W3Lf23/jUmDvk=,tag:5uVwIOkB2+MRHPGlKGQtGg==,type:str] token: ENC[AES256_GCM,data:8omssG3GwCFIegfz+8IAGGhFGj01RB3dqqHeFpmZOzMJUshIDvSRuTTpGFhUBC7Xue8h09hAhpirIHmqzyG3I+e2Se/VZZoByXmpyIKesl3+NqOXDkJvgImqhvFVkTiSe5p/vSN3slWDylfkThQ0hZYw5mB9J13M5965iUnWcRbg+1fYFdTuSgrHY8Rxt4da0287A0YGnsN63k7j32XOJndxsRoOLoo+IQ+X+hiPOJkfGYxY0MglnxaxhPwH8SP1V+p78N75Z2npOtMdEikdHmj/NmKbqUXN2P0+IXthxV17WePCulZVsKC1Jw+clgbyAvHcQeVG/yyrcb1CRRQpszHtq1Pz7DHvfAG+gxyPNyP7D6oQNT8foX4C6CwuHgYQtM1x0D6oAL+lppQWJ0kEV/GDlJSXQnp/aBbVAqDmqS0TCx40nVmQ0PvMcjtsiZJigkRJRNLCg6n+qmhc5Rh9RhslPN5JXU0orWs9QYAoLXzdDDGP/R9tlEhwQBxwGrFAp016iilqPavMdI8txrWWdvezQuAh//eeW5LQSa6t363VCjX8phnXeJltOgXYlyuKnCCmv0a6XwhmT0PA+32/F0BxTf9lcZConpurlvOHdznaVeUXcFOEwKouDC7smPIZZqcRU8OIbWs7YXqMgatgb/bJVtB0P0Avsj9t9Uz8Dv8xBV+90U5qwM7HV16FIERorDquzgKFcvtb8/QfjTINoswpHZKNCbmQPxfJYPheJFwMQGFn+b+ecv+Z7qng9JEujJSNtEPv2CIuVmSxZJaU5g2CMu3rFGIA3qF81Bf1Ri8n+KYWgOKpQt11nClouv2XePO8JKI6fslF411zJ8zD4E/6Qg95UWhLh0RG2cXzYSXXvrpXDlIe9spc4OLuj4tFtXkiZZvfM5MgRTtoh93soypUpEbswTji2UprC3OPikjIIW49YysGVsH2100/67HbtinRoazM1M+DjaD2pMryx7kW/oVpyaW61wiqtHk9nq8vqROLWBhQxzGSh9157z/46AT+8PN89gFh5uNdFuhFz8e8/HIV3HtIrzrtR+flJfHJ1ZT5dhTDicSMiC/DhG/hupX4GHGX6zlaMgBqB8bKxxvs+v0iHfSkDIkuenZ+nTD72DP5yuIQVIwGV16CZA6rusjb1zLn6QYpvQtCuqlih+epsGNEYP6B3rvNMc/N7JcwY4YMTK+C46EC9mXhpfPn0a5OdD4kQ6s=,iv:+g9W5MzgtLppD1K3dZ/tCuaMxaa194W3Lf23/jUmDvk=,tag:5uVwIOkB2+MRHPGlKGQtGg==,type:str]
tailscale:
auth-key: ENC[AES256_GCM,data:ffBLc/LIm67P/DUSvDUek/qWHLfQbmSE3jQL1/TLHrZEEGk+HqEO4Prlj5bGGAU37tiHaD7Ksj1wqX3U+Q==,iv:Uw1tnuxMPZVwqvrRkZZusJM/a7QeoggR047+CKx9fnY=,tag:kS1IixpXYm1QVBIAmmgQ6Q==,type:str]
sops: sops:
age: age:
- enc: | - enc: |
@@ -75,7 +77,7 @@ sops:
6POXxpxjGhlWJaV47jqeN+7mQY2oTHE/x4raoX/KA2ouXL29K8QpmA== 6POXxpxjGhlWJaV47jqeN+7mQY2oTHE/x4raoX/KA2ouXL29K8QpmA==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
recipient: age1pewusvlcgzcnk0kpskgc9qr6jlq8s2yzwnqrnh84p7v5z0kj3qhsya8h2x recipient: age1pewusvlcgzcnk0kpskgc9qr6jlq8s2yzwnqrnh84p7v5z0kj3qhsya8h2x
lastmodified: "2026-03-24T00:15:02Z" lastmodified: "2026-06-10T03:00:15Z"
mac: ENC[AES256_GCM,data:dYTwO5DtkKinTKfBXGuvXRFxl8yavxXMKTw27M5/GcK/kkstHBG119IRk9B9KC6s6IHTY81U3MeUxE9XwdBiE7q4m15+ZO2vmdBVhN8wAh+82P9BP0HSaxLkjWLeKWBfULyLX/YXmQVsr09/NUEVSZcugJ6m40Ta+X9AQgO+cyA=,iv:FmsznsKTuIr61s3Zn0QZKSKvb/e2AljEB1ijKE52RKk=,tag:rHF2Xi4iP9VF33rxpBr5pg==,type:str] mac: ENC[AES256_GCM,data:F1DU7Sj9lsTOHWlyuY4L4N+urrBXv2GLKhlzohMA9iF6/bphRABa9we3WZX9MFNIc2Te+2SCh3f6Rv5eHwWSsZVdD4iwN/6HTabdNPTEOZyk/OVsap/F374MLu8ys3OVZ5vzg2PYCGmhlf4PQMrjvaz+CV3t8UezB6E4U0N3RuU=,iv:wT2ieUON5VInJhDtZFLENzyaty5TqcisLmYePnCHGM4=,tag:z9CYpCWa/XdSqPKMG3M41A==,type:str]
unencrypted_suffix: _unencrypted unencrypted_suffix: _unencrypted
version: 3.12.1 version: 3.13.1