Compare commits

...

15 Commits

Author SHA1 Message Date
Olivier 3ca626dffe Flake lock update 2026-06-06 15:51:46 -03:00
Olivier 2bdb173ac7 Refactor NixOS configurations to replace "NixOS-V2" with "chiasson-nix" across multiple modules
- Updated wallpaper module to reflect new naming convention for wallpapers.
- Changed author name in rbw-lock-toggle and wvkbd-toggle plugins to "chiasson.cloud".
- Modified ideapad configuration to use the new flake path.
- Adjusted yt-dlp-telequebec-overlay documentation to match new module structure.
- Corrected documentation reference in wisdom module to point to updated conventions.
2026-06-06 03:26:58 -03:00
Olivier d05212ffd3 Add documentation for NixOS and Home Manager conventions 2026-06-06 03:26:19 -03:00
Olivier 380b428d9a Implement r5500 media stack configuration with NFS and Docker services
- Added configuration for media stack on r5500, including paths for Jellyfin, Sonarr, Radarr, and other media services.
- Integrated NFS client for accessing Jellyfin libraries from nixdesk.
- Established Docker services for Dispatcharr and Organizr, including necessary user and group setups.
- Created systemd services for managing media directories and ensuring proper permissions.
2026-06-04 16:57:15 -03:00
Olivier 2a911b057b Add navi deployment module and integrate SSH inventory for remote management
- Introduced a new `navi` module for managing deployments across multiple hosts.
- Enhanced SSH inventory management to support public key application for authorized hosts.
- Configured system deployment builder for seamless integration with Navi.
- Updated various host configurations to enable deployment capabilities and streamline SSH access.
2026-06-04 16:51:30 -03:00
Olivier 403cf2fde5 Update flake.nix to specify DDRM revision; modify 14900k host configuration.nix to enhance git settings and include devenv in services 2026-06-04 16:46:39 -03:00
Olivier 65064d971c Add Moonfin Flatpak bundle support and update Flatpak configuration 2026-06-04 16:44:23 -03:00
Olivier 875fa633c7 Update pokeclicker default.nix to include libxscrnsaver and libxtst dependencies 2026-06-04 16:43:17 -03:00
Olivier 1cbca207b6 Update gtk4 theming configuration in gtk-qt-theming.nix 2026-06-04 16:42:31 -03:00
Olivier f701177197 Disable Hyprland for Ideapad host 2026-06-04 16:41:41 -03:00
Olivier 778cde8c40 Update kernel reference in t2linux to use latest version (7.0) 2026-06-04 16:41:14 -03:00
Olivier f9d5c70fb1 Refactor cursor package handling in home-manager configuration
- Simplified the cursor package selection by removing the deprecated `cursor-agent` option and retaining only `cursor-cli`.
- Updated the activation script to remove the broken `cursor-agent` symlink, ensuring proper resolution to `cursor-cli`.
- Adjusted the configuration to reflect these changes in the Nix IDE tools integration.
2026-06-04 16:40:12 -03:00
Olivier 1fd25998fb Add raspberrypi-utils overlay for handling dropped attributes 2026-06-04 16:39:34 -03:00
Olivier 7994f174f6 Toggle dock visibility to false in home-manager configuration 2026-06-04 16:38:50 -03:00
Olivier d48e4f8a08 Update dgop package reference to use host platform 2026-06-04 16:38:25 -03:00
46 changed files with 1519 additions and 135 deletions
+47
View File
@@ -0,0 +1,47 @@
# chiasson-nix
My personal NixOS + Home Manager flake. [flake.nix](../flake.nix) feeds `./modules` through flake-parts and [import-tree](https://github.com/vic/import-tree) so config stays in small files instead of one monster module.
How to work on it: [conventions.md](./conventions.md).
## Hosts
| Host | What it is |
|------|------------|
| `t2mbp` | Intel MacBook with T2 chip — Niri/DMS, `t2linux` + fan daemon |
| `14900k` | Main x86 desktop — same GUI stack, also cross-builds aarch64 and exports NFS for Jellyfin |
| `ideapad` | Lenovo IdeaPad Duet 3 (Chromebook, `lenovo-wormdingler`) — Mobile NixOS on Snapdragon, Niri/DMS, touch/tablet bits in `_private/` |
| `uConsole` | Clockwork Pi uConsole (Pi 5 CM) — Niri/DMS, built with `nixos-raspberrypi` + oom-hardware |
| `nix-server` | Headless x86 VM — Attic, Gitea, Immich, SwiftShare, Personal-Website, DDRM, Portainer, … |
| `r5500` | Headless media box — Jellyfin, *arr, qBittorrent, Dispatcharr; library on NFS from `nix-server` |
Each machine: `modules/hosts/<name>/default.nix``nixosConfigurations.<name>`, real config in `*Configuration` + optional `_private/` and `_services/`.
## Deploy / rebuild
Remote builds use the `builder` user (`systemDeployBuilder`, wired through `client-services` on desktops).
Fleet deploy is [Navi](https://github.com/cafkafk/navi) — config in `modules/deploy/navi.nix`, outputs `flake.navi` / `flake.naviHive`.
```bash
nix develop # devShell has navi + hints
navi apply --on <host>
navi apply-local --node 14900k --sudo # this machine, if hostname matches
```
Plain rebuild still works: `sudo nixos-rebuild switch --flake .#<host>`.
## Repo layout
```
modules/
hosts/<host>/ # per-machine composition
system/ # nixosModules.system aggregate
desktop/ # GUI stack
wisdom/ # HM modules (exports still named wisdom*)
ssh/ # inbound NixOS + outbound HM
deploy/ # navi hive
lib/ # pure helpers → flake.lib
```
Machine-only stuff lives in `hosts/<host>/_private/` — import-tree skips `_private/` globally, so those files only get pulled in where you import them.
+105
View File
@@ -0,0 +1,105 @@
# Conventions
## How the flake is wired
`flake.nix` runs import-tree on `./modules` with a few extra filters: skip `/package/`, `/packages/`, and `dms/home-manager/` (callPackage noise + DMS HM colocation). Don't add a global `/home-manager/` filter — `modules/ssh/home-manager/` needs to load.
Every scanned `.nix` should be a flake fragment:
```nix
{ self, inputs, ... }: {
flake.nixosModules.systemFoo = { config, lib, pkgs, ... }: { /* */ };
}
```
Don't define the same `flake.nixosModules.foo` in two files — merge order isn't something to bet on.
**Outputs I actually use:** `nixosConfigurations.*`, `nixosModules.*`, `homeManagerModules.*`, `lib.*`, `packages.*`, `navi` / `naviHive`. `modules/parts.nix` declares supported systems and the `flake.lib` / `flake.homeManagerModules` option slots.
## Option namespaces
Project options live under `chiasson.*` only:
| Prefix | For |
|--------|-----|
| `chiasson.system.*` | Machine policy — docker, flatpak, audio, gaming, deploy builder, … |
| `chiasson.desktop.*` | GUI — compositors, DMS, wallpapers, display manager |
| `chiasson.home.*` | Home Manager toggles (module exports are still `wisdom*`) |
| `chiasson.users.*` | Catalog, `enabled`, `hostOverrides`, `extraModules` |
| `chiasson.ssh.*` | SSH inventory |
Leave upstream `services.*`, `networking.*`, `home.*`, etc. alone.
## Module shape
**NixOS leaf** — camelCase export, usually prefixed (`systemDocker`, `desktopNiri`):
```nix
{ ... }: {
flake.nixosModules.systemSomething = { config, lib, pkgs, ... }:
let cfg = config.chiasson.system.something;
in {
options.chiasson.system.something.enable = lib.mkEnableOption "";
config = lib.mkIf cfg.enable { /* */ };
};
}
```
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`.
User apps / dotfiles → wisdom. Daemons, firewall, kernel → NixOS. Sometimes both (LocalSend: HM installs, `systemLocalsend` opens the firewall).
**Odd exports**
- `"client-services"` — shared setup for laptops/tablets/desktops (not the VMs). Turns on Bluetooth, lets Navi log in as `builder` to update the machine, enables printing and mounting USB drives, and sets SSH to password. Used on `t2mbp`, `14900k`, `ideapad`, `uConsole`. Skipped on `nix-server` and `r5500`.
- `systemUconsoleKernelBuilder` — cross-build the uConsole kernel on x86_64, not for running on the Pi.
- `systemFlatpak` — allowlist sync; skips heavy `flatpak update` when lists unchanged (`runHeavyMaintenanceEverySwitch`).
**Desktop notes**
- `chiasson.desktop.displayManager.variant`: `sddm` vs `dankgreeter` (DMS + Hyprland/Niri tends toward DankGreeter).
- `desktopWallpapers` copies `inputs.wallpapers` into the store; override `chiasson.desktop.wallpapers.source` if needed.
## Users
User definitions live in `usersCatalogDefaults` (`modules/system/users/catalog-default.nix`) — olivier, server, builder, etc. Pick who exists on a host with `chiasson.users.enabled`.
Passwords aren't in the repo. They're in `secrets/secrets.yaml` (encrypted with sops). Each host that has `olivier` must declare that secret and set `neededForUsers = true`; sops-nix decrypts it at boot, and the catalog points `hashedPasswordFile` at that file. That's why the catalog is a proper module instead of a static list — it needs `config.sops.secrets.…` at eval time.
On a host:
```nix
chiasson.users.enabled = [ "olivier" ];
chiasson.users.hostOverrides.<name> = { /* optional */ };
chiasson.users.extraModules.olivier = [
self.homeManagerModules.wisdomTerminalsKitty
# …
];
```
`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/`.
## Adding things
**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.
**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.
## When editing
- `nix flake check` after touching `modules/`.
- Stage/commit new files if the flake is read from git.
- Small diffs; `mkEnableOption` + `mkIf` for toggles.
- Cross-module deps: assertions or `mkDefault`, mention it in the option description.
- New flake inputs in `flake.nix` with `follows` where versions should track nixpkgs.
- `client-services` SSH defaults are for my LAN — don't copy to anything internet-facing without tightening.
- If I'll forget the behavior in six months, update these docs in the same change.
Generated
+337 -50
View File
@@ -16,6 +16,21 @@
"type": "github" "type": "github"
} }
}, },
"crane": {
"locked": {
"lastModified": 1770169865,
"narHash": "sha256-iPiy13xzDQ9GjpOez+NNIjh/qjl7i4RDf9dF2x5mF9I=",
"owner": "ipetkov",
"repo": "crane",
"rev": "8254ccf3b5b5131890ee073776f2e61c6d1e55d4",
"type": "github"
},
"original": {
"owner": "ipetkov",
"repo": "crane",
"type": "github"
}
},
"cursor": { "cursor": {
"inputs": { "inputs": {
"nixpkgs": [ "nixpkgs": [
@@ -23,11 +38,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1779537840, "lastModified": 1780452866,
"narHash": "sha256-IS3aolEKgyL0VuMfd/QX2AHvur1YukCTa6eZdxQWe1A=", "narHash": "sha256-Fq5cR/qGYcGwOOZf6sShtM+kljwecgntgcKXnIBrQDE=",
"ref": "refs/heads/main", "ref": "refs/heads/main",
"rev": "8d9c19f98abf47aa4504efa8d2233730b4afed50", "rev": "344f4065e69455adbcdfb4db1cd8d07a73ae7140",
"revCount": 109, "revCount": 114,
"type": "git", "type": "git",
"url": "https://git.chiasson.cloud/Olivier/cursor-nixos-flake" "url": "https://git.chiasson.cloud/Olivier/cursor-nixos-flake"
}, },
@@ -52,6 +67,7 @@
"url": "https://git.chiasson.cloud/Olivier/DDRM" "url": "https://git.chiasson.cloud/Olivier/DDRM"
}, },
"original": { "original": {
"rev": "c3d014f96855e45b53f7391ce19729493887cb96",
"type": "git", "type": "git",
"url": "https://git.chiasson.cloud/Olivier/DDRM" "url": "https://git.chiasson.cloud/Olivier/DDRM"
} }
@@ -76,6 +92,29 @@
"type": "github" "type": "github"
} }
}, },
"disko": {
"inputs": {
"nixpkgs": [
"navi",
"nixos-anywhere",
"nixpkgs"
]
},
"locked": {
"lastModified": 1769524058,
"narHash": "sha256-zygdD6X1PcVNR2PsyK4ptzrVEiAdbMqLos7utrMDEWE=",
"owner": "nix-community",
"repo": "disko",
"rev": "71a3fc97d80881e91710fe721f1158d3b96ae14d",
"type": "github"
},
"original": {
"owner": "nix-community",
"ref": "master",
"repo": "disko",
"type": "github"
}
},
"dms": { "dms": {
"inputs": { "inputs": {
"nixpkgs": [ "nixpkgs": [
@@ -149,6 +188,46 @@
"inputs": { "inputs": {
"nixpkgs-lib": "nixpkgs-lib_2" "nixpkgs-lib": "nixpkgs-lib_2"
}, },
"locked": {
"lastModified": 1769996383,
"narHash": "sha256-AnYjnFWgS49RlqX7LrC4uA+sCCDBj0Ry/WOJ5XWAsa0=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "57928607ea566b5db3ad13af0e57e921e6b12381",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"flake-parts_3": {
"inputs": {
"nixpkgs-lib": [
"navi",
"nixos-anywhere",
"nixpkgs"
]
},
"locked": {
"lastModified": 1768135262,
"narHash": "sha256-PVvu7OqHBGWN16zSi6tEmPwwHQ4rLPU9Plvs8/1TUBY=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "80daad04eddbbf5a4d883996a73f3f542fa437ac",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"flake-parts_4": {
"inputs": {
"nixpkgs-lib": "nixpkgs-lib_3"
},
"locked": { "locked": {
"lastModified": 1778716662, "lastModified": 1778716662,
"narHash": "sha256-m1Yf0wZ8j1OHjTc2UwHwyQRSnNeSgLJOd7q5Y45hzi4=", "narHash": "sha256-m1Yf0wZ8j1OHjTc2UwHwyQRSnNeSgLJOd7q5Y45hzi4=",
@@ -163,7 +242,7 @@
"type": "github" "type": "github"
} }
}, },
"flake-parts_3": { "flake-parts_5": {
"inputs": { "inputs": {
"nixpkgs-lib": [ "nixpkgs-lib": [
"nur", "nur",
@@ -227,11 +306,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1779507042, "lastModified": 1780679734,
"narHash": "sha256-7wOwi8B6D0BYsieZCnHZZj2sNUzgJhLoIVSfkwB7lxQ=", "narHash": "sha256-KmRNvpNOb7QEORa06bVgjW9kITcx0VhsI7w0vhmZyD8=",
"owner": "nix-community", "owner": "nix-community",
"repo": "home-manager", "repo": "home-manager",
"rev": "509ed3c603349a9d43de9e2ae6613baea6bd5b34", "rev": "b2b7db486e06e098711dc291bb25db82850e1d16",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -248,11 +327,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1778805320, "lastModified": 1780515920,
"narHash": "sha256-nGFJ01m2CTBKD4ABtcY4vLhHrRN91LKr/pn41PcU78A=", "narHash": "sha256-8KX2hEeOX6KP3hBBJJI8dGWVrzbOOf1rBPmg/GUG24U=",
"owner": "nix-community", "owner": "nix-community",
"repo": "home-manager", "repo": "home-manager",
"rev": "9846abe15e7d0d36b8acbd4d05f2b87461744c92", "rev": "4c5c1e8ba14f1c7475fa31ff11bc1c19cd220974",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -292,6 +371,50 @@
"type": "github" "type": "github"
} }
}, },
"navi": {
"inputs": {
"crane": "crane",
"flake-parts": "flake-parts_2",
"nix-github-actions": "nix-github-actions",
"nixos-anywhere": "nixos-anywhere",
"nixpkgs": "nixpkgs",
"stable": "stable"
},
"locked": {
"lastModified": 1777283737,
"narHash": "sha256-sVwTOYwxzUUqAEr5kjOZooJ/v8H2hcuZ42f17MgCqX8=",
"owner": "cafkafk",
"repo": "navi",
"rev": "b060683e4ca2bd37a1c3bcb4e04abd2296190e9d",
"type": "github"
},
"original": {
"owner": "cafkafk",
"repo": "navi",
"type": "github"
}
},
"nix-github-actions": {
"inputs": {
"nixpkgs": [
"navi",
"nixpkgs"
]
},
"locked": {
"lastModified": 1729742964,
"narHash": "sha256-B4mzTcQ0FZHdpeWcpDYPERtyjJd/NIuaQ9+BV1h+MpA=",
"owner": "nix-community",
"repo": "nix-github-actions",
"rev": "e04df33f62cdcf93d73e9a04142464753a16db67",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nix-github-actions",
"type": "github"
}
},
"nix-monitor": { "nix-monitor": {
"locked": { "locked": {
"lastModified": 1771568669, "lastModified": 1771568669,
@@ -307,21 +430,44 @@
"type": "github" "type": "github"
} }
}, },
"nix-vm-test": {
"inputs": {
"nixpkgs": [
"navi",
"nixos-anywhere",
"nixpkgs"
]
},
"locked": {
"lastModified": 1769079217,
"narHash": "sha256-R6qzhu+YJolxE2vUsPQWWwUKMbAG5nXX3pBtg8BNX38=",
"owner": "Enzime",
"repo": "nix-vm-test",
"rev": "58c15f78947b431d6c206e0966500c7e9139bd2f",
"type": "github"
},
"original": {
"owner": "Enzime",
"ref": "pr-105-latest",
"repo": "nix-vm-test",
"type": "github"
}
},
"nixcord": { "nixcord": {
"inputs": { "inputs": {
"flake-compat": "flake-compat", "flake-compat": "flake-compat",
"flake-parts": "flake-parts_2", "flake-parts": "flake-parts_4",
"nixpkgs": [ "nixpkgs": [
"nixpkgs" "nixpkgs"
], ],
"nixpkgs-nixcord": "nixpkgs-nixcord" "nixpkgs-nixcord": "nixpkgs-nixcord"
}, },
"locked": { "locked": {
"lastModified": 1779498537, "lastModified": 1780751320,
"narHash": "sha256-6LQjFDS69JufrN4sVsMNsXxeSF6BbDzMSbN7sVApsaA=", "narHash": "sha256-3PlWrZkSwElqYHMnWi4d0A+GxlvCXk0JaMsTGghmQGE=",
"owner": "KaylorBen", "owner": "KaylorBen",
"repo": "nixcord", "repo": "nixcord",
"rev": "45a98c17b0d9e695bdee92ab00c76657eddf47e7", "rev": "2b70dbed679d2705696deb36f0778c9ef9f1ded2",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -330,7 +476,62 @@
"type": "github" "type": "github"
} }
}, },
"nixos-anywhere": {
"inputs": {
"disko": "disko",
"flake-parts": "flake-parts_3",
"nix-vm-test": "nix-vm-test",
"nixos-images": "nixos-images",
"nixos-stable": "nixos-stable",
"nixpkgs": [
"navi",
"nixpkgs"
],
"treefmt-nix": "treefmt-nix"
},
"locked": {
"lastModified": 1772874867,
"narHash": "sha256-d79nCSys2aaV6E1tL/H4xEeKJvLFwEeP6yj5q0glbuQ=",
"owner": "cafkafk",
"repo": "nixos-anywhere",
"rev": "b88c2ee9734ce4ec5aa63cf74c2ea58f6f9970ec",
"type": "github"
},
"original": {
"owner": "cafkafk",
"ref": "kexec-ssh-args-bug",
"repo": "nixos-anywhere",
"type": "github"
}
},
"nixos-images": { "nixos-images": {
"inputs": {
"nixos-stable": [
"navi",
"nixos-anywhere",
"nixos-stable"
],
"nixos-unstable": [
"navi",
"nixos-anywhere",
"nixpkgs"
]
},
"locked": {
"lastModified": 1766770015,
"narHash": "sha256-kUmVBU+uBUPl/v3biPiWrk680b8N9rRMhtY97wsxiJc=",
"owner": "nix-community",
"repo": "nixos-images",
"rev": "e4dba54ddb6b2ad9c6550e5baaed2fa27938a5d2",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nixos-images",
"type": "github"
}
},
"nixos-images_2": {
"inputs": { "inputs": {
"nixos-stable": [ "nixos-stable": [
"nixos-raspberrypi", "nixos-raspberrypi",
@@ -360,7 +561,7 @@
"inputs": { "inputs": {
"argononed": "argononed", "argononed": "argononed",
"flake-compat": "flake-compat_2", "flake-compat": "flake-compat_2",
"nixos-images": "nixos-images", "nixos-images": "nixos-images_2",
"nixpkgs": [ "nixpkgs": [
"nixpkgs" "nixpkgs"
] ]
@@ -380,17 +581,33 @@
"type": "github" "type": "github"
} }
}, },
"nixpkgs": { "nixos-stable": {
"locked": { "locked": {
"lastModified": 1779357205, "lastModified": 1769598131,
"narHash": "sha256-cCO8aTqss5x9Ky8GWkpY0Hy5fyTZEbtifSUV8QjSzic=", "narHash": "sha256-e7VO/kGLgRMbWtpBqdWl0uFg8Y2XWFMdz0uUJvlML8o=",
"owner": "nixos", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "f83fc3c307e74bc5fd5adb7eb6b8b13ffd2a36e1", "rev": "fa83fd837f3098e3e678e6cf017b2b36102c7211",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "nixos", "owner": "NixOS",
"ref": "nixos-25.11",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1750134718,
"narHash": "sha256-v263g4GbxXv87hMXMCpjkIxd/viIF7p3JpJrwgKdNiI=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "9e83b64f727c88a7711a2c463a7b16eedb69a84c",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable", "ref": "nixos-unstable",
"repo": "nixpkgs", "repo": "nixpkgs",
"type": "github" "type": "github"
@@ -412,6 +629,21 @@
} }
}, },
"nixpkgs-lib_2": { "nixpkgs-lib_2": {
"locked": {
"lastModified": 1769909678,
"narHash": "sha256-cBEymOf4/o3FD5AZnzC3J9hLbiZ+QDT/KDuyHXVJOpM=",
"owner": "nix-community",
"repo": "nixpkgs.lib",
"rev": "72716169fe93074c333e8d0173151350670b824c",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nixpkgs.lib",
"type": "github"
}
},
"nixpkgs-lib_3": {
"locked": { "locked": {
"lastModified": 1777168982, "lastModified": 1777168982,
"narHash": "sha256-GOkGPcboWE9BmGCRMLX3worL4EMnsnG8MyKmXNeYuhQ=", "narHash": "sha256-GOkGPcboWE9BmGCRMLX3worL4EMnsnG8MyKmXNeYuhQ=",
@@ -428,27 +660,43 @@
}, },
"nixpkgs-nixcord": { "nixpkgs-nixcord": {
"locked": { "locked": {
"lastModified": 1779102034, "lastModified": 1780453794,
"narHash": "sha256-vZJZjLo513IeI8hjzHFc6TDezUd4uCE2Eq4SNO3DNNg=", "narHash": "sha256-bXMRa9VTsHSPXL4Cw8R6JJLQeY3Y/IP4+YJCYVmQ7FY=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "687f05a9184cad4eaf905c48b63649e3a86f5433", "rev": "6b316287bae2ee04c9b93c8c858d930fd07d7338",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "NixOS", "owner": "NixOS",
"ref": "nixos-25.11", "ref": "nixos-26.05",
"repo": "nixpkgs", "repo": "nixpkgs",
"type": "github" "type": "github"
} }
}, },
"nixpkgs_2": { "nixpkgs_2": {
"locked": { "locked": {
"lastModified": 1778869304, "lastModified": 1780243769,
"narHash": "sha256-30sZNZoA1cqF5JNO9fVX+wgiQYjB7HJqqJ4ztCDeBZE=", "narHash": "sha256-x5UQuRsH3MqI0U9afaXSNqzTPSeZlRLvFAav2Ux1pNw=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "331800de5053fcebacf6813adb5db9c9dca22a0c",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_3": {
"locked": {
"lastModified": 1780336545,
"narHash": "sha256-vhVhuXzFrIOfcssC/9hDHx7MHzDKjF3keHuREOQqQiQ=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "d233902339c02a9c334e7e593de68855ad26c4cb", "rev": "4df1b885d76a54e1aa1a318f8d16fd6005b6401f",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -460,17 +708,17 @@
}, },
"nur": { "nur": {
"inputs": { "inputs": {
"flake-parts": "flake-parts_3", "flake-parts": "flake-parts_5",
"nixpkgs": [ "nixpkgs": [
"nixpkgs" "nixpkgs"
] ]
}, },
"locked": { "locked": {
"lastModified": 1779493406, "lastModified": 1780771425,
"narHash": "sha256-70dCjL6KdsNG+hPHqUsrTF/gQtnucRMo2B/oGvf8aOw=", "narHash": "sha256-qxX2a/MnwrIkMUZ7nLafJkDvaDaLLsPRC0NK2VXW+sc=",
"owner": "nix-community", "owner": "nix-community",
"repo": "NUR", "repo": "NUR",
"rev": "e27d8a76f2167da18bd37ab38f463c13daf2bc21", "rev": "a144829f0faf14576d4faa88a42cd62bebb7428b",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -556,10 +804,11 @@
"home-manager": "home-manager", "home-manager": "home-manager",
"import-tree": "import-tree", "import-tree": "import-tree",
"mobile-nixos": "mobile-nixos", "mobile-nixos": "mobile-nixos",
"navi": "navi",
"nix-monitor": "nix-monitor", "nix-monitor": "nix-monitor",
"nixcord": "nixcord", "nixcord": "nixcord",
"nixos-raspberrypi": "nixos-raspberrypi", "nixos-raspberrypi": "nixos-raspberrypi",
"nixpkgs": "nixpkgs", "nixpkgs": "nixpkgs_2",
"nur": "nur", "nur": "nur",
"oom-hardware": "oom-hardware", "oom-hardware": "oom-hardware",
"personal-website": "personal-website", "personal-website": "personal-website",
@@ -580,11 +829,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1777944972, "lastModified": 1780547341,
"narHash": "sha256-VfGRo1qTBKOe3s2gOv8LSoA6Fk19PvBlwQ1ECN0Evn8=", "narHash": "sha256-Gq8KNx5A7hBB3uGJaj6eQfLDIz5YdLu92gqBcvHvoUo=",
"owner": "Mic92", "owner": "Mic92",
"repo": "sops-nix", "repo": "sops-nix",
"rev": "c591bf665727040c6cc5cb409079acb22dcce33c", "rev": "9ed65852b6257fbeae4355bc24ecfea307ca759a",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -601,11 +850,11 @@
"systems": "systems_2" "systems": "systems_2"
}, },
"locked": { "locked": {
"lastModified": 1779000518, "lastModified": 1780422259,
"narHash": "sha256-wdtytSnzMe85J/qeXJALMzSLRFTZ1gBHwn81l1PtT8k=", "narHash": "sha256-dWGk4SEdI189kQW5cE4Uo1Mc+P+kQEdgMcyMgTtmQOA=",
"owner": "Gerg-L", "owner": "Gerg-L",
"repo": "spicetify-nix", "repo": "spicetify-nix",
"rev": "5dde76b38418892ccb3d99e99bed7f8a43ac294c", "rev": "8414bbf2fcc7bc0a22c675e498e3c7365c1aec0a",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -614,6 +863,22 @@
"type": "github" "type": "github"
} }
}, },
"stable": {
"locked": {
"lastModified": 1750133334,
"narHash": "sha256-urV51uWH7fVnhIvsZIELIYalMYsyr2FCalvlRTzqWRw=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "36ab78dab7da2e4e27911007033713bab534187b",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-25.05",
"repo": "nixpkgs",
"type": "github"
}
},
"swiftshare": { "swiftshare": {
"inputs": { "inputs": {
"flake-utils": "flake-utils_2", "flake-utils": "flake-utils_2",
@@ -703,11 +968,11 @@
"t2linux-patches": { "t2linux-patches": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1779369552, "lastModified": 1779914035,
"narHash": "sha256-vDcWjgjhYAQcXZH40QN17ZV9BS0zqZeme9APXBqjlHs=", "narHash": "sha256-VsHuI2CbQ8gFplW+51gUJvCqo1Ts10Ueks9aTtkAOiw=",
"owner": "t2linux", "owner": "t2linux",
"repo": "linux-t2-patches", "repo": "linux-t2-patches",
"rev": "716093d3244566cd708362661de269ab7e67ff0f", "rev": "7ee7d19c38e5df31a386b2a0c35ca8f064003960",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -716,6 +981,28 @@
"type": "github" "type": "github"
} }
}, },
"treefmt-nix": {
"inputs": {
"nixpkgs": [
"navi",
"nixos-anywhere",
"nixpkgs"
]
},
"locked": {
"lastModified": 1769691507,
"narHash": "sha256-8aAYwyVzSSwIhP2glDhw/G0i5+wOrren3v6WmxkVonM=",
"owner": "numtide",
"repo": "treefmt-nix",
"rev": "28b19c5844cc6e2257801d43f2772a4b4c050a1b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "treefmt-nix",
"type": "github"
}
},
"wallpapers": { "wallpapers": {
"flake": false, "flake": false,
"locked": { "locked": {
@@ -734,14 +1021,14 @@
}, },
"wrapper-modules": { "wrapper-modules": {
"inputs": { "inputs": {
"nixpkgs": "nixpkgs_2" "nixpkgs": "nixpkgs_3"
}, },
"locked": { "locked": {
"lastModified": 1779297405, "lastModified": 1780661205,
"narHash": "sha256-VFoBwH7ZjVxCnvZTb5ODRXt70sLtWMxstive0N+RS50=", "narHash": "sha256-3F5DixT3Gk91lBI9E+TGMm0ko5HrRbDiL23di16TJGA=",
"owner": "BirdeeHub", "owner": "BirdeeHub",
"repo": "nix-wrapper-modules", "repo": "nix-wrapper-modules",
"rev": "e7ed7a1205945befdf2e0d73ba7df91d935e5af1", "rev": "8dd304c3582ddd339217e1cc5fb53f50acb63c2d",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -758,11 +1045,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1779455631, "lastModified": 1780567926,
"narHash": "sha256-svU6Ro4xiMxMA1KJGwQ/nfKwz3yXE/SONCw2Z1qTXHA=", "narHash": "sha256-LVaiAnBwgr2YotaIlrcwCgmbwHsE2ccegRztLjur/d4=",
"owner": "0xc000022070", "owner": "0xc000022070",
"repo": "zen-browser-flake", "repo": "zen-browser-flake",
"rev": "5bcdfcef664bf62831dcb4b947004d9c5fbf7201", "rev": "eea9ae34eb9011aee9b8ce8ee2bc2dd111ee8285",
"type": "github" "type": "github"
}, },
"original": { "original": {
+3 -1
View File
@@ -100,9 +100,11 @@
# DDRM browser-extension backend # DDRM browser-extension backend
ddrm = { ddrm = {
url = "git+https://git.chiasson.cloud/Olivier/DDRM"; url = "git+https://git.chiasson.cloud/Olivier/DDRM?rev=c3d014f96855e45b53f7391ce19729493887cb96";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
}; };
navi.url = "github:cafkafk/navi";
}; };
outputs = inputs: outputs = inputs:
+149
View File
@@ -0,0 +1,149 @@
{
inputs,
self,
lib,
...
}:
let
ssh = self.lib.sshInventory;
# Remote deploy identity (see `nixosModules.systemDeployBuilder`).
defaultTargetUser = _: "builder";
hostSpecs = {
"14900k" = {
configuration = self.nixosModules."14900kConfiguration";
system = "x86_64-linux";
specialArgs = {
inherit self inputs;
host = "14900k";
system = "x86_64-linux";
};
};
ideapad = {
configuration = self.nixosModules.ideapadConfiguration;
system = "aarch64-linux";
specialArgs = {
inherit self inputs;
host = "ideapad";
system = "aarch64-linux";
};
};
t2mbp = {
configuration = self.nixosModules.t2mbpConfiguration;
system = "x86_64-linux";
specialArgs = {
inherit self inputs;
host = "t2mbp";
system = "x86_64-linux";
};
};
uConsole = {
modules = [
inputs.nixos-raspberrypi.nixosModules.raspberry-pi-5.base
inputs.oom-hardware.nixosModules.uc.kernel
inputs.oom-hardware.nixosModules.uc.configtxt
inputs.oom-hardware.nixosModules.uc.base-cm5
self.nixosModules.uConsoleConfiguration
];
system = "aarch64-linux";
specialArgs = inputs // {
inherit self;
inputs = inputs;
host = "uConsole";
system = "aarch64-linux";
};
};
nix-server = {
configuration = self.nixosModules.nix-serverConfiguration;
system = "x86_64-linux";
specialArgs = {
inherit self inputs;
host = "nix-server";
system = "x86_64-linux";
};
};
r5500 = {
configuration = self.nixosModules.r5500Configuration;
system = "x86_64-linux";
specialArgs = {
inherit self inputs;
host = "r5500";
system = "x86_64-linux";
};
};
};
deployments = lib.mapAttrs (
name: entry:
{
targetHost = entry.hostName;
targetUser = defaultTargetUser name;
tags =
[ name ]
++ lib.optionals (name == "nix-server") [ "server" ]
++ lib.optionals (lib.elem name [
"ideapad"
"uConsole"
]) [ "aarch64" ];
}
// lib.optionalAttrs (name == "14900k") {
allowLocalDeployment = true;
}
// lib.optionalAttrs (name == "nix-server") {
targetPort = 22;
}
) ssh.activeHosts;
metaNixpkgs = import inputs.nixpkgs {
system = "x86_64-linux";
};
in
{
flake.navi = self.lib.mkNaviHiveConfig {
inherit metaNixpkgs hostSpecs deployments;
};
flake.naviHive = inputs.navi.lib.makeHive self.outputs.navi;
perSystem =
{
pkgs,
system,
...
}:
lib.optionalAttrs (lib.elem system [
"x86_64-linux"
"aarch64-linux"
]) {
devShells.default = pkgs.mkShell {
packages = [ inputs.navi.packages.${system}.default ];
shellHook = ''
echo "Navi fleet deploy (from repo root):"
echo " navi apply --on <host> # build + switch one host"
echo " navi apply-local --node 14900k --sudo # switch this machine locally (needs root), --node if hostname differs"
echo " navi tui # interactive fleet dashboard"
'';
};
apps = {
navi = {
type = "app";
program = lib.getExe inputs.navi.packages.${system}.default;
};
navi-tui = {
type = "app";
program =
toString (
pkgs.writeShellApplication {
name = "navi-tui";
runtimeInputs = [ inputs.navi.packages.${system}.default ];
text = ''
exec navi tui "$@"
'';
}
);
};
};
};
}
@@ -149,7 +149,7 @@ in {
# Core features # Core features
enableSystemMonitoring = true; # System monitoring widgets (dgop) enableSystemMonitoring = true; # System monitoring widgets (dgop)
dgop.package = inputs.dgop.packages.${pkgs.system}.default; dgop.package = inputs.dgop.packages.${pkgs.stdenv.hostPlatform.system}.default;
enableVPN = true; # VPN management widget enableVPN = true; # VPN management widget
enableDynamicTheming = true; # Wallpaper-based theming (matugen) enableDynamicTheming = true; # Wallpaper-based theming (matugen)
enableAudioWavelength = true; # Audio visualizer (cava) enableAudioWavelength = true; # Audio visualizer (cava)
@@ -355,7 +355,7 @@ in {
syncModeWithPortal = true; syncModeWithPortal = true;
terminalsAlwaysDark = true; terminalsAlwaysDark = true;
showDock = true; showDock = false;
dockAutoHide = true; dockAutoHide = true;
dockGroupByApp = true; dockGroupByApp = true;
dockOpenOnOverview = false; dockOpenOnOverview = false;
@@ -3,7 +3,7 @@
"name": "Bitwarden (rbw) lock", "name": "Bitwarden (rbw) lock",
"description": "Bar control for rbw vault: locked/unlocked padlock; click to unlock or lock", "description": "Bar control for rbw vault: locked/unlocked padlock; click to unlock or lock",
"version": "1.0.0", "version": "1.0.0",
"author": "NixOS-V2", "author": "chiasson.cloud",
"type": "widget", "type": "widget",
"capabilities": ["dankbar-widget"], "capabilities": ["dankbar-widget"],
"component": "./RbwLockToggle.qml", "component": "./RbwLockToggle.qml",
@@ -3,7 +3,7 @@
"name": "Virtual Keyboard Toggle", "name": "Virtual Keyboard Toggle",
"description": "Bar button to show/hide the wvkbd on-screen keyboard (for touch/tablet)", "description": "Bar button to show/hide the wvkbd on-screen keyboard (for touch/tablet)",
"version": "1.0.0", "version": "1.0.0",
"author": "NixOS-V2", "author": "chiasson.cloud",
"type": "widget", "type": "widget",
"capabilities": ["dankbar-widget"], "capabilities": ["dankbar-widget"],
"component": "./WvkbdToggle.qml", "component": "./WvkbdToggle.qml",
+4 -4
View File
@@ -7,7 +7,7 @@
guiEnabled = d.hyprland.enable || d.niri.enable || d.plasma.enable; guiEnabled = d.hyprland.enable || d.niri.enable || d.plasma.enable;
wallpaperDir = pkgs.stdenvNoCC.mkDerivation { wallpaperDir = pkgs.stdenvNoCC.mkDerivation {
pname = "nixos-v2-wallpapers"; pname = "chiasson-nix-wallpapers";
version = "0.1"; version = "0.1";
src = builtins.path { src = builtins.path {
path = cfg.source; path = cfg.source;
@@ -26,7 +26,7 @@
{ {
options.chiasson.desktop.wallpapers = { options.chiasson.desktop.wallpapers = {
enable = lib.mkEnableOption '' enable = lib.mkEnableOption ''
Copy `source` into the store; sets `NIXOS_V2_WALLPAPERS` and `/etc/wallpapers`. Copy `source` into the store; sets `CHIASSON_NIX_WALLPAPERS` and `/etc/wallpapers`.
'' // { '' // {
default = true; default = true;
}; };
@@ -43,8 +43,8 @@
config = lib.mkIf (guiEnabled && cfg.enable) { config = lib.mkIf (guiEnabled && cfg.enable) {
environment = { environment = {
variables.NIXOS_V2_WALLPAPERS = installPath; variables.CHIASSON_NIX_WALLPAPERS = installPath;
sessionVariables.NIXOS_V2_WALLPAPERS = installPath; sessionVariables.CHIASSON_NIX_WALLPAPERS = installPath;
etc."wallpapers".source = installPath; etc."wallpapers".source = installPath;
}; };
}; };
@@ -1,4 +1,4 @@
# NFS exports from nixdesk (14900k) to nix-server (192.168.2.238): # NFS exports from nixdesk (14900k) to r5500 (192.168.2.100), formerly nix-server (192.168.2.238):
# - /mnt/deep/jellyfin → nix-server /mnt/nixdesk-jellyfin (Jellyfin bulk libraries) # - /mnt/deep/jellyfin → nix-server /mnt/nixdesk-jellyfin (Jellyfin bulk libraries)
# #
# Jellyfin root on nixdesk uses owner olivier + group nfsmedia (990); dirs here are 2775 so # Jellyfin root on nixdesk uses owner olivier + group nfsmedia (990); dirs here are 2775 so
@@ -48,7 +48,8 @@ in
# Squash nix-server clients to olivier:nfsmedia so Jellyfin can write .nfo/posters into # Squash nix-server clients to olivier:nfsmedia so Jellyfin can write .nfo/posters into
# existing olivier-owned library folders (990-only squash was "other" r-x on typical 755 trees). # existing olivier-owned library folders (990-only squash was "other" r-x on typical 755 trees).
exports = '' exports = ''
/mnt/deep/jellyfin 192.168.2.238(rw,sync,no_subtree_check,crossmnt,root_squash,all_squash,anonuid=${toString olivierUid},anongid=990,fsid=1) /mnt/deep/jellyfin 192.168.2.100(rw,sync,no_subtree_check,crossmnt,root_squash,all_squash,anonuid=${toString olivierUid},anongid=990,fsid=1)
/mnt/deep/jellyfin 192.168.2.238(rw,sync,no_subtree_check,crossmnt,root_squash,all_squash,anonuid=${toString olivierUid},anongid=990,fsid=2)
''; '';
}; };
@@ -0,0 +1,14 @@
# Moonfin 2.0.0 Jellyfin client — upstream Flatpak bundle (not on Flathub yet).
# https://github.com/Moonfin-Client/Moonfin-Core/releases/tag/2.0.0
{ pkgs, ... }:
{
chiasson.system.flatpak.bundles = [
{
appId = "org.moonfin.linux";
bundle = pkgs.fetchurl {
url = "https://github.com/Moonfin-Client/Moonfin-Core/releases/download/2.0.0/Moonfin_Linux_v2.0.0.flatpak";
hash = "sha256-sLtrsqBaJ1wriTkIdLylqMc9ygNkHrNm4YS/816nIFQ=";
};
}
];
}
+7 -2
View File
@@ -19,6 +19,7 @@
./_private/displays.nix ./_private/displays.nix
./_private/media-disk.nix ./_private/media-disk.nix
./_private/jellyfin-nfs-export.nix ./_private/jellyfin-nfs-export.nix
./_private/moonfin-flatpak.nix
]; ];
sops = { sops = {
@@ -127,6 +128,8 @@ services.cloudflare-warp.enable = true;
prismlauncher prismlauncher
dualsensectl dualsensectl
devenv
]; ];
@@ -161,8 +164,10 @@ services.cloudflare-warp.enable = true;
{ {
programs.git = { programs.git = {
enable = true; enable = true;
userName = "OlivierChiasson"; settings.user = {
userEmail = "olivierchiasson@hotmail.fr"; name = "OlivierChiasson";
email = "olivierchiasson@hotmail.fr";
};
}; };
chiasson.home = { chiasson.home = {
+6 -1
View File
@@ -1,7 +1,12 @@
{ self, ... }: { { self, ... }: {
#TODO[epic=Moderate] Move this somewhere else, would prefer not relying on this module #TODO[epic=Moderate] Move this somewhere else, would prefer not relying on this module
flake.nixosModules."client-services" = { ... }: { flake.nixosModules."client-services" = { ... }: {
imports = [ self.nixosModules.systemBluetooth ]; imports = [
self.nixosModules.systemBluetooth
self.nixosModules.systemDeployBuilder
];
chiasson.system.deploy.builder.enable = true;
# Lab-ish SSH defaults on clients — tighten for anything exposed. # Lab-ish SSH defaults on clients — tighten for anything exposed.
services.openssh = { services.openssh = {
+2 -2
View File
@@ -105,7 +105,7 @@
# Per-session tablet-mode / autorotate daemons live in `_private/touch-tablet.nix`. # Per-session tablet-mode / autorotate daemons live in `_private/touch-tablet.nix`.
chiasson.desktop = { chiasson.desktop = {
niri.enable = true; niri.enable = true;
hyprland.enable = true; hyprland.enable = false;
defaultSession = "niri"; defaultSession = "niri";
shell = "dms"; shell = "dms";
@@ -120,7 +120,7 @@
"-lc" "-lc"
'' ''
ssh -t olivier@nixdesk \ ssh -t olivier@nixdesk \
"nixos-rebuild switch --flake path:/home/olivier/NixOS-V2#ideapad --target-host olivier@ideapad --sudo --ask-sudo-password 2>&1" "nixos-rebuild switch --flake path:/home/olivier/chiasson-nix#ideapad --target-host olivier@ideapad --sudo --ask-sudo-password 2>&1"
'' ''
]; ];
}; };
@@ -9,7 +9,9 @@
{ {
systemd.tmpfiles.settings."nix-server-dispatcharr-data" = { systemd.tmpfiles.settings."nix-server-dispatcharr-data" = {
"/var/lib/dispatcharr"."d" = { "/var/lib/dispatcharr"."d" = {
mode = "0755"; # Dispatcharr runs as a non-root user inside the container; keep /data writable.
# (Plugins like EPG Janitor write to `/data/*.json`.)
mode = "0777";
user = "root"; user = "root";
group = "root"; group = "root";
}; };
+3 -10
View File
@@ -10,26 +10,17 @@
{ {
imports = [ imports = [
inputs.ddrm.nixosModules.default inputs.ddrm.nixosModules.default
self.nixosModules.systemDeployBuilder
self.nixosModules.nix-serverHardware self.nixosModules.nix-serverHardware
inputs.sops-nix.nixosModules.sops inputs.sops-nix.nixosModules.sops
self.nixosModules.system self.nixosModules.system
self.nixosModules.users self.nixosModules.users
./_services/attic-cache-server.nix ./_services/attic-cache-server.nix
./_services/portainer.nix ./_services/portainer.nix
./_services/organizr.nix
./_services/swiftshare.nix ./_services/swiftshare.nix
./_services/personal-website.nix ./_services/personal-website.nix
./_services/immich.nix ./_services/immich.nix
./_services/jellyfin.nix
./_services/nixdesk-nfs-client.nix
./_services/ddrm-media-server.nix ./_services/ddrm-media-server.nix
./_services/sonarr.nix
./_services/prowlarr.nix
./_services/flaresolverr.nix
./_services/radarr.nix
./_services/qbittorrent.nix
./_services/seerr.nix
./_services/dispatcharr.nix
./_services/gitea.nix ./_services/gitea.nix
./_services/cloudflare-dyndns.nix ./_services/cloudflare-dyndns.nix
]; ];
@@ -83,6 +74,8 @@
extraPackages = with pkgs; [ btop ]; extraPackages = with pkgs; [ btop ];
}; };
chiasson.system.deploy.builder.enable = true;
chiasson.users = { chiasson.users = {
enabled = [ "server" ]; enabled = [ "server" ];
hostOverrides.server = { hostOverrides.server = {
@@ -0,0 +1,52 @@
# Media stack storage on r5500: btrfs subvolume @media-stack on the OS disk (sda4).
{ config, pkgs, lib, ... }:
let
btrfsUuid = "934a5ec3-4bab-49c3-96c9-c857c50076ba";
btrfsDevice = "/dev/disk/by-uuid/${btrfsUuid}";
# Created under subvol=@ → full path is @/@media-stack (not a top-level @media-stack).
mediaSubvol = "@/@media-stack";
in
{
# Create @media-stack before /mnt/media-stack is mounted.
systemd.services.r5500-media-stack-subvolume = {
description = "Create btrfs subvolume @media-stack on sda4 if missing";
before = [ "mnt-media\\x2dmedia\\x2dstack.mount" ];
wantedBy = [ "local-fs-pre.target" ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
};
script = ''
rootMount="/mnt/.r5500-btrfs-root"
mkdir -p "$rootMount"
if ! mountpoint -q "$rootMount"; then
mount -o subvol=@ "${btrfsDevice}" "$rootMount"
umountAfter=1
else
umountAfter=0
fi
if ! ${pkgs.btrfs-progs}/bin/btrfs subvolume show "$rootMount/@media-stack" >/dev/null 2>&1; then
${pkgs.btrfs-progs}/bin/btrfs subvolume create "$rootMount/@media-stack"
fi
if [ "$umountAfter" -eq 1 ]; then
umount "$rootMount"
fi
'';
};
# Optional 1 TiB cap (run once after first boot if desired):
# sudo btrfs quota enable /mnt/media-stack && sudo btrfs qgroup limit 1T /mnt/media-stack
fileSystems."/mnt/media-stack" = {
device = btrfsDevice;
fsType = "btrfs";
neededForBoot = false;
options = [
"subvol=${mediaSubvol}"
"compress=zstd"
"noatime"
"nofail"
"x-systemd.device-timeout=30"
];
};
}
@@ -0,0 +1,70 @@
# Shared media group, directory layout, and Jellyfin config bind-mount on /mnt/media-stack.
{ config, lib, pkgs, ... }:
let
paths = import ./media-stack-paths.nix;
prowlarrCustomIndexer = "${./../_services/prowlarr/torrent9-custom.yml}";
inherit (paths)
mediaRoot
downloadsDir
downloadsIncompleteDir
sonarrDataDir
radarrDataDir
prowlarrDataDir
qbittorrentDataDir
seerrConfigDir
dispatcharrDataDir
organizrDataDir
jellyfinConfigDir
jellyfinMoviesDir
jellyfinTvDir
;
in
{
_module.args.mediaStackPaths = paths;
users.groups.media = { };
users.users.server.extraGroups = [ "media" ];
# Layout dirs only after /mnt/media-stack is mounted (tmpfiles at early boot would
# otherwise create paths on the root fs and break bind mounts / boot).
systemd.services.r5500-media-stack-dirs = {
description = "Create media-stack directory layout";
after = [ "mnt-media\\x2dmedia\\x2dstack.mount" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
};
script = ''
set -e
install -d -m 0775 -o root -g media ${mediaRoot}
install -d -m 2775 -o root -g media ${jellyfinMoviesDir} ${jellyfinTvDir} ${downloadsDir}
install -d -m 2775 -o root -g media ${downloadsIncompleteDir}
install -d -m 0755 -o jellyfin -g jellyfin ${jellyfinConfigDir}
install -d -m 0700 -o sonarr -g sonarr ${sonarrDataDir}
install -d -m 0700 -o radarr -g radarr ${radarrDataDir}
install -d -m 0700 -o prowlarr -g prowlarr ${prowlarrDataDir}
install -d -m 0750 -o qbittorrent -g qbittorrent ${qbittorrentDataDir}
install -d -m 0755 -o jellyseerr -g jellyseerr ${seerrConfigDir} 2>/dev/null \
|| install -d -m 0755 -o root -g root ${seerrConfigDir}
install -d -m 0777 -o root -g root ${dispatcharrDataDir}
install -d -m 0755 -o organizr -g organizr ${organizrDataDir} 2>/dev/null \
|| install -d -m 0755 -o root -g root ${organizrDataDir}
install -d -m 0700 -o prowlarr -g prowlarr ${prowlarrDataDir}/Definitions/Custom
ln -sfn ${prowlarrCustomIndexer} ${prowlarrDataDir}/Definitions/Custom/torrent9-custom.yml
'';
};
# Jellyfin metadata on the media subvolume; nofail so a missing subvol never bricks boot.
fileSystems."/var/lib/jellyfin" = {
device = jellyfinConfigDir;
fsType = "none";
neededForBoot = false;
options = [
"bind"
"nofail"
"x-systemd.after=mnt-media\\x2dmedia\\x2dstack.mount"
];
};
}
@@ -0,0 +1,16 @@
# Path constants for the r5500 media stack (imported by _services/* and media-paths.nix).
{
mediaRoot = "/mnt/media-stack";
downloadsDir = "/mnt/media-stack/downloads";
downloadsIncompleteDir = "/mnt/media-stack/downloads/incomplete";
sonarrDataDir = "/mnt/media-stack/sonarr";
radarrDataDir = "/mnt/media-stack/radarr";
prowlarrDataDir = "/mnt/media-stack/prowlarr";
qbittorrentDataDir = "/mnt/media-stack/qbittorrent";
seerrConfigDir = "/mnt/media-stack/seerr";
dispatcharrDataDir = "/mnt/media-stack/dispatcharr";
organizrDataDir = "/mnt/media-stack/organizr";
jellyfinConfigDir = "/mnt/media-stack/jellyfin/config";
jellyfinMoviesDir = "/mnt/media-stack/jellyfin/movies";
jellyfinTvDir = "/mnt/media-stack/jellyfin/tv";
}
@@ -0,0 +1,34 @@
{ lib, pkgs, mediaStackPaths, ... }:
let
dataDir = mediaStackPaths.dispatcharrDataDir;
in
{
systemd.tmpfiles.settings."r5500-dispatcharr-data" = {
"${dataDir}"."d" = {
mode = "0777";
user = "root";
group = "root";
};
};
systemd.services.docker-dispatcharr.preStart = lib.mkBefore ''
${pkgs.coreutils}/bin/mkdir -p ${dataDir}
'';
virtualisation.oci-containers.containers.dispatcharr = {
image = "ghcr.io/dispatcharr/dispatcharr:latest";
ports = [ "9191:9191" ];
volumes = [
"${dataDir}:/data"
];
environment = {
DISPATCHARR_ENV = "aio";
REDIS_HOST = "localhost";
CELERY_BROKER_URL = "redis://localhost:6379/0";
DISPATCHARR_LOG_LEVEL = "info";
TZ = "America/Moncton";
};
};
networking.firewall.allowedTCPPorts = [ 9191 ];
}
@@ -0,0 +1,6 @@
# Docker backend for Dispatcharr and Organizr on r5500.
{ ... }:
{
virtualisation.docker.enable = true;
virtualisation.oci-containers.backend = "docker";
}
@@ -0,0 +1,5 @@
{ ... }:
{
services.flaresolverr.enable = true;
networking.firewall.allowedTCPPorts = [ 8191 ];
}
@@ -0,0 +1,29 @@
# Jellyfin on r5500. Local libraries: /mnt/media-stack/jellyfin/{movies,tv}.
# Bulk libraries: /mnt/nixdesk-jellyfin/{movies,tv} (NFS from 14900k).
{ lib, ... }:
{
nixpkgs.overlays = [
(final: prev: {
jellyfin-web = prev.jellyfin-web.overrideAttrs (oldAttrs: {
postInstall =
(oldAttrs.postInstall or "")
+ ''
find "$out" -type f \( -name 'banner-light.*.png' -o -name 'banner-dark.*.png' \) \
-exec truncate -s 0 {} \;
'';
});
})
];
users.users.jellyfin.extraGroups = [ "media" ];
services.jellyfin = {
enable = true;
openFirewall = true;
};
systemd.services.jellyfin.serviceConfig = {
SupplementaryGroups = [ "media" ];
PrivateUsers = lib.mkForce false;
};
}
@@ -0,0 +1,28 @@
# NFS mounts of nixdesk (14900k) bulk storage for r5500. Exports live in
# modules/hosts/14900k/_private/jellyfin-nfs-export.nix
#
# Jellyfin library paths:
# Movies → /mnt/nixdesk-jellyfin/movies
# Shows → /mnt/nixdesk-jellyfin/tv
{ ... }:
let
nfsExportHost = "192.168.2.25";
nfsClientOpts = [
"rw"
"noatime"
"nofail"
"_netdev"
"nfsvers=3"
"tcp"
"lookupcache=none"
"x-systemd.automount"
"x-systemd.idle-timeout=3600"
];
in
{
fileSystems."/mnt/nixdesk-jellyfin" = {
device = "${nfsExportHost}:/mnt/deep/jellyfin";
fsType = "nfs";
options = nfsClientOpts;
};
}
@@ -0,0 +1,48 @@
{ lib, pkgs, mediaStackPaths, ... }:
let
configDir = mediaStackPaths.organizrDataDir;
in
{
users.groups.organizr = { gid = 950; };
users.users.organizr = {
isSystemUser = true;
uid = 950;
group = "organizr";
};
systemd.tmpfiles.settings."r5500-organizr-config" = {
"${configDir}"."d" = {
mode = "0755";
user = "organizr";
group = "organizr";
};
};
systemd.tmpfiles.settings."r5500-organizr-config-perms" = {
"${configDir}"."Z" = {
mode = "0755";
user = "organizr";
group = "organizr";
};
};
systemd.services.docker-organizr.preStart = lib.mkBefore ''
${pkgs.coreutils}/bin/mkdir -p ${configDir}
${pkgs.coreutils}/bin/chown -R organizr:organizr ${configDir}
'';
virtualisation.oci-containers.containers.organizr = {
image = "ghcr.io/organizr/organizr:latest";
ports = [ "8888:80" ];
volumes = [
"${configDir}:/config"
];
environment = {
PUID = "950";
PGID = "950";
TZ = "America/Moncton";
};
};
networking.firewall.allowedTCPPorts = [ 8888 ];
}
@@ -0,0 +1,20 @@
{ lib, mediaStackPaths, ... }:
{
services.prowlarr = {
enable = true;
dataDir = mediaStackPaths.prowlarrDataDir;
};
users.groups.prowlarr = { };
users.users.prowlarr = {
isSystemUser = true;
group = "prowlarr";
extraGroups = [ "media" ];
};
systemd.services.prowlarr.serviceConfig.ReadWritePaths = lib.mkAfter [
mediaStackPaths.prowlarrDataDir
];
networking.firewall.allowedTCPPorts = [ 9696 ];
}
@@ -0,0 +1,193 @@
---
id: torrent9-custom
name: Torrent9 (Custom URL)
description: "Torrent9 is a FRENCH Public site for MOVIES / TV / GENERAL"
language: fr-FR
type: public
encoding: UTF-8
followredirect: true
testlinktorrent: false
links:
- https://www.torrent9.club/
- https://www6.torrent9.to/
legacylinks:
- https://www.torrent9.pl/ # this is a proxy for torrent9clone
- https://torrent9.black-mirror.xyz/ # this is a proxy for torrent9clone
- https://torrent9.unblocked.casa/ # this is a proxy for torrent9clone
- https://torrent9.proxyportal.fun/ # this is a proxy for torrent9clone
- https://torrent9.uk-unblock.xyz/ # this is a proxy for torrent9clone
- https://torrent9.ind-unblock.xyz/ # this is a proxy for torrent9clone
- https://ww1.torrent9.to/
- https://www.torrent9.is/
- https://torrent9.li/ # not a proxy for torrent9 or torrent9clone
- https://www.oxtorrent.me/
- https://www.torrent9.gg/
- https://www.torrent9.fi/ # this is the torrent9clone domain
- https://www.torrent9.fm/
- https://torrent9.se/ # redirect to www.
- https://torrent9.ninjaproxy1.com/ # no response data
- https://torrent9.proxyninja.org/ # Error 1007
- https://www.torrent9.se/
- https://torrent9.unblockninja.com/ # 403 forbidden
- https://ww1.torrent9.fm/
- https://www.torrent9.zone/ # clone? details links are broken
- https://torrent9.to/
- https://ww2.torrent9.to/
- https://www5.torrent9.to/
caps:
# dont forget to update the search fields category case block
categorymappings:
- {id: films, cat: Movies, desc: "Movies"}
- {id: series, cat: TV, desc: "TV"}
- {id: musique, cat: Audio, desc: "Music"}
- {id: ebook, cat: Books, desc: "Books"}
- {id: logiciels, cat: PC, desc: "Software"}
- {id: jeux-pc, cat: PC/Games, desc: "PC Games"}
- {id: other, cat: Other, desc: "Other"} # dummy cat for results missing icon
modes:
search: [q]
tv-search: [q, season, ep]
movie-search: [q]
music-search: [q]
book-search: [q]
allowrawsearch: true
settings:
- name: info_flaresolverr
type: info_flaresolverr
- name: multilang
type: checkbox
label: Replace MULTi by another language in release name
default: false
- name: multilanguage
type: select
label: Replace MULTi by this language
default: FRENCH
options:
FRENCH: FRENCH
MULTi FRENCH: MULTi FRENCH
ENGLISH: ENGLISH
MULTi ENGLISH: MULTi ENGLISH
VOSTFR: VOSTFR
MULTi VOSTFR: MULTi VOSTFR
- name: vostfr
type: checkbox
label: Replace VOSTFR and SUBFRENCH with ENGLISH
default: false
- name: sort
type: select
label: Sort requested from site (Only works for searches with Keywords)
default: ".html"
options:
".html": best
".html,trie-date-d": created desc
".html,trie-date-a": created asc
".html,trie-seeds-d": seeders desc
".html,trie-seeds-a": seeders asc
".html,trie-poid-d": size desc
".html,trie-poid-a": size asc
".html,trie-nom-d": title desc
".html,trie-nom-a": title asc
download:
selectors:
- selector: a[href^="magnet:?"]
attribute: href
- selector: script:contains("magnet:?")
filters:
- name: regexp
args: "\\s'(magnet:\\?.+?)';"
search:
paths:
- path: "{{ if .Keywords }}search_torrent/{{ .Keywords }}{{ .Config.sort }}{{ else }}home/{{ end }}"
keywordsfilters:
# if searching for season packs with S01 to saison 1 #9712
- name: re_replace
args: ["(?i)(S0)(\\d{1,2})$", "saison $2"]
- name: re_replace
args: ["(?i)(S)(\\d{1,3})$", "saison $2"]
- name: replace
args: [" ", "-"]
headers:
# site blocks all Linux User-Agents, so use a slightly altered Windows Jackett UA here (e.g. Safari/537.36 > Safari/537.35)
User-Agent: ["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.35"]
rows:
selector: table.table-striped > tbody > tr
filters:
- name: andmatch
fields:
category_optional:
selector: td:nth-child(1) i
optional: true
case:
i[class="fa fa-video-camera"]: films
i[class="fa fa-tv"]: series # search by name
i[class="fa fa-desktop"]: series # keywordless search
i[class="fa fa-music"]: musique
i[class="fa fa-gamepad"]: jeux-pc
i[class="fa fa-laptop"]: logiciels
i[class="fa fa-book"]: ebook
category:
text: "{{ if .Result.category_optional }}{{ .Result.category_optional }}{{ else }}other{{ end }}"
title_default:
selector: td:nth-child(1) a
title_optional:
selector: td:nth-child(1) a[title]
attribute: title
optional: true
title_phase1:
text: "{{ if .Result.title_optional }}{{ .Result.title_optional }}{{ else }}{{ .Result.title_default }}{{ end }}"
filters:
- name: re_replace
args: ["(?i)\\b(FRENCH|MULTI|TRUEFRENCH|VOSTFR|SUBFRENCH)\\b(.+?)(\\b((19|20)\\d{2})\\b)$", "$3 $1$2"]
title_vostfr:
text: "{{ .Result.title_phase1 }}"
filters:
- name: re_replace
args: ["(?i)\\b(vostfr|subfrench)\\b", "ENGLISH"]
title_phase2:
text: "{{ if .Config.vostfr }}{{ .Result.title_vostfr }}{{ else }}{{ .Result.title_phase1 }}{{ end }}"
title_multilang:
text: "{{ .Result.title_phase2 }}"
filters:
- name: re_replace
args: ["(?i)\\b(MULTI(?!.*(?:FRENCH|ENGLISH|VOSTFR)))\\b", "{{ .Config.multilanguage }}"]
title:
text: "{{ if .Config.multilang }}{{ .Result.title_multilang }}{{ else }}{{ .Result.title_phase2 }}{{ end }}"
details:
selector: td:nth-child(1) a
attribute: href
download:
selector: td:nth-child(1) a
attribute: href
date:
selector: td:nth-child(2):contains("/")
optional: true
default: now
filters:
- name: dateparse
args: "dd/MM/yyyy"
size:
selector: "{{ if .Keywords }}td:nth-child(3){{ else }}td:nth-child(2){{ end }}"
filters:
- name: re_replace
args: ["(\\w)o", "$1B"]
seeders:
selector: "{{ if .Keywords }}td:nth-child(4){{ else }}td:nth-child(3){{ end }}"
optional: true
default: 0
leechers:
selector: "{{ if .Keywords }}td:nth-child(5){{ else }}td:nth-child(4){{ end }}"
optional: true
default: 0
downloadvolumefactor:
text: 0
uploadvolumefactor:
text: 1
# engine n/a
@@ -0,0 +1,43 @@
{ lib, mediaStackPaths, ... }:
let
webPort = 8081;
btPort = 51413;
inherit (mediaStackPaths) downloadsDir downloadsIncompleteDir qbittorrentDataDir;
in
{
services.qbittorrent = {
enable = true;
openFirewall = true;
webuiPort = webPort;
torrentingPort = btPort;
};
users.groups.qbittorrent = { };
users.users.qbittorrent = {
isSystemUser = true;
group = "qbittorrent";
extraGroups = [ "media" ];
home = qbittorrentDataDir;
};
fileSystems."/var/lib/qbittorrent" = {
device = qbittorrentDataDir;
fsType = "none";
neededForBoot = false;
options = [
"bind"
"nofail"
"x-systemd.after=mnt-media\\x2dmedia\\x2dstack.mount"
];
};
networking.firewall.allowedTCPPorts = [ webPort btPort ];
networking.firewall.allowedUDPPorts = [ btPort ];
# Default save path for new torrents (existing qBittorrent.conf may override after migration).
systemd.services.qbittorrent.preStart = lib.mkAfter ''
mkdir -p ${downloadsDir} ${downloadsIncompleteDir}
chmod 2775 ${downloadsDir} ${downloadsIncompleteDir}
chgrp media ${downloadsDir} ${downloadsIncompleteDir}
'';
}
+20
View File
@@ -0,0 +1,20 @@
{ lib, mediaStackPaths, ... }:
{
services.radarr = {
enable = true;
dataDir = mediaStackPaths.radarrDataDir;
};
users.groups.radarr = { };
users.users.radarr = {
isSystemUser = true;
group = "radarr";
extraGroups = [ "media" ];
};
systemd.services.radarr.serviceConfig.ReadWritePaths = lib.mkAfter [
mediaStackPaths.radarrDataDir
];
networking.firewall.allowedTCPPorts = [ 7878 ];
}
+35
View File
@@ -0,0 +1,35 @@
{ lib, mediaStackPaths, ... }:
{
nixpkgs.overlays = [
(final: prev: {
seerr = prev.seerr.overrideAttrs (oldAttrs: {
postInstall =
(oldAttrs.postInstall or "")
+ ''
find "$out/share/public" -maxdepth 1 -type f \( -name 'logo_full.svg' -o -name 'logo_stacked.svg' \) \
-exec truncate -s 0 {} \;
'';
});
})
];
services.seerr = {
enable = true;
openFirewall = true;
configDir = mediaStackPaths.seerrConfigDir;
};
users.groups.jellyseerr = { };
users.users.jellyseerr = {
isSystemUser = true;
group = "jellyseerr";
};
systemd.services.seerr.serviceConfig = {
DynamicUser = lib.mkForce false;
User = "jellyseerr";
Group = "jellyseerr";
ReadWritePaths = [ mediaStackPaths.seerrConfigDir ];
};
}
+20
View File
@@ -0,0 +1,20 @@
{ lib, mediaStackPaths, ... }:
{
services.sonarr = {
enable = true;
dataDir = mediaStackPaths.sonarrDataDir;
};
users.groups.sonarr = { };
users.users.sonarr = {
isSystemUser = true;
group = "sonarr";
extraGroups = [ "media" ];
};
systemd.services.sonarr.serviceConfig.ReadWritePaths = lib.mkAfter [
mediaStackPaths.sonarrDataDir
];
networking.firewall.allowedTCPPorts = [ 8989 ];
}
+16
View File
@@ -9,10 +9,24 @@
}: }:
{ {
imports = [ imports = [
self.nixosModules.systemDeployBuilder
self.nixosModules.r5500Hardware self.nixosModules.r5500Hardware
inputs.sops-nix.nixosModules.sops inputs.sops-nix.nixosModules.sops
self.nixosModules.system self.nixosModules.system
self.nixosModules.users self.nixosModules.users
./_private/media-disk.nix
./_private/media-paths.nix
./_services/docker-media.nix
./_services/nixdesk-nfs-client.nix
./_services/jellyfin.nix
./_services/sonarr.nix
./_services/radarr.nix
./_services/prowlarr.nix
./_services/flaresolverr.nix
./_services/seerr.nix
./_services/qbittorrent.nix
./_services/dispatcharr.nix
./_services/organizr.nix
]; ];
boot.loader.grub = { boot.loader.grub = {
@@ -55,6 +69,8 @@
extraPackages = with pkgs; [ btop git ]; extraPackages = with pkgs; [ btop git ];
}; };
chiasson.system.deploy.builder.enable = true;
chiasson.users = { chiasson.users = {
enabled = [ "server" ]; enabled = [ "server" ];
hostOverrides.server = { hostOverrides.server = {
@@ -49,8 +49,9 @@ let
// (args.argsOverride or { }) // (args.argsOverride or { })
); );
# linux_6_19 was dropped from nixpkgs; t2linux/linux-t2-patches HEAD targets 7.x (see 76589a8).
linuxT2Kernel = (pkgs.callPackage linuxT2GenericFromDir { }) { linuxT2Kernel = (pkgs.callPackage linuxT2GenericFromDir { }) {
kernel = pkgs.linux_6_19; kernel = pkgs.linux_latest;
t2linuxPatchesSrc = inputs.t2linux-patches; t2linuxPatchesSrc = inputs.t2linux-patches;
}; };
in in
@@ -0,0 +1,14 @@
# nixpkgs unstable dropped several Raspberry Pi attrs; nixos-raspberrypi modules still
# reference them but ship derivations under pkgs/raspberrypi/.
{ inputs, ... }: {
nixpkgs.overlays = [
(final: _prev:
let
rp = "${inputs.nixos-raspberrypi}/pkgs/raspberrypi";
in
{
raspberrypi-utils = final.callPackage "${rp}/raspberrypi-utils.nix" { };
raspberrypi-udev-rules = final.callPackage "${rp}/udev-rules.nix" { };
})
];
}
+1
View File
@@ -29,6 +29,7 @@
self.nixosModules."client-services" self.nixosModules."client-services"
./_private/platform.nix ./_private/platform.nix
./_private/raspberrypi-utils-overlay.nix
./_private/wifi-brcmfmac.nix ./_private/wifi-brcmfmac.nix
./_private/services.nix ./_private/services.nix
./_private/cockpit.nix ./_private/cockpit.nix
+33
View File
@@ -0,0 +1,33 @@
# Build a raw Navi hive attrset from host specs + deployment targets.
# Call `inputs.navi.lib.makeHive` on the result to produce `flake.naviHive`.
{ lib, inputs, ... }: {
flake.lib.mkNaviHiveConfig =
{
metaNixpkgs,
hostSpecs,
deployments,
}:
let
deployNodes = lib.filterAttrs (name: _: deployments ? ${name}) hostSpecs;
in
{
meta = {
nixpkgs = metaNixpkgs;
nodeNixpkgs = lib.mapAttrs (
name: spec:
import inputs.nixpkgs {
system = spec.system;
}
) deployNodes;
nodeSpecialArgs = lib.mapAttrs (_: spec: spec.specialArgs) deployNodes;
allowApplyAll = false;
};
}
// lib.mapAttrs (
name: spec:
{
imports = spec.modules or [ spec.configuration ];
deployment = deployments.${name};
}
) deployNodes;
}
+16 -4
View File
@@ -60,10 +60,17 @@
(builtins.attrNames selectedHosts) (builtins.attrNames selectedHosts)
); );
# Must come before inventory `Host` blocks and before `Host *`: LAN Gitea SSH is not a catalog PC, # Gitea git-over-SSH listens on port 222. System SSH (nix deploy, server@…) uses port 22
# and `Host *` sets `IdentityAgent none` — without this, git@nix-server never sees rbw keys. # via the catalog `nix-server` Host block — never list nix-server or 192.168.2.238 here.
giteaSshBlock = identityAgent: '' giteaSshBlock = identityAgent: ''
Host git.chiasson.cloud gitea nix-server 192.168.2.238 Host git.chiasson.cloud gitea
HostName 192.168.2.238
Port 222
User git
IdentityAgent ${identityAgent}
IdentitiesOnly no
Match host nix-server,192.168.2.238 user git
HostName 192.168.2.238 HostName 192.168.2.238
Port 222 Port 222
User git User git
@@ -84,11 +91,16 @@
entry = selectedHosts.${hostName}; entry = selectedHosts.${hostName};
hostPatterns = builtins.concatStringsSep " " (entry.aliases ++ [ entry.hostName ]); hostPatterns = builtins.concatStringsSep " " (entry.aliases ++ [ entry.hostName ]);
userLine = if user == null then "" else " User ${user}\n"; userLine = if user == null then "" else " User ${user}\n";
portLine =
if hostName == "nix-server" then
" Port 22\n"
else
"";
in in
'' ''
Host ${hostPatterns} Host ${hostPatterns}
HostName ${entry.hostName} HostName ${entry.hostName}
${userLine} IdentityFile ~/${mkIdentityFileName hostName} ${userLine}${portLine} IdentityFile ~/${mkIdentityFileName hostName}
IdentityAgent ${identityAgent} IdentityAgent ${identityAgent}
IdentitiesOnly yes IdentitiesOnly yes
'') '')
+4 -20
View File
@@ -7,26 +7,10 @@
let let
cfg = config.chiasson.ssh.inbound; cfg = config.chiasson.ssh.inbound;
inventory = self.lib.sshInventory; inventory = self.lib.sshInventory;
resolveSelection =
selection:
if selection == "all" then
inventory.authorizedKeys
else
let
missing = builtins.filter (name: !(builtins.hasAttr name inventory.hosts)) selection;
in
if missing != [ ] then
throw "ssh.inbound: unknown host keys: ${builtins.concatStringsSep ", " missing}"
else
lib.unique (
builtins.filter (key: key != null) (
builtins.map (hostName: inventory.hosts.${hostName}.publicKey) selection
)
);
in in
{ {
options.chiasson.ssh.inbound = { options.chiasson.ssh.inbound = {
enable = lib.mkEnableOption "Apply `authorizedKeys` from the SSH inventory."; enable = lib.mkEnableOption "Apply SSH inventory public keys to `authorized_keys`.";
userAuthorizedHosts = lib.mkOption { userAuthorizedHosts = lib.mkOption {
type = lib.types.attrsOf (lib.types.either (lib.types.enum [ "all" ]) (lib.types.listOf lib.types.str)); type = lib.types.attrsOf (lib.types.either (lib.types.enum [ "all" ]) (lib.types.listOf lib.types.str));
default = { }; default = { };
@@ -35,15 +19,15 @@
admin = [ "14900k" "t2mbp" ]; admin = [ "14900k" "t2mbp" ];
}; };
description = '' description = ''
Per user: `"all"` or a list of inventory host names whose keys land in `authorized_keys`. Catalog users that receive the SSH inventory public keys in `authorized_keys`.
''; '';
}; };
}; };
config = lib.mkIf cfg.enable { config = lib.mkIf cfg.enable {
users.users = lib.mapAttrs users.users = lib.mapAttrs
(_user: selection: { (_user: _selection: {
openssh.authorizedKeys.keys = resolveSelection selection; openssh.authorizedKeys.keys = inventory.authorizedKeys;
}) })
cfg.userAuthorizedHosts; cfg.userAuthorizedHosts;
}; };
+49
View File
@@ -0,0 +1,49 @@
# Navi / remote-deploy identity: push closures + activate system profiles over SSH.
{ ... }: {
flake.nixosModules.systemDeployBuilder =
{ config, lib, pkgs, ... }:
let
cfg = config.chiasson.system.deploy.builder;
in
{
options.chiasson.system.deploy.builder = {
enable = lib.mkEnableOption ''
Fleet deploy user for Navi (and similar tools).
Creates the `builder` catalog user, trusts it with the Nix daemon for
`nix copy`, and grants passwordless sudo for non-interactive activation.
SSH inbound is limited to the deploy machine key (see catalog `builder.ssh`).
'';
};
config = lib.mkIf cfg.enable {
chiasson.users.enabled = lib.mkAfter [ "builder" ];
users.users.builder = {
password = "!";
# nix copy / navi push opens an SSH session; nologin breaks the store protocol.
shell = pkgs.bash;
};
nix.settings.trusted-users = lib.mkAfter [ "builder" ];
# Navi wraps remote steps in `sudo -H --` (nix-env, switch-to-configuration,
# provenance under /etc/navi, readlink, …). Scoped store-path rules are fragile;
# this account has no wheel; SSH/key-only in practice (password locked).
security.sudo.extraRules = [
{
users = [ "builder" ];
commands = [
{
command = "ALL";
options = [
"NOPASSWD"
"SETENV"
];
}
];
}
];
};
};
}
+42 -1
View File
@@ -12,7 +12,8 @@
else true else true
) )
cfg.flathub.appIds; cfg.flathub.appIds;
allowedAppIds = cfg.flathub.appIds ++ cfg.unmanaged.appIds; bundleAppIds = map (b: b.appId) cfg.bundles;
allowedAppIds = cfg.flathub.appIds ++ cfg.unmanaged.appIds ++ bundleAppIds;
# Bump in Nix when allowlists change; used to skip slow `flatpak update` / `uninstall --unused` on no-op rebuilds. # Bump in Nix when allowlists change; used to skip slow `flatpak update` / `uninstall --unused` on no-op rebuilds.
declarativeHash = builtins.hashString "sha256" ( declarativeHash = builtins.hashString "sha256" (
lib.concatStringsSep "\n" ( lib.concatStringsSep "\n" (
@@ -20,6 +21,8 @@
++ lib.sort lib.lessThan allowedAppIds ++ lib.sort lib.lessThan allowedAppIds
++ [ "flathub" ] ++ [ "flathub" ]
++ lib.sort lib.lessThan flathubForSystem ++ lib.sort lib.lessThan flathubForSystem
++ [ "bundles" ]
++ map (b: "${b.appId}@${b.bundle.outPath}") cfg.bundles
) )
); );
stateFile = "/var/lib/nixos/flatpak-declarative.hash"; stateFile = "/var/lib/nixos/flatpak-declarative.hash";
@@ -60,6 +63,28 @@
Installed from elsewhere (not Flathub); never auto-installed and never removed by the script. Installed from elsewhere (not Flathub); never auto-installed and never removed by the script.
''; '';
}; };
bundles = lib.mkOption {
type = lib.types.listOf (
lib.types.submodule {
options = {
appId = lib.mkOption {
type = lib.types.str;
description = "Flatpak application ID inside the bundle (e.g. org.moonfin.linux).";
};
bundle = lib.mkOption {
type = lib.types.package;
description = "Upstream .flatpak bundle file (e.g. pkgs.fetchurl { ... }).";
};
};
}
);
default = [ ];
description = ''
System-wide installs from upstream Flatpak bundles (not Flathub).
Reinstalled on switch when the bundle path or appId changes.
'';
};
}; };
config = lib.mkIf cfg.enable { config = lib.mkIf cfg.enable {
@@ -104,6 +129,22 @@
fi fi
done done
${lib.concatStringsSep "\n" (
map (
b: ''
echo "flatpak: ensuring ${b.appId} from bundle."
if ! ${pkgs.flatpak}/bin/flatpak --system install \
--assumeyes \
--noninteractive \
--reinstall \
--bundle \
${lib.escapeShellArg b.bundle.outPath}; then
echo "flatpak: WARN failed to install ${b.appId} from bundle (skip)." >&2
fi
''
) cfg.bundles
)}
run_heavy=0 run_heavy=0
if [ "$HEAVY" -eq 1 ]; then if [ "$HEAVY" -eq 1 ]; then
run_heavy=1 run_heavy=1
+14
View File
@@ -77,6 +77,20 @@
}; };
}; };
}; };
builder = {
isNormalUser = true;
description = "Navi fleet deploy (push + activate only)";
extraGroups = [ ];
createHome = false;
homeManager = {
enable = false;
module = null;
};
ssh.inbound.enable = true;
};
}; };
}; };
} }
+1 -1
View File
@@ -5,7 +5,7 @@
# #
# git diff master..ie/telequebec-update -- \ # git diff master..ie/telequebec-update -- \
# yt_dlp/extractor/telequebec.py yt_dlp/extractor/_extractors.py \ # yt_dlp/extractor/telequebec.py yt_dlp/extractor/_extractors.py \
# > path/to/NixOS-V2/modules/patches/yt-dlp-telequebec.patch # > path/to/chiasson-nix/modules/patches/yt-dlp-telequebec.patch
# #
{ ... }: { { ... }: {
flake.nixosModules.systemYtDlpTelequebecPatch = flake.nixosModules.systemYtDlpTelequebecPatch =
@@ -13,8 +13,9 @@
at-spi2-core, at-spi2-core,
libsecret, libsecret,
libuuid, libuuid,
libxscrnsaver,
libxtst,
mesa, mesa,
xorg,
}: }:
let let
@@ -56,8 +57,8 @@ stdenvNoCC.mkDerivation {
at-spi2-core at-spi2-core
libsecret libsecret
libuuid libuuid
xorg.libXScrnSaver libxscrnsaver
xorg.libXtst libxtst
]; ];
unpackPhase = "true"; unpackPhase = "true";
+1 -1
View File
@@ -1,4 +1,4 @@
# HM side of the flake; option tree is `chiasson.home.*` (docs/module-conventions.md). # HM side of the flake; option tree is `chiasson.home.*` (docs/conventions.md).
{ self, inputs, ... }: { { self, inputs, ... }: {
imports = [ imports = [
./apps/discord.nix ./apps/discord.nix
+7 -1
View File
@@ -110,10 +110,16 @@
gtk3.extraConfig = { gtk3.extraConfig = {
gtk-application-prefer-dark-theme = 1; gtk-application-prefer-dark-theme = 1;
}; };
gtk4.extraConfig = { gtk4 = {
theme = {
name = cfg.gtkTheme.name;
package = cfg.gtkTheme.package;
};
extraConfig = {
gtk-application-prefer-dark-theme = 1; gtk-application-prefer-dark-theme = 1;
}; };
}; };
};
} }
(lib.mkIf cfg.matugenGtkColors { (lib.mkIf cfg.matugenGtkColors {
gtk.gtk3.extraCss = '' gtk.gtk3.extraCss = ''
+8 -25
View File
@@ -12,15 +12,7 @@
cursorPkgs.default cursorPkgs.default
else else
null; null;
# NixOS-New `home-shared.nix`: `cursor-cli` alongside the AppImage. nixpkgs now names this cursorCli = if pkgs ? cursor-cli then pkgs.cursor-cli else null;
# `cursor-agent`; keep both for compatibility across pins.
defaultAgentPkg =
if pkgs ? cursor-agent then
pkgs.cursor-agent
else if pkgs ? cursor-cli then
pkgs.cursor-cli
else
null;
nixIdeTools = [ pkgs.nixd pkgs.nixfmt ]; nixIdeTools = [ pkgs.nixd pkgs.nixfmt ];
cursorWithNixIde = cursorWithNixIde =
if cursorPkg == null then if cursorPkg == null then
@@ -47,21 +39,6 @@
default = true; default = true;
description = "`EDITOR` / `VISUAL` `cursor --wait`."; description = "`EDITOR` / `VISUAL` `cursor --wait`.";
}; };
agent = {
enable = lib.mkEnableOption ''
Cursor Agent CLI (`cursor-agent` in current nixpkgs; older pins used `cursor-cli`).
'' // {
default = true;
};
package = lib.mkOption {
type = with lib.types; nullOr package;
default = defaultAgentPkg;
defaultText = "pkgs.cursor-agent or pkgs.cursor-cli or null";
description = ''
Package providing the `cursor-agent` CLI. Set to `null` to omit the CLI while keeping the GUI app.
'';
};
};
nixIde = { nixIde = {
enable = lib.mkEnableOption '' enable = lib.mkEnableOption ''
Nix IDE extension tooling (`nixd` LSP, `nixfmt` formatter). Nix IDE extension tooling (`nixd` LSP, `nixfmt` formatter).
@@ -82,8 +59,14 @@
else else
cursorPkg) cursorPkg)
] ]
++ lib.optionals (cfg.agent.enable && cfg.agent.package != null) [ cfg.agent.package ] ++ lib.optionals (cursorCli != null) [ cursorCli ]
++ lib.optionals cfg.nixIde.enable nixIdeTools; ++ lib.optionals cfg.nixIde.enable nixIdeTools;
# Cursor's updater drops a generic-linux binary here; remove it so `cursor-agent` resolves to cursor-cli.
home.activation.removeBrokenCursorAgent = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
$DRY_RUN_CMD rm -f "$HOME/.local/bin/cursor-agent"
'';
home.sessionVariables = lib.mkIf cfg.setAsDefaultEditor { home.sessionVariables = lib.mkIf cfg.setAsDefaultEditor {
EDITOR = "cursor --wait"; EDITOR = "cursor --wait";
VISUAL = "cursor --wait"; VISUAL = "cursor --wait";