Compare commits

..

16 Commits

Author SHA1 Message Date
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
Olivier dcdd2c2d90 Rebase to flake parts #13 2026-05-30 21:26:13 -03:00
Olivier 9a4ed1b04b Rebase to flake parts #11 2026-05-29 00:08:10 -03:00
57 changed files with 1961 additions and 162 deletions
+2
View File
@@ -5,6 +5,7 @@ keys:
- &host_t2mbp age1yr7vurfxc3w8ewfw9djfm54atw6ayze69qglamecuft5q0n9gu2sadsa2m - &host_t2mbp age1yr7vurfxc3w8ewfw9djfm54atw6ayze69qglamecuft5q0n9gu2sadsa2m
- &host_ideapad age1hya7pgpe8zal52w3pjf036tpapmehedatfm4r84h30t4wuh079ssedfd37 - &host_ideapad age1hya7pgpe8zal52w3pjf036tpapmehedatfm4r84h30t4wuh079ssedfd37
- &host_nix-server age1p05z980kdtngk9mw67hfev72h7xhslplpxfk9yskgmf0hl4lu3ls04zht9 - &host_nix-server age1p05z980kdtngk9mw67hfev72h7xhslplpxfk9yskgmf0hl4lu3ls04zht9
- &host_r5500 age1pewusvlcgzcnk0kpskgc9qr6jlq8s2yzwnqrnh84p7v5z0kj3qhsya8h2x
creation_rules: creation_rules:
- path_regex: secrets/[^/]+\.(yaml|json|env|ini)$ - path_regex: secrets/[^/]+\.(yaml|json|env|ini)$
key_groups: key_groups:
@@ -15,6 +16,7 @@ creation_rules:
- *host_t2mbp - *host_t2mbp
- *host_ideapad - *host_ideapad
- *host_nix-server - *host_nix-server
- *host_r5500
# Host secrets at modules/hosts/nix-server/secrets.yaml (see configuration imports), # Host secrets at modules/hosts/nix-server/secrets.yaml (see configuration imports),
# or optional extra files under _secrets/. # or optional extra files under _secrets/.
- path_regex: modules/hosts/nix-server/(secrets\.(yaml|json|env|ini)|_secrets/.*\.(yaml|json|env|ini))$ - path_regex: modules/hosts/nix-server/(secrets\.(yaml|json|env|ini)|_secrets/.*\.(yaml|json|env|ini))$
+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
+364 -22
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",
@@ -186,7 +265,25 @@
}, },
"flake-utils": { "flake-utils": {
"inputs": { "inputs": {
"systems": "systems_2" "systems": "systems"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"flake-utils_2": {
"inputs": {
"systems": "systems_3"
}, },
"locked": { "locked": {
"lastModified": 1731533236, "lastModified": 1731533236,
@@ -274,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,
@@ -289,10 +430,33 @@
"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"
], ],
@@ -312,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",
@@ -342,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"
] ]
@@ -362,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"
@@ -394,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=",
@@ -425,6 +675,22 @@
} }
}, },
"nixpkgs_2": { "nixpkgs_2": {
"locked": {
"lastModified": 1779357205,
"narHash": "sha256-cCO8aTqss5x9Ky8GWkpY0Hy5fyTZEbtifSUV8QjSzic=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "f83fc3c307e74bc5fd5adb7eb6b8b13ffd2a36e1",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_3": {
"locked": { "locked": {
"lastModified": 1778869304, "lastModified": 1778869304,
"narHash": "sha256-30sZNZoA1cqF5JNO9fVX+wgiQYjB7HJqqJ4ztCDeBZE=", "narHash": "sha256-30sZNZoA1cqF5JNO9fVX+wgiQYjB7HJqqJ4ztCDeBZE=",
@@ -442,7 +708,7 @@
}, },
"nur": { "nur": {
"inputs": { "inputs": {
"flake-parts": "flake-parts_3", "flake-parts": "flake-parts_5",
"nixpkgs": [ "nixpkgs": [
"nixpkgs" "nixpkgs"
] ]
@@ -485,6 +751,27 @@
"type": "github" "type": "github"
} }
}, },
"personal-website": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1779906441,
"narHash": "sha256-py8KJJMi4awjZHi5FWBPYfbRPvk3Rg9SeFkPJydsG2E=",
"ref": "refs/heads/main",
"rev": "339a9ba1ef79dc9976af77f1fea0302de8a696d0",
"revCount": 16,
"type": "git",
"url": "https://git.chiasson.cloud/Olivier/Personal-Website"
},
"original": {
"type": "git",
"url": "https://git.chiasson.cloud/Olivier/Personal-Website"
}
},
"quickshell": { "quickshell": {
"inputs": { "inputs": {
"nixpkgs": [ "nixpkgs": [
@@ -517,12 +804,14 @@
"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",
"sops-nix": "sops-nix", "sops-nix": "sops-nix",
"spicetify-nix": "spicetify-nix", "spicetify-nix": "spicetify-nix",
"swiftshare": "swiftshare", "swiftshare": "swiftshare",
@@ -558,7 +847,7 @@
"nixpkgs": [ "nixpkgs": [
"nixpkgs" "nixpkgs"
], ],
"systems": "systems" "systems": "systems_2"
}, },
"locked": { "locked": {
"lastModified": 1779000518, "lastModified": 1779000518,
@@ -574,9 +863,25 @@
"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", "flake-utils": "flake-utils_2",
"nixpkgs": [ "nixpkgs": [
"nixpkgs" "nixpkgs"
] ]
@@ -625,6 +930,21 @@
"type": "github" "type": "github"
} }
}, },
"systems_3": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"t2fanrd": { "t2fanrd": {
"inputs": { "inputs": {
"nixpkgs": [ "nixpkgs": [
@@ -648,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": {
@@ -661,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": {
@@ -679,7 +1021,7 @@
}, },
"wrapper-modules": { "wrapper-modules": {
"inputs": { "inputs": {
"nixpkgs": "nixpkgs_2" "nixpkgs": "nixpkgs_3"
}, },
"locked": { "locked": {
"lastModified": 1779297405, "lastModified": 1779297405,
+11 -3
View File
@@ -92,11 +92,19 @@
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
}; };
# DDRM browser-extension backend # After pushing Personal-Website, `nix flake update personal-website` refreshes the lock pin.
ddrm = { personal-website = {
url = "git+https://git.chiasson.cloud/Olivier/DDRM"; url = "git+https://git.chiasson.cloud/Olivier/Personal-Website";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
}; };
# DDRM browser-extension backend
ddrm = {
url = "git+https://git.chiasson.cloud/Olivier/DDRM?rev=c3d014f96855e45b53f7391ce19729493887cb96";
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=";
};
}
];
}
+10 -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 = {
@@ -96,6 +97,8 @@ services.cloudflare-warp.enable = true;
gaming.launchers.enableBottles = false; gaming.launchers.enableBottles = false;
gaming.gamescope.enable = true; gaming.gamescope.enable = true;
gaming.steam.steamTinkerLaunch.enable = true; gaming.steam.steamTinkerLaunch.enable = true;
gaming.sunshine.enable = true;
gaming.sunshine.cudaSupport = true;
monitorInput.enable = true; monitorInput.enable = true;
@@ -124,6 +127,9 @@ services.cloudflare-warp.enable = true;
thunderbird thunderbird
prismlauncher prismlauncher
dualsensectl
devenv
]; ];
@@ -158,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"
'' ''
]; ];
}; };
@@ -0,0 +1,18 @@
# personal-website sops secrets
Add these keys to `secrets.yaml` (on a host with your age key):
```yaml
personal-website:
ghcr-token: <same PAT as swiftshare/ghcr-token, or new>
database-password: <strong password>
auth-secret: <openssl rand -base64 32>
oauth-discord-client-secret: <Discord OAuth secret>
```
```bash
cd modules/hosts/nix-server
sops secrets.yaml
```
After editing, verify with `sops -d secrets.yaml | yq '.personal-website'`.
@@ -0,0 +1,26 @@
# Cloudflare dynamic DNS via NixOS (kissgyorgy/cloudflare-dyndns).
{ config, ... }:
let
secretFilePath = ../secrets.yaml;
in
{
sops.secrets."cloudflare-ddns/api-token".sopsFile = secretFilePath;
services.cloudflare-dyndns = {
enable = true;
apiTokenFile = config.sops.secrets."cloudflare-ddns/api-token".path;
domains = [
"chiasson.cloud"
"chiassoncloud.services"
"swiftshare.cloud"
"blackfry.day"
"yestur.day"
"rp-own.life"
"xn--1iu.cc"
];
proxied = true;
ipv4 = true;
ipv6 = false;
# Default: *:0/5 (every 5 minutes).
};
}
@@ -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";
}; };
@@ -0,0 +1,47 @@
{ lib, ... }:
{
services.gitea = {
enable = true;
# Migrated sqlite DB and repos; do not provision a fresh database.
database = {
type = "sqlite3";
createDatabase = false;
};
settings = {
server = {
DOMAIN = "git.chiasson.cloud";
HTTP_PORT = 3002;
ROOT_URL = "https://git.chiasson.cloud/";
# Clone URLs and LAN git@… -p 222 (was Docker host 222 → container 22).
# Port 222 is <1024 (privileged); systemd must grant CAP_NET_BIND_SERVICE below.
SSH_PORT = 222;
START_SSH_SERVER = true;
SSH_LISTEN_HOST = "0.0.0.0";
SSH_LISTEN_PORT = 222;
};
service.DISABLE_REGISTRATION = false;
};
};
# First boot after migration runs DB migrate + hook regen; default WatchdogSec=30 kills
# gitea while storage/actions init is still running. Type=notify also fails if startup
# is slow; PrivateUsers breaks access to migrated files owned by the real gitea uid.
# Port 222 is privileged (<1024); Docker mapped host 222→container 22 as root.
systemd.services.gitea.serviceConfig = {
Type = lib.mkForce "simple";
PrivateUsers = lib.mkForce false;
NoNewPrivileges = lib.mkForce false;
AmbientCapabilities = "CAP_NET_BIND_SERVICE";
CapabilityBoundingSet = lib.mkForce [ "CAP_NET_BIND_SERVICE" ];
TimeoutStartSec = lib.mkForce "20min";
WatchdogSec = lib.mkForce 0;
};
networking.firewall.allowedTCPPorts = [
3002
222
];
}
@@ -0,0 +1,56 @@
{ config, ... }:
let
secretFilePath = ../secrets.yaml;
in
{
sops.secrets."personal-website/database-password".sopsFile = secretFilePath;
sops.secrets."personal-website/auth-secret".sopsFile = secretFilePath;
sops.secrets."personal-website/oauth-discord-client-secret".sopsFile = secretFilePath;
sops.templates."personal-website-postgres.env" = {
content = ''
POSTGRES_PASSWORD=${config.sops.placeholder."personal-website/database-password"}
POSTGRES_USER=chiassoncloud
POSTGRES_DB=chiassoncloud
'';
};
sops.templates."personal-website.env" = {
content = ''
DATABASE_URL=postgresql://chiassoncloud:${config.sops.placeholder."personal-website/database-password"}@personal-website-db:5432/chiassoncloud
AUTH_SECRET=${config.sops.placeholder."personal-website/auth-secret"}
AUTH_DISCORD_SECRET=${config.sops.placeholder."personal-website/oauth-discord-client-secret"}
'';
};
services.personalWebsite = {
enable = true;
app = {
image = "ghcr.io/olivierchiasson/personal-website:main";
ghcr = {
username = "olivierchiasson";
passwordFile = config.sops.secrets."swiftshare/ghcr-token".path;
};
port = 3001;
authUrl = "https://chiasson.cloud";
publicUrl = "https://chiasson.cloud";
disableTelemetry = true;
environmentFiles = [ config.sops.templates."personal-website.env".path ];
};
database = {
user = "chiassoncloud";
name = "chiassoncloud";
environmentFiles = [ config.sops.templates."personal-website-postgres.env".path ];
};
auth.discord.clientId = "1400660345068191855";
umami = {
websiteId = "3b2f29d3-11b8-4a3b-bc76-bda3f27926d1";
scriptUrl = "https://analytics.chiasson.cloud/script.js";
};
};
}
+6 -10
View File
@@ -10,25 +10,19 @@
{ {
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/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/gitea.nix
./_services/prowlarr.nix ./_services/cloudflare-dyndns.nix
./_services/flaresolverr.nix
./_services/radarr.nix
./_services/qbittorrent.nix
./_services/seerr.nix
./_services/dispatcharr.nix
]; ];
boot.loader.grub = { boot.loader.grub = {
@@ -80,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 = {
+16 -10
View File
@@ -1,5 +1,5 @@
swiftshare: swiftshare:
ghcr-token: ENC[AES256_GCM,data:wNzBA8Ib5WjxoKkGiWkfeGspKzy/vzbwwAp/+cjRF9Vsmlyx67OovQ==,iv:MCrkALYCHiPDb1tNQaWRrxuYSRXD6JtzJzEOr1aqhBk=,tag:okQfIP5IJUUIFfwAlZM1ow==,type:str] ghcr-token: ENC[AES256_GCM,data:V/5dLVLv4BbAxdMiBxXgmNbK17HAQkqzHJA2NWzOFfFlcy3dq8SnZQ==,iv:YTB3Bef+kZXunXVUCkFj/YZo1POdx2K+bNvzarSJ1Iw=,tag:HEBT4ZKMXTIy+ZEkNx3rHw==,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]
@@ -11,10 +11,15 @@ swiftshare:
minio-secret-key: ENC[AES256_GCM,data:szkx+MTbMWmfbQ==,iv:+1zlHJRKMR4XDv1rrkOeilz06YA1W/1o+egylm/ZjPs=,tag:70QO3dPp9WRd71Puzl47QA==,type:str] minio-secret-key: ENC[AES256_GCM,data:szkx+MTbMWmfbQ==,iv:+1zlHJRKMR4XDv1rrkOeilz06YA1W/1o+egylm/ZjPs=,tag:70QO3dPp9WRd71Puzl47QA==,type:str]
immich: immich:
database-password: ENC[AES256_GCM,data:YWLt2pty/yVrrF7K,iv:uqrQGfST/A6LzRZ4+O0puXA1bd/7CL5A/T7jU+/++X8=,tag:/gNGK3z4RembX+tBET4M5g==,type:str] database-password: ENC[AES256_GCM,data:YWLt2pty/yVrrF7K,iv:uqrQGfST/A6LzRZ4+O0puXA1bd/7CL5A/T7jU+/++X8=,tag:/gNGK3z4RembX+tBET4M5g==,type:str]
personal-website:
database-password: ENC[AES256_GCM,data:PR6nNKOqB/SE956hXA4=,iv:/1usgEXfY+ef9bOAaCdjduqBqoonAm5saFBSjdGhm1Q=,tag:mDThIsYVUKyN6vQlh2YYbQ==,type:str]
auth-secret: ENC[AES256_GCM,data:NHY+0tOA6FRmvkKZ/KgpogwOf+DuF42aoxspPUdVSi/iY5dF2yY8hwHaehI=,iv:9Gv/1YDcU+rMuX3PrwwT97qdsywIn+/wWEk17evyloo=,tag:R6RZbRhyXkwfZ0p/fZ+YcA==,type:str]
oauth-discord-client-secret: ENC[AES256_GCM,data:YegPgoSRKNcDaID9LPWxHDz4T7VnhFfuWMyALfFhpg8=,iv:VSLWA1HG1+Y70tKnRoFulBZSKdoJTYmIDzCXIZeFYCc=,tag:yNR8rrm/7Mrj/RIVNLFfsg==,type:str]
cloudflare-ddns:
api-token: ENC[AES256_GCM,data:wFKbclETO0YQTcfNUdKyr6mxQODeiaYn3gLeC1mWeRda97rOvlum+Q==,iv:IuT4exNhh0z+9DbY3WNnVqEy4398DTm7aluhOv9XFss=,tag:GGPoSJLBScTmXyQ7Vab6EA==,type:str]
sops: sops:
age: age:
- recipient: age1yyzgmazjxkvwtfcv9re3lqmt2ru5dcrfu3sauysm0wzfwzvyap8qkjkq32 - enc: |
enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBlVWpFK2RRSHRxVFVSdEdI YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBlVWpFK2RRSHRxVFVSdEdI
S01BSEZQUTZCV0tvM1lpSFNYc3g3ek5QNjJrClNyVUtKYnRtWVRYRkE2SStWRVRR S01BSEZQUTZCV0tvM1lpSFNYc3g3ek5QNjJrClNyVUtKYnRtWVRYRkE2SStWRVRR
@@ -22,8 +27,8 @@ sops:
ZUpnemRBSmlSZVpmRW0wNFhIK3BibVkKdD14ki8dJbYMjsBkC1Nm5TOM6M33eLJ6 ZUpnemRBSmlSZVpmRW0wNFhIK3BibVkKdD14ki8dJbYMjsBkC1Nm5TOM6M33eLJ6
IUrKDWeZXEVe2sMhBb31Zv+tinwtHSsvpxDIsjstpxtH+5wTyoQVdA== IUrKDWeZXEVe2sMhBb31Zv+tinwtHSsvpxDIsjstpxtH+5wTyoQVdA==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
- recipient: age1elk6zwmcylwfk7gd4pjda7g29upftjvxys8py42s8d42jklnyv7s7dm9z2 recipient: age1yyzgmazjxkvwtfcv9re3lqmt2ru5dcrfu3sauysm0wzfwzvyap8qkjkq32
enc: | - enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA1bFJDNDdsWGIzMDl3TmRr YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA1bFJDNDdsWGIzMDl3TmRr
MStsZVFRa1dIVmJGU3krWWlpc2FZMU9EREF3CkdDZFc0Y3ZIMVZxNHorWFRHaWwy MStsZVFRa1dIVmJGU3krWWlpc2FZMU9EREF3CkdDZFc0Y3ZIMVZxNHorWFRHaWwy
@@ -31,8 +36,8 @@ sops:
NUZIYnZIMDRWTXpwTURMc2tzelp3VjAKHHBkHhz+t03W0ojsOBB2i3K4ZMUXvrwF NUZIYnZIMDRWTXpwTURMc2tzelp3VjAKHHBkHhz+t03W0ojsOBB2i3K4ZMUXvrwF
4mjNqNBcAJ1uHgJP7qvpNjxEW1LcsdQKmXavoqizX+XfLaA3zEwB0Q== 4mjNqNBcAJ1uHgJP7qvpNjxEW1LcsdQKmXavoqizX+XfLaA3zEwB0Q==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
- recipient: age1p05z980kdtngk9mw67hfev72h7xhslplpxfk9yskgmf0hl4lu3ls04zht9 recipient: age1elk6zwmcylwfk7gd4pjda7g29upftjvxys8py42s8d42jklnyv7s7dm9z2
enc: | - enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAwTWR0N3UwdTB0UDZxRmV5 YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAwTWR0N3UwdTB0UDZxRmV5
R3dkYUhZaElMbkxxSllTNWkrb05VSkJrMUNRCjZTUTlvVTU2MHY5ZS9oU2pCSlFu R3dkYUhZaElMbkxxSllTNWkrb05VSkJrMUNRCjZTUTlvVTU2MHY5ZS9oU2pCSlFu
@@ -40,7 +45,8 @@ sops:
WWZwbkR4dTFjK2NZcW9pTTNHd252N3cKiz8l9AWciFOBU+wcT9T1WA4bToPYfq8G WWZwbkR4dTFjK2NZcW9pTTNHd252N3cKiz8l9AWciFOBU+wcT9T1WA4bToPYfq8G
Nf0uOoSWPTJ/2SRNkSu7FMumATH4ldQ6TFSwKda3mBfBwhnFzLq10Q== Nf0uOoSWPTJ/2SRNkSu7FMumATH4ldQ6TFSwKda3mBfBwhnFzLq10Q==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
lastmodified: "2026-04-30T18:45:45Z" recipient: age1p05z980kdtngk9mw67hfev72h7xhslplpxfk9yskgmf0hl4lu3ls04zht9
mac: ENC[AES256_GCM,data:DD9NZcYQVSByaQvGAB7b/Wpk7SWBBsWtzAM9MkIHMmyxNomiPPUFQR6+18QDUCHQXk1xXMUi79bnTRz8SdoBXVjbHG8Qhy3n6D1sFeEgXC42pgem7hBPfmJlgcIPNYEguXPISLsp/Zx9ISEnH5Zul0v8/G2ACN7Y/U3jtaHx4U8=,iv:g1k16EhTR+t9jCpvhmiXYZV99aMk1DrS4frpl5q93lM=,tag:FigaXNw+IbpZ7E0a+ySb3g==,type:str] lastmodified: "2026-05-28T21:04:07Z"
mac: ENC[AES256_GCM,data:L53UYFh5xtuMx19GKAg3jW7U0/DlwJ2usy/pup+4t1HQN3KHxMwbc4BzLYkLnRBTwKMJdfKXiYmmYiYvfbbWzsPtfXLxPnF/5ROiCJ2NlxAe86SmRy2nI++eTHAXRgexIhYyL7SchsroGRvW2B3aL1jV+Eu11fD9trA9Ex1EfuI=,iv:XrRJCFSgwW2+N+4FnWrFFZz8UEVzhuhpRtHGtf8dyqc=,tag:LcO4+ilwKdU+JPyjyKaGNw==,type:str]
unencrypted_suffix: _unencrypted unencrypted_suffix: _unencrypted
version: 3.12.2 version: 3.13.1
@@ -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 ];
}
+85
View File
@@ -0,0 +1,85 @@
{ self, inputs, ... }: {
flake.nixosModules.r5500Configuration =
{
self,
config,
lib,
pkgs,
...
}:
{
imports = [
self.nixosModules.systemDeployBuilder
self.nixosModules.r5500Hardware
inputs.sops-nix.nixosModules.sops
self.nixosModules.system
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 = {
enable = true;
efiSupport = false;
device = "/dev/sda";
};
services.openssh = {
enable = true;
settings = {
PasswordAuthentication = true;
KbdInteractiveAuthentication = false;
PermitRootLogin = "no";
UseDns = false;
};
};
sops = {
defaultSopsFile = ../../../secrets/secrets.yaml;
defaultSopsFormat = "yaml";
age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ];
};
sops.secrets."users/server/hashedPassword".neededForUsers = true;
security.sudo.wheelNeedsPassword = true;
nix.settings = {
experimental-features = [ "nix-command" "flakes" ];
trusted-users = [ "root" "@wheel" ];
allowed-users = [ "root" "@wheel" ];
};
chiasson.system = {
networking = {
hostName = "r5500";
networkManager.enable = true;
};
extraPackages = with pkgs; [ btop git ];
};
chiasson.system.deploy.builder.enable = true;
chiasson.users = {
enabled = [ "server" ];
hostOverrides.server = {
hashedPasswordFile = config.sops.secrets."users/server/hashedPassword".path;
};
};
services.xserver.enable = lib.mkDefault false;
system.stateVersion = "25.11";
};
}
+13
View File
@@ -0,0 +1,13 @@
{ self, inputs, ... }: {
flake.nixosConfigurations.r5500 = inputs.nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
specialArgs = {
inherit self inputs;
host = "r5500";
system = "x86_64-linux";
};
modules = [
self.nixosModules.r5500Configuration
];
};
}
+51
View File
@@ -0,0 +1,51 @@
{ ... }: {
flake.nixosModules.r5500Hardware =
{
config,
lib,
pkgs,
modulesPath,
...
}:
{
imports = [
(modulesPath + "/installer/scan/not-detected.nix")
];
boot.initrd.availableKernelModules = [
"uhci_hcd"
"ehci_pci"
"ahci"
"usb_storage"
"usbhid"
"sd_mod"
];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ "kvm-intel" ];
boot.extraModulePackages = [ ];
fileSystems."/" = {
device = "/dev/disk/by-uuid/934a5ec3-4bab-49c3-96c9-c857c50076ba";
fsType = "btrfs";
options = [ "subvol=@" ];
};
fileSystems."/home" = {
device = "/dev/disk/by-uuid/934a5ec3-4bab-49c3-96c9-c857c50076ba";
fsType = "btrfs";
options = [ "subvol=@home" ];
};
fileSystems."/boot" = {
device = "/dev/disk/by-uuid/6399d086-687b-4ca9-ad34-da1dd85203d5";
fsType = "ext4";
};
swapDevices = [
{ device = "/dev/disk/by-uuid/ddb9fea1-7c44-44bc-bc74-79a3adb6cc35"; }
];
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
};
}
@@ -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;
}
+23 -5
View File
@@ -37,6 +37,12 @@
aliases = [ "nix-server" ]; aliases = [ "nix-server" ];
publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIL3KDicMjtOFR6LfZrFzfAD1gdYUdwv6ZM4PSgtmIuzd nix-server"; publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIL3KDicMjtOFR6LfZrFzfAD1gdYUdwv6ZM4PSgtmIuzd nix-server";
}; };
r5500 = {
hostName = "192.168.2.100";
aliases = [ "r5500" ];
publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIK7iWCEtkYDLZFRF3w1gzyAok5VCAGUOwu4iWZdMjf3D r5500";
};
}; };
mkIdentityFileName = hostName: ".ssh/id_ed25519_${lib.strings.toLower hostName}.pub"; mkIdentityFileName = hostName: ".ssh/id_ed25519_${lib.strings.toLower hostName}.pub";
@@ -54,11 +60,18 @@
(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@192.168.2.103 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 casaos 192.168.2.103 Host git.chiasson.cloud gitea
HostName 192.168.2.103 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
Port 222 Port 222
User git User git
IdentityAgent ${identityAgent} IdentityAgent ${identityAgent}
@@ -78,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;
}; };
+1
View File
@@ -21,6 +21,7 @@
self.nixosModules.systemPalera1n self.nixosModules.systemPalera1n
self.nixosModules.systemCaching self.nixosModules.systemCaching
inputs.swiftshare.nixosModules.systemServiceSwiftshare inputs.swiftshare.nixosModules.systemServiceSwiftshare
inputs.personal-website.nixosModules.systemServicePersonalWebsite
self.nixosModules.systemServiceImmich self.nixosModules.systemServiceImmich
]; ];
}; };
+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
+44
View File
@@ -88,6 +88,39 @@
''; '';
}; };
sunshine = {
enable = lib.mkEnableOption "Sunshine self-hosted Moonlight streaming host";
openFirewall = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Open Sunshine/Moonlight ports via `services.sunshine.openFirewall`.";
};
capSysAdmin = lib.mkOption {
type = lib.types.bool;
default = true;
description = ''
Grant CAP_SYS_ADMIN to Sunshine for DRM/KMS capture (`services.sunshine.capSysAdmin`).
'';
};
autoStart = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Start Sunshine with the graphical session.";
};
cudaSupport = lib.mkOption {
type = lib.types.bool;
default = false;
description = ''
Build Sunshine with CUDA/NVENC (`pkgs.sunshine.override { cudaSupport = true; }`).
Enable on NVIDIA hosts for hardware encoding.
'';
};
};
launchers = { launchers = {
enableBottles = lib.mkOption { enableBottles = lib.mkOption {
type = lib.types.bool; type = lib.types.bool;
@@ -128,6 +161,17 @@
dedicatedServer.openFirewall = cfg.steam.dedicatedServer.openFirewall; dedicatedServer.openFirewall = cfg.steam.dedicatedServer.openFirewall;
}; };
services.sunshine = lib.mkIf cfg.sunshine.enable {
enable = true;
openFirewall = cfg.sunshine.openFirewall;
capSysAdmin = cfg.sunshine.capSysAdmin;
autoStart = cfg.sunshine.autoStart;
package = pkgs.sunshine.override {
cudaSupport = cfg.sunshine.cudaSupport;
cudaPackages = pkgs.cudaPackages;
};
};
hardware.graphics = lib.mkIf cfg.graphics.enable { hardware.graphics = lib.mkIf cfg.graphics.enable {
enable = true; enable = true;
enable32Bit = cfg.graphics.enable32Bit; enable32Bit = cfg.graphics.enable32Bit;
+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
+8 -2
View File
@@ -110,8 +110,14 @@
gtk3.extraConfig = { gtk3.extraConfig = {
gtk-application-prefer-dark-theme = 1; gtk-application-prefer-dark-theme = 1;
}; };
gtk4.extraConfig = { gtk4 = {
gtk-application-prefer-dark-theme = 1; theme = {
name = cfg.gtkTheme.name;
package = cfg.gtkTheme.package;
};
extraConfig = {
gtk-application-prefer-dark-theme = 1;
};
}; };
}; };
} }
+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";
+51 -42
View File
@@ -12,60 +12,69 @@ caching:
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]
sops: sops:
age: age:
- recipient: age1yyzgmazjxkvwtfcv9re3lqmt2ru5dcrfu3sauysm0wzfwzvyap8qkjkq32 - enc: |
enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBtc25Wd0dlZldBVHZKTDRW YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBBTmhhd2M0M1ZsdEtReUFM
ZlVHamluT0hPbVArdWx0Qlk5czR6ZldIaEhRCnVkUVp6cHRwdnNsRDBhV285Q0tV MEs0SHZ4dUkxNWdUZjBoSFVQd0RvZTdWVndnCitxNGRGUWcwWWpZcytpUzhOcCtF
RitNWHNrUy9pN0dpanFSVHJjK0VuM1kKLS0tIHVLRWFRNmZyR2JOR3FwYjZsME9v SHRoSzZkcWVnZzdrL0JoVkk0cTlSbWcKLS0tIGFCUFd6WXpxVWg2MVFCcnJ1Qklp
L2ZKbFIydkJ5L25GSWFBamhtbHZXV0UKAPIGMBbIdR7sCxsCYJa/9kajqmceOAJa TDBBc21HWHJiZkZPZ3ZyWFhOeEFaT3cK5SFqqR2Fc6o/RTDPkly6xS+hEY331ws6
/jjxxcaWDkv5cmq0u8qFZEDkTjY3PKoJofBcx5q0npuJmrIA/oLeJA== 8KIGxGXC9iO7ExGOD5nzLzFJmjZzfp6IIvdQ0rzkJY4C1vKW1pPZMw==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
- recipient: age1elk6zwmcylwfk7gd4pjda7g29upftjvxys8py42s8d42jklnyv7s7dm9z2 recipient: age1yyzgmazjxkvwtfcv9re3lqmt2ru5dcrfu3sauysm0wzfwzvyap8qkjkq32
enc: | - enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBvcm5rZmtlUy9yQSs3Vnhh YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBEeWEwQ0o4TDZkSFI4WGFZ
eDVYaUxvcCtMYXhEa0ViTmFYK3JONnFsTWw0Cmdad2ZKdGcxeGQzeGVnTXRUeGJZ QXZIVGQ4ajI5SFpLczJpZHh3Tm9lMkZ6K1dBCjhWeDVWcVNWM01maXVDdTRYRzI4
R1kxQ1ZDK0FsQzc4S1EwMFN4VVZaTzAKLS0tIDJ5S3QycG5ZdThIVlZEVjBnMG9K OUk0c0czSzNhVWxkUWdIU3JCaytZdkEKLS0tIHpBNzdpT0oxNkhBalVXYkpzRitn
V1RSem1LdWpKT2o2aHh5dC9wVGJRakUK+5/eE+9WtSXmwoJ2Nqk4ni01GS4c3gLQ dXRWUmxmTFNnYm5ZbzFYMjJ5THJlQ3cKn6AZhiS/eqvmsFbKkv79sE198ywNeNZf
p+wcpiOsxwnnisZTxag2yCn4hlv6FcOUWOcISq5H/sxwKgjBaeeuRQ== BQJSQ353CmJQ7z7uL0m+/96aeaUu9Kjk37bPeMmxrXhhW7Kvwzwuiw==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
- recipient: age193gw802ytal7h5p5q37kpd9079k2vsflzmnvupcwfxh2kjdrwqtsk3g6rm recipient: age1elk6zwmcylwfk7gd4pjda7g29upftjvxys8py42s8d42jklnyv7s7dm9z2
enc: | - enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB6TjZVN3dFbmg5MGhNVFdD YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBHbDY1WXl5Wk81Rk0zOXE4
YTZ4WVdOUWRGTzkrUmRGVUpobmg0Y0ozaUFNClh6cXhKWVkxcTlRYmZ5NmNqeEJJ dGczVWZQZW9pMkxrZUdIWUg1V0EzQVdOb1Y0CmxYMnR1RDk4TUVCQm4xVWlnMHNB
YzdYR1FyRExabXBDSnVSbXNxZy91am8KLS0tIHZLa0dXWHZhMDU5Q3BNR2U3NG00 eSs2QVZpQy83KzZMZDAzbGRkR2xmQ0UKLS0tIE5GNFRTWDhFQ1IrZUY0Si96UUhp
eFp4Vmxha0xOVTVwd09PVzgrdzFNL2sKDEofAS4W4i8+VBU0wl1yTWmOogNbGHhY WXc4djdTVmp1bG5kN3ByTHViVy81R1kKGEgUjmwNtL7CFqILNWUEh2mHM4C3L9de
azvb0QmxrYpurxjep3BYsc/5Co6U4mwowidoyzQLsiBJWDWy3wPdLQ== FBoqaUvE9Dc8lmxjicKKAlnf2ZbwTZ6+Iv2I+KLakjFMznSi4QFO0g==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
- recipient: age1yr7vurfxc3w8ewfw9djfm54atw6ayze69qglamecuft5q0n9gu2sadsa2m recipient: age193gw802ytal7h5p5q37kpd9079k2vsflzmnvupcwfxh2kjdrwqtsk3g6rm
enc: | - enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB2d3dLVitDTVpyNllCTEJN YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBxZmxST1V3WXN1N01VaE16
R1RLc2NkRnBrandDakhxeTJWeEdMVkZyS0JRCnl5Uk9FSk1iZmxCZUI1ZWdmYlJa Njd3S2kzT1l1R2o5TXErYXIyTEo2ZWxNemxrCm5QZG92WDBjNzNLRUxqZ2txLzNz
dEFlbjkxYjI4Y2FDZmswOGZqMUxGUXcKLS0tIGdqVWNaU1NWcGs4QXZyTkc5Z2VK dUU3cURjN1Y3d2ZBMHZuV0hOUWFsVzAKLS0tIG85TnBvL0F2dzBTdHQwdmZoOFhs
MGpaWmtHdzdpRDJxQXNveTd5WFkzTTgKxKuoC36uLqy+QoSGVcQekv5wn69yF0qH d2F5QWsxaUR0M2x1M1RVaHp0ZG1vVlkKtrMg6lwXFrXyZiweS6C1a46/Z5oDbHpm
2qZPAm3wnf0KzZ8Wo/B8nXkjkq4llfKHbwfePiMRL4RObKXAejYhLA== FJ7xh8z3fPGRsoDWa/Qet8seNgIbLfAgyQvtIzKiKDTat4f73WIrsw==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
- recipient: age1hya7pgpe8zal52w3pjf036tpapmehedatfm4r84h30t4wuh079ssedfd37 recipient: age1yr7vurfxc3w8ewfw9djfm54atw6ayze69qglamecuft5q0n9gu2sadsa2m
enc: | - enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB4am5wWWRkL3J0dlZVaWJR YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB3TDhIdDRXVmgyVVZHQXp5
eVdMNnhTdGJqeFlNdnppTHNLdjdjcVNYbUQ4CjcwdGM5Nmdvck1wb0RSRjMwL01D N3dlTXBZb0E0bEtuZ2RFN3pQYkZWK25SbEg0CkFaMXRleFBISFAvSzNSSGpBbFVo
STlFdkdDallLLzNnbGY4WndiLzdRMzAKLS0tIHRENzRFTVA2MWE5djVHUy9peVdG eTgxZitQNmZmYWx0a2dPcjRMdGpzNDQKLS0tIHpOTmtCM2lCS0NkZ2cvb0loK0pt
dkZDTTJvWU1vZHN2ZUR1R1Z3UWpkSlEKjo621j7xjyyHcc/ij8/X0H5uNLD6SnZa RTBMdHdnTnFBT2IzQ3JvWWpmZStFSTgK6CxSfV6tlejPie4xKiVUnEhpH7DYJ2lu
o+apPj44+fTJFL5+VjP/XOsx0rCKTQhnV0gGfZU2SPtfH2BUiDjV0g== 9U9bw7i3qKvjSlFQBYghF0P4EK32k3x8lkAucK3m3ub8+wbunOJWTg==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
- recipient: age1p05z980kdtngk9mw67hfev72h7xhslplpxfk9yskgmf0hl4lu3ls04zht9 recipient: age1hya7pgpe8zal52w3pjf036tpapmehedatfm4r84h30t4wuh079ssedfd37
enc: | - enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA5YmF1K1ZtQmhoMGRVM0Fk YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAwbW1GSERmWGNvK2dCRjVC
bW1PL0RPQ1ZUZEg4VU5VUi9BdlNhM2hPZkRNCnVIaUJJR1dFeXA1SWk2UFBkQW9a M2U1SERPU3dGRnlwRmV4bHdNd3RxYzJ2YWk4Cm52bXZuYkt4dlEvbVA0cm51cmRX
cjc4bFRDb2p0eXJodG5IWk01ekdCdG8KLS0tIHFzU3l3WGcyTmZ0QjRyTmQwcDRZ MW1NYXllU1MwdE5jeEI4VmU5TytjcDAKLS0tIHNPOTIyN0ZlSHI3M0F3S01POGZF
Mnp5K1VjcncrWWt4SEUrUVlCTTc3OVkKtRqPoatEp8NvZW4Z73nfCUshdz90SCad RFM3NndnWSt4NnM5TkcyeXRmUFB1Q2MKDCuWlWqV/Dogr6wXDb02BGrsnpyGe22D
VFgYF/2DYc7lSDP7otbsjBzGlauQQTWF1wfgEVOkw2EzOt2LCoflbg== ivWjVoYx5ClxlKyt7Xuies9fLNjRTVfjJFD5DQTinSPjxrusJg0O5w==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
recipient: age1p05z980kdtngk9mw67hfev72h7xhslplpxfk9yskgmf0hl4lu3ls04zht9
- enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA5UkhFTld1T2x4bEZ0Mldy
dmZ4b3UzaHgxN2FUenBaVFRFY0puZlVVM2dRClVMK3VCM0w3cUM5Um90YmM1TUhu
Vk5wMm4vZXNsTnlQWFV0ZG1tS25UWkkKLS0tIDR3d2NmZjVzUEF2aHN3NW05bGVp
L0NHbUF3NzJ3NUtnZFVRMVdLa2ZtWWcKzuMx/jDuZ3z7IPPvAmcYyjLi4c3Toj7a
6POXxpxjGhlWJaV47jqeN+7mQY2oTHE/x4raoX/KA2ouXL29K8QpmA==
-----END AGE ENCRYPTED FILE-----
recipient: age1pewusvlcgzcnk0kpskgc9qr6jlq8s2yzwnqrnh84p7v5z0kj3qhsya8h2x
lastmodified: "2026-03-24T00:15:02Z" lastmodified: "2026-03-24T00:15:02Z"
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:dYTwO5DtkKinTKfBXGuvXRFxl8yavxXMKTw27M5/GcK/kkstHBG119IRk9B9KC6s6IHTY81U3MeUxE9XwdBiE7q4m15+ZO2vmdBVhN8wAh+82P9BP0HSaxLkjWLeKWBfULyLX/YXmQVsr09/NUEVSZcugJ6m40Ta+X9AQgO+cyA=,iv:FmsznsKTuIr61s3Zn0QZKSKvb/e2AljEB1ijKE52RKk=,tag:rHF2Xi4iP9VF33rxpBr5pg==,type:str]
unencrypted_suffix: _unencrypted unencrypted_suffix: _unencrypted