Compare commits

...

19 Commits

Author SHA1 Message Date
Olivier 6b8fa60540 docs: update README with feature overview and module structure 2026-07-22 18:02:21 -03:00
Olivier 86dc80eb3c feat(wisdom): enhance vscode module with java support and writable settings
Add Java extension support and configure multiple JDK runtimes.
Introduce a wrapped VS Code package that includes JDK 25 in the PATH
to support the Red Hat Java language server.

Switch from using `programs.vscode.userSettings` to a manually managed
`settings.json` file via `home.file`. This allows the settings file to
be writable by external tools like `kilo-code` at runtime while
preserving them across Nix configuration switches by using `force = false`.
2026-07-19 18:13:30 -03:00
Olivier f1bd5146a3 refactor(hosts): simplify streaming display configuration
Refactor the streaming display logic for the 14900k host to use the
`streamingDisplay` configuration object directly instead of separate
variables. This simplifies the monitor layout logic for both Niri and
Hyprland.

Additionally, remove the fragile `sunshineOutputIndex` option in favor
of using the stable connector name for Sunshine's `output_name`. This
prevents issues where numeric monitor indices drift after hotplugging.
The change also includes an explicit encoder list for Sunshine to avoid
probing for AV1 on hardware that does not support it.
2026-07-18 09:35:52 -03:00
Olivier 6f35d60aab refactor(system): simplify steamTinkerLaunch logic and package assignment
Refactor the steamExtraPkgs conditional logic into a single line and
introduce a launcherSet variable to reduce redundancy when assigning
packages to the system or specific users.
2026-07-17 17:37:38 -03:00
Olivier 3d2e74d115 feat(wisdom): add vscode editor module
Add a new module for Visual Studio Code configuration including
default extensions (nix-ide, markdown-all-in-one, prettier) and
nixd/nixfmt settings. Enable vscode on 14900k, t2mbp, and uConsole
hosts with additional extensions.

Also clean up unfree predicate logic in ideapad configuration.
2026-07-17 17:17:14 -03:00
Olivier 548a438f6a refactor(wisdom): rename wisdomSimpleSlice to wisdomSlice
Rename the helper function `wisdomSimpleSlice` to `wisdomSlice` across
the codebase and update documentation to reflect the new naming
convention. This change simplifies the API for creating trivial
single-package slices.
2026-07-17 11:44:56 -03:00
Olivier 0939766e3a refactor(wisdom): migrate app and browser modules to wisdomSimpleSlice
Replace manual Home Manager module definitions in `localsend`,
`pokeclicker`, and `flow-browser` with the `wisdomSimpleSlice`
helper. This reduces boilerplate by centralizing the option
declaration and configuration logic.
2026-07-17 11:31:35 -03:00
Olivier f424ab98c7 refactor(hosts): remove redundant host and system arguments
Remove explicit `host` and `system` definitions from `specialArgs` in
various host `default.nix` files. These values are redundant as they
can be derived from the flake context or are already provided by the
calling environment.
2026-07-17 11:30:34 -03:00
Olivier 15a4d8d5e8 feat(nfs): centralize nixdesk jellyfin nfs client module
Move the nixdesk jellyfin NFS client configuration from individual
host services to a centralized NixOS module in `modules/system/nfs/`.

This change:
- Creates `modules/system/nfs/default.nix` containing the `nixdeskJellyfin`
  flake module.
- Removes redundant NFS client definitions from `modules/hosts/nix-server/`
  and `modules/hosts/r5500/`.
- Updates `modules/hosts/r5500/configuration.nix` to use the new
  centralized module.
- Cleans up unused local disk configuration in `modules/hosts/14900k/`.
2026-07-17 11:11:21 -03:00
Olivier 5f7e9cada5 refactor(desktop): clean up options formatting and simplify slice logic
Refactor `modules/desktop/options.nix` to improve code readability by
expanding the `optPath` list definition and simplifying the
`wisdomSimpleSlice` configuration logic.

Also apply minor style fixes to `modules/wisdom/filebrowsers/nemo.nix`
to ensure consistent spacing within list definitions.
2026-07-16 20:55:53 -03:00
Olivier c4a0eb0a98 feat(chromium-hevc): enhance GPU configuration options and streamline desktop item creation 2026-07-16 19:51:06 -03:00
Olivier 103a485bf8 feat(wisdom): introduce wisdomSimpleSlice helper for package modules
Implement a `wisdomSimpleSlice` helper in `desktop/options.nix` to reduce
boilerplate for simple Home Manager modules that only manage packages.
This helper handles the `enable` option creation, platform-gating via
`availableOn`, and conditional inclusion in `home.packages`.

Refactor several existing wisdom modules to use this new helper:
- `wisdomBrowsersChrome`
- `wisdomBrowsersEdge`
- `wisdomEditorsKate`
- `wisdomEditorsObsidian`

Additionally, clean up `wisdom/default.nix` by removing the explicit
import of `wisdomShellBash` (which is now handled via auto-wiring) and
improving code formatting.
2026-07-16 19:46:23 -03:00
Olivier 13f35677be refactor(wisdom): decentralize module defaults and update conventions
Move Home Manager module defaults from a centralized `desktop-home-defaults.nix`
file into their respective `wisdom` slices. This change aligns with the
updated documentation regarding module ownership, where defaults live
with the option that owns them rather than in a shared defaults file.

Additionally, update `docs/conventions.md` to reflect this new pattern for
defining `mkEnableOption` defaults and move base git/encryption
configurations into the core `wisdom` module.

- Delete `modules/hosts/desktop-home-defaults.nix`
- Update `modules/wisdom/default.nix` to include shared git and package defaults
- Add `default = ...` to `mkEnableOption` in various `wisdom` modules
- Update `docs/conventions.md` with new HM slice guidelines
2026-07-16 13:51:48 -03:00
Olivier 579a120326 feat(14900k): update dms-defaults settings and plugin configurations
Add various UI and system configuration parameters to dms-defaults,
including matugen contrast, widget styling, hyprland layout overrides,
elevation settings, battery indicator options, and workspace color
customizations. Also update plugin settings to include a Home Assistant
refresh counter.
2026-07-16 13:45:31 -03:00
Olivier c90d368432 refactor(ssh): restructure inventory and modularize helper logic
Relocate SSH inventory and rbw socket wiring from `modules/lib/` to
dedicated `modules/ssh/` modules to improve logical grouping.

- Move `ssh-inventory.nix` to `modules/ssh/inventory.nix`.
- Move `rbw-ssh-socket.nix` to `modules/ssh/rbw.nix`.
- Relocate `users-merge.nix` logic into `modules/system/users.nix` to
  reduce dependency on generic library modules.
- Update `docs/conventions.md` to reflect new module paths.
- Refactor `modules/deploy/navi.nix` to use a local helper for
  generating Navi hive configurations.
- Add Pi5 specific Niri KDL configuration snippets.
2026-07-15 23:27:03 -03:00
Olivier a787840ce2 style(desktop): reformat nix code and simplify module logic
Refactor `modules/desktop/options.nix` to improve code readability through
consistent indentation and whitespace management. Simplified the
conditional logic for `home-manager.extraSpecialArgs` to remove
unnecessary complexity.

Clean up host configurations by removing commented-out code, removing
unnecessary whitespace, and flattening nested attribute sets into
single-line declarations where appropriate.
2026-07-15 22:20:07 -03:00
Olivier e2fd47aa58 feat(users): add declarative rbw configuration for olivier
Introduce rbw settings within olivier's home-manager module to
centralize the email and base_url configuration. This replaces
manual per-host configuration with a declarative approach.
2026-07-15 18:58:54 -03:00
Olivier 5d70868e4f feat(14900k): replace windsurf with devin-desktop in olivier's extra packages
Update the extra packages for user olivier in the private AI module
to include devin-desktop instead of windsurf.
2026-07-15 18:20:50 -03:00
Olivier db88a44435 refactor(ssh): centralize identity file logic and optimize host selection
Refactor the SSH configuration generation to reduce duplication and
improve efficiency.

- Extract `IdentityFile` line generation into a shared variable in
  `ssh-inventory.nix` to ensure consistency between per-host and
  gitea blocks.
- Optimize `selectedHosts` resolution in `home-manager/default.nix`
  by reusing `activeHosts` when the "all" wildcard is used.
- Tighten IPv4 regex matching in `ssh-inventory.nix` using anchors.
- Update `users-merge.nix` to propagate `extraIdentities` to the
  rbw outbound configuration.
- Set default `authorizedHosts` to "all" for inbound SSH users.
2026-07-15 18:20:40 -03:00
60 changed files with 1135 additions and 1194 deletions
+14 -4
View File
@@ -17,16 +17,26 @@ How to work on it: [conventions.md](./conventions.md).
Each machine: `modules/hosts/<name>/default.nix``nixosConfigurations.<name>`, real config in `*Configuration` + optional `_private/` and `_services/`.
## Features at a glance
- **Users catalog** — one catalog in `modules/system/users.nix`, pick who exists on a host with `chiasson.users.enabled = [ "example" ]`, override per host. Home Manager wires itself from the same list.
- **SSH + Bitwarden (rbw) keys** — one ed25519 keypair per catalog user, private key in Bitwarden, public key in `modules/ssh/inventory.nix`. Outbound SSH uses a `.pub` filter against the rbw agent; inbound `authorized_keys` are generated from the same inventory. [Full wiring in conventions.md](./conventions.md#ssh-model).
- **Wisdom modules** — Home Manager slices in `modules/wisdom/` auto-catalog via `lib.wisdomCatalogExtraModules` (defined in `modules/desktop/options.nix`). The baseline `wisdom` module handles git identity + shared tools; all other `wisdom*` exports are pulled in once per user through `desktopHomeBase``chiasson.users.extraModules.olivier`. Hosts only flip `chiasson.home.<category>.<slice>.enable` — no manual imports in `home.nix`. Categories include `apps` (spotify, discord, localsend), `browsers` (chrome, edge, zen, …), `editors` (cursor, vscode, obsidian, …), `shells` (bash, fish, oh-my-posh, yazi), `desktop` (gtk-qt-theming, screenshot), and `terminals` (kitty). Trivial single-package slices can be written with the `lib.wisdomSlice` helper instead of repeating the `root`/`cfg` + `mkIf` boilerplate.
- **Wallpapers from a dedicated repo** — `modules/desktop/wallpapers.nix` pins `inputs.wallpapers` into the store and exposes it as `CHIASSON_NIX_WALLPAPERS` and `/etc/wallpapers`. Override `chiasson.desktop.wallpapers.source` if needed.
- **Private host data** — import-tree skips `_private/` globally, so machine-only files (firmware quirks, display configs, service tweaks) live next to the host module without leaking elsewhere.
## Deploy / rebuild
Remote builds use the `builder` user (`systemDeployBuilder`, wired through `client-services` on desktops).
Remote builds use the `builder` user (`systemDeployBuilder`, wired through `client-services` on desktops/laptops/tablets).
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
nix develop # devShell has navi + just + hints
just # list local helpers like `sync-dms`
navi apply --on <host>
navi apply-local --node 14900k --sudo # this machine, if hostname matches
navi tui # interactive fleet dashboard
```
Plain rebuild still works: `sudo nixos-rebuild switch --flake .#<host>`.
@@ -37,11 +47,11 @@ Plain rebuild still works: `sudo nixos-rebuild switch --flake .#<host>`.
modules/
hosts/<host>/ # per-machine composition
system/ # nixosModules.system aggregate
desktop/ # GUI stack
desktop/ # GUI stack (niri, hyprland, plasma, DMS)
wisdom/ # HM modules (exports still named wisdom*)
ssh/ # inbound NixOS + outbound HM
deploy/ # navi hive
lib/ # pure helpers → flake.lib
patches/ # one-off patches (yt-dlp, t2fanrd, …)
```
Machine-only stuff lives in `hosts/<host>/_private/` — import-tree skips `_private/` globally, so those files only get pulled in where you import them.
+8 -6
View File
@@ -47,7 +47,7 @@ Leave upstream `services.*`, `networking.*`, `home.*`, etc. alone.
Aggregates: `nixosModules.system` and `nixosModules.desktop` import their leaves. Host configs import those stacks and set options — they shouldn't reimplement whole subsystems.
**Home** — files under `modules/wisdom/`. Baseline is `homeManagerModules.wisdom` (`chiasson.home.enable`, wired via `chiasson.desktop.homeManager.bundleWisdom`). Other `wisdom*` slices auto-wire once per user via `lib.wisdomCatalogExtraModules self` (`modules/lib/wisdom-catalog.nix`); hosts only set matching `chiasson.home.*.enable` toggles — no re-import in `home.nix`.
**Home** — files under `modules/wisdom/`. Baseline is `homeManagerModules.wisdom` (`chiasson.home.enable`, wired via `chiasson.desktop.homeManager.bundleWisdom`). Other `wisdom*` slices auto-wire once per user via `lib.wisdomCatalogExtraModules self` (`modules/desktop/options.nix`); hosts only set matching `chiasson.home.*.enable` toggles — no re-import in `home.nix`.
User apps / dotfiles → wisdom. Daemons, firewall, kernel → NixOS. Sometimes both (LocalSend: HM installs, `systemLocalsend` opens the firewall).
@@ -76,9 +76,9 @@ SSH authentication has two independent sides:
| **Client** (connecting from) | identity private keys | "Here's proof I own key K" |
| **Server** (connecting to) | `authorized_keys` | "Do I trust key K?" |
A key is just a cryptographic identity — it's not bound to a host or a machine account name. The model is **one key per user account**: each catalog account (`olivier`, `server`, `builder`) has its own keypair in `modules/lib/ssh-inventory.nix`, regardless of which machine that account runs on. Concretely: when `olivier@14900k` sshes to `server@r5500`, OpenSSH routes via a `Match user server` block (generated from `chiasson.ssh.outbound.rbw.extraIdentities`) → `IdentityFile ~/.ssh/id_ed25519_server.pub` (written from `users.server.publicKey`) to ask the rbw agent for the `server` private key; r5500's `server` `authorized_keys` holds the matching server pubkey. olivier's catalog default sets `extraIdentities = [ "server" "builder" ]` so `ssh <account>@<host>` Just Works for every catalog role. We also support `from="ip,cidr"` constraints per account to scope which source IPs may authenticate as it.
A key is just a cryptographic identity — it's not bound to a host or a machine account name. The model is **one key per user account**: each catalog account (`olivier`, `server`, `builder`) has its own keypair in `modules/ssh/inventory.nix`, regardless of which machine that account runs on. Concretely: when `olivier@14900k` sshes to `server@r5500`, OpenSSH routes via a `Match user server` block (generated from `chiasson.ssh.outbound.rbw.extraIdentities`) → `IdentityFile ~/.ssh/id_ed25519_server.pub` (written from `users.server.publicKey`) to ask the rbw agent for the `server` private key; r5500's `server` `authorized_keys` holds the matching server pubkey. olivier's catalog default sets `extraIdentities = [ "server" "builder" ]` so `ssh <account>@<host>` Just Works for every catalog role. We also support `from="ip,cidr"` constraints per account to scope which source IPs may authenticate as it.
This repo enforces "one key per user": every user has one ed25519 keypair, the private key in Bitwarden, the public key inline in `modules/lib/ssh-inventory.nix`. On SSH connect:
This repo enforces "one key per user": every user has one ed25519 keypair, the private key in Bitwarden, the public key inline in `modules/ssh/inventory.nix`. On SSH connect:
1. `~/.ssh/config` (managed by home-manager) sets `IdentityAgent SSH_AUTH_SOCK` and `IdentityFile ~/.ssh/id_ed25519_<user>.pub` for every host block.
2. OpenSSH reads the `.pub` file as a **filter** against the agent (OpenSSH 7.3+ trick — `.pub` as IdentityFile tells the agent "give me the private key matching this pub"), so even with multiple keys loaded, only the matching one is tried. This is why the old "too many auth failures" symptom is gone by construction.
@@ -105,7 +105,7 @@ ssh root@nix-server nixos-rebuild switch --flake .#nix-server
### Adding a host
Edit `modules/lib/ssh-inventory.nix` and add:
Edit `modules/ssh/inventory.nix` and add:
```nix
hosts."newhost" = {
@@ -119,7 +119,7 @@ Then add `self.nixosModules.<host>Configuration` to `modules/deploy/navi.nix`.
### Adding a catalog user
1. Generate a keypair: `rbw gen ssh-key <name>`.
2. Add a `users.<name> = { publicKey = "ssh-ed25519 …"; };` line under `users` in `modules/lib/ssh-inventory.nix`.
2. Add a `users.<name> = { publicKey = "ssh-ed25519 …"; };` line under `users` in `modules/ssh/inventory.nix`.
3. Add a `<name> = { isNormalUser = true; … ssh.inbound.enable = true; ssh.inbound.authorizedHosts = "all"; }` entry under `chiasson.users.catalog` in `modules/system/users.nix`.
4. Enable on every host that needs that user, e.g.:
@@ -172,7 +172,9 @@ The integration block at the bottom of `modules/system/users.nix` turns the cata
**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:** add `modules/wisdom/.../foo.nix` exporting `flake.homeManagerModules.wisdomFoo` (import-tree picks it up; `wisdomCatalogExtraModules` includes every `wisdom*` export except `wisdom` / `wisdomShellBash`). Gate packages on `chiasson.home.*.enable`, set `mkDefault true` in `desktop-home-base.nix` if shared, or `enable = true` in a host `home.nix`. Upstream HM deps stay imported unconditionally — use `mkIf` on `cfg.enable` for config (never `config`-dependent `imports`; that recurses).
**New HM slice:** add `modules/wisdom/.../foo.nix` exporting `flake.homeManagerModules.wisdomFoo` (import-tree picks it up; `wisdomCatalogExtraModules` includes every `wisdom*` export). Every `wisdom*` slice auto-catalogs — including the bash shell — so the root module no longer manually `imports` it. Gate packages on `chiasson.home.*.enable`, and set the slice's own `enable` default with `mkEnableOption "…" // { default = true/false }` in that same module — defaults live with the option that owns them, not in a shared defaults file. Hosts flip toggles in `home.nix`. Upstream HM deps stay imported unconditionally — use `mkIf` on `cfg.enable` for config (never `config`-dependent `imports`; that recurses).
**Trivial single-package slice:** use `self.lib.wisdomSlice { path = "a.b"; default = true/false; description = "…"; packages = pkgs: [ … ]; }` instead of the `root`/`cfg` + `mkIf (root.enable && cfg.enable)` boilerplate. `packages` is a `pkgs -> [ drv ]` function responsible for its own `availableOn`/platform gating. Several such slices may share one `simple.nix` in their category dir (each still exporting its own `wisdom*` module), rather than one file per slice.
**Derivations:** `let` inside a fragment, or `flake.packages` / `flake.lib` — not a bare `mkDerivation` file import-tree will try to load.
+32 -1
View File
@@ -98,9 +98,40 @@ let
metaNixpkgs = import inputs.nixpkgs {
system = "x86_64-linux";
};
# Build a raw Navi hive attrset from host specs + deployment targets.
# `inputs.navi.lib.makeHive` turns the result into `flake.naviHive`.
mkNaviHiveConfig =
{
metaNixpkgs,
hostSpecs,
deployments,
}:
let
deployNodes = lib.filterAttrs (name: _: deployments ? ${name}) hostSpecs;
in
{
flake.navi = self.lib.mkNaviHiveConfig {
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;
in
{
flake.navi = mkNaviHiveConfig {
inherit metaNixpkgs hostSpecs deployments;
};
+53 -12
View File
@@ -1,4 +1,38 @@
{ ... }: {
{ lib, ... }:
{
flake.lib.wisdomCatalogExtraModules =
self:
let
names = lib.sort builtins.lessThan (
lib.filter (n: lib.hasPrefix "wisdom" n && n != "wisdom") (
builtins.attrNames self.homeManagerModules
)
);
in
map (name: self.homeManagerModules.${name}) names;
flake.lib.wisdomSlice =
{ path, default, description, packages }:
{ config, lib, pkgs, ... }:
let
segs = lib.splitString "." path;
optPath = [
"chiasson"
"home"
]
++ segs
++ [ "enable" ];
root = config.chiasson.home;
cfg = lib.getAttrFromPath segs root;
enabled = lib.getAttrFromPath optPath config;
in
{
options = lib.setAttrByPath optPath (lib.mkEnableOption description // { inherit default; });
config = lib.mkIf (root.enable && enabled) {
home.packages = lib.filter (lib.meta.availableOn pkgs.stdenv.hostPlatform) (packages pkgs);
};
};
flake.nixosModules.desktopOptions =
{ config, options, lib, pkgs, self, inputs, ... }:
let
@@ -34,11 +68,13 @@
{
options.chiasson.desktop = {
defaultSession = lib.mkOption {
type = lib.types.nullOr (lib.types.enum [
type = lib.types.nullOr (
lib.types.enum [
"hyprland"
"niri"
"plasma"
]);
]
);
default = null;
example = "niri";
description = ''
@@ -56,10 +92,12 @@
displayManager = {
variant = lib.mkOption {
type = lib.types.nullOr (lib.types.enum [
type = lib.types.nullOr (
lib.types.enum [
"sddm"
"dankgreeter"
]);
]
);
default = null;
description = ''
SDDM vs DankGreeter (greetd + DMS [docs](https://danklinux.com/docs/dankgreeter/nixos-flake)).
@@ -80,9 +118,11 @@
};
displayManager.sddm = {
wayland.enable = lib.mkEnableOption ''
wayland.enable =
lib.mkEnableOption ''
SDDM greeter on Wayland (nicer on HiDPI; turn off if the greeter glitches on your GPU).
'' // {
''
// {
default = true;
};
@@ -159,10 +199,12 @@
};
keyring = {
enable = lib.mkEnableOption ''
enable =
lib.mkEnableOption ''
gnome-keyring + pam (login + sddm or greetd) + HM user service + `gcr` + `services.xserver.updateDbusEnvironment`.
niri/hyprland: `dbus-update-activation-environment` at compositor start so libsecret/Electron see `WAYLAND_DISPLAY`.
'' // {
''
// {
default = true;
};
};
@@ -184,8 +226,7 @@
message = "chiasson.desktop.defaultSession = \"plasma\" requires chiasson.desktop.plasma.enable = true.";
}
{
assertion =
cfg.displayManager.variant != "dankgreeter" || cfg.hyprland.enable || cfg.niri.enable;
assertion = cfg.displayManager.variant != "dankgreeter" || cfg.hyprland.enable || cfg.niri.enable;
message = "chiasson.desktop.displayManager.variant = \"dankgreeter\" requires chiasson.desktop.hyprland or chiasson.desktop.niri.";
}
{
@@ -210,7 +251,7 @@
(lib.mkIf (dmsEnabled && hmAvailable) {
"home-manager".sharedModules = [ self.homeManagerModules.desktopShellDms ];
})
(lib.mkIf (hmAvailable && (dmsEnabled || cfg.niri.enable)) {
(lib.mkIf hmAvailable {
"home-manager".extraSpecialArgs = { inherit inputs; };
})
];
+1 -1
View File
@@ -7,5 +7,5 @@
# Replace the old acceleration line with this:
package = pkgs.ollama-cuda;
};
chiasson.users.extraModules.olivier = [{chiasson.home.extraPackages = with pkgs; [ zed-editor windsurf];}];
chiasson.users.extraModules.olivier = [{chiasson.home.extraPackages = with pkgs; [ zed-editor devin-desktop];}];
}
+22 -53
View File
@@ -1,25 +1,15 @@
# Monitor layout for 14900k.
# NVIDIA (default): DP-3 ultrawide, HDMI-A-3 + DP-4 side/top stack; DP-2 virtual 4K when streamingDisplay enabled.
# Intel iGPU (gpu passthru): DP-1 ultrawide, HDMI-A-2 Samsung to the left.
# Niri: `extraSettings` (KDL via `extraConfig`, other wrapper-modules keys) + `extraBinds` (merged keybinds).
# Hyprland: `chiasson.desktop.hyprland.settings` (merged in HM when `chiasson.desktop.hyprland.enable`).
# NVIDIA (default): DP-3 ultrawide, HDMI-A-3 + DP-4 stack; DP-2 virtual 4K when streamingDisplay is on.
# Intel iGPU (gpu passthru): DP-1 ultrawide, HDMI-A-2 to the left.
# Niri uses `extraSettings.extraConfig`; Hyprland uses `chiasson.desktop.hyprland.settings`.
#TODO[epic=Moderate] Clean this up, move to host's configuration.nix.
{ config, lib, ... }:
let
streamingDisplay = config.chiasson.system.streamingDisplay;
gpuPassthru = config.chiasson.system.gpuPassthru.enable;
streamingDisplay = config.chiasson.system.streamingDisplay.enable or false;
streamingCfg = config.chiasson.system.streamingDisplay or { };
virtualOutputNiri =
lib.optionalString streamingDisplay ''
output "${streamingCfg.connector}" {
mode "${streamingCfg.mode}"
scale ${toString (streamingCfg.niriScale or 1.0)}
position ${streamingCfg.niriPosition}
}
'';
# Niri outputs: a static block per GPU layout, plus the optional virtual 4K panel.
niriOutputs =
(if gpuPassthru then
''
@@ -54,48 +44,30 @@ let
position x=0 y=-1080
}
'')
+ virtualOutputNiri;
virtualOutputHyprland =
lib.optionalString streamingDisplay
"${streamingCfg.connector}, ${streamingCfg.mode}, 7680x0, ${toString (streamingCfg.niriScale or 1.0)}";
+ lib.optionalString streamingDisplay.enable ''
output "${streamingDisplay.connector}" {
mode "${streamingDisplay.mode}"
scale ${toString streamingDisplay.niriScale}
position ${streamingDisplay.niriPosition}
}
'';
# Hyprland monitor strings, with the virtual panel appended when enabled.
hyprlandMonitors =
(if gpuPassthru then
[
"DP-1, 2560x1080@144, 0x0, 1"
"HDMI-A-2, 1920x1080@60, -1920x0, 1"
]
[ "DP-1, 2560x1080@144, 0x0, 1" "HDMI-A-2, 1920x1080@60, -1920x0, 1" ]
else
[
"DP-3, 2560x1080@144, 0x0, 1"
"DP-4, 1920x1080@144, 0x-1080, 1"
"HDMI-A-3, 1920x1080@60, -1920x0, 1"
])
++ lib.optional streamingDisplay virtualOutputHyprland;
[ "DP-3, 2560x1080@144, 0x0, 1" "DP-4, 1920x1080@144, 0x-1080, 1" "HDMI-A-3, 1920x1080@60, -1920x0, 1" ])
++ lib.optional streamingDisplay.enable "${streamingDisplay.connector}, ${streamingDisplay.mode}, 7680x0, ${toString streamingDisplay.niriScale}";
hyprlandWorkspaces =
if gpuPassthru then
[
"1, monitor:DP-1, default:true"
"2, monitor:DP-1"
"3, monitor:DP-1"
"4, monitor:HDMI-A-2, default:true"
"5, monitor:HDMI-A-2"
"6, monitor:HDMI-A-2"
]
[ "1, monitor:DP-1, default:true" "2, monitor:DP-1" "3, monitor:DP-1"
"4, monitor:HDMI-A-2, default:true" "5, monitor:HDMI-A-2" "6, monitor:HDMI-A-2" ]
else
[
"1, monitor:DP-3, default:true"
"2, monitor:DP-3"
"3, monitor:DP-3"
"4, monitor:HDMI-A-3, default:true"
"5, monitor:HDMI-A-3"
"6, monitor:HDMI-A-3"
"7, monitor:DP-4"
"8, monitor:DP-4"
"9, monitor:DP-4"
];
[ "1, monitor:DP-3, default:true" "2, monitor:DP-3" "3, monitor:DP-3"
"4, monitor:HDMI-A-3, default:true" "5, monitor:HDMI-A-3" "6, monitor:HDMI-A-3"
"7, monitor:DP-4" "8, monitor:DP-4" "9, monitor:DP-4" ];
in
{
chiasson.desktop.niri.extraSettings = {
@@ -103,10 +75,7 @@ in
};
chiasson.desktop.niri.extraBinds."XF86Tools".spawn = [
"wpctl"
"set-mute"
"@DEFAULT_AUDIO_SOURCE@"
"toggle"
"wpctl" "set-mute" "@DEFAULT_AUDIO_SOURCE@" "toggle"
];
chiasson.desktop.hyprland.settings = lib.mkIf config.chiasson.desktop.hyprland.enable {
@@ -39,7 +39,8 @@
"hassUrl": "https://home.chiasson.cloud/",
"hassTokenPath": "/run/secrets/home-assistant/auth-token",
"showButtonsOnStatusBar": true,
"showAttributes": false
"showAttributes": false,
"haRefreshCounter": 1
},
"unifiedTaskbar": {
"enabled": true,
@@ -4,11 +4,14 @@
"customThemeFile": "",
"registryThemeVariants": {},
"matugenScheme": "scheme-tonal-spot",
"matugenContrast": 0,
"runUserMatugenTemplates": true,
"matugenTargetMonitor": "",
"popupTransparency": 1,
"dockTransparency": 1,
"widgetBackgroundColor": "sch",
"widgetBackgroundCustomColor": "#6750A4",
"widgetBackgroundCustomStrength": 0.5,
"widgetColorMode": "default",
"controlCenterTileColorMode": "primary",
"buttonColorMode": "primary",
@@ -17,11 +20,18 @@
"niriLayoutRadiusOverride": -1,
"niriLayoutBorderSize": -1,
"hyprlandLayoutGapsOverride": -1,
"hyprlandLayoutGapsOutOverride": -1,
"hyprlandLayoutRadiusOverride": -1,
"hyprlandLayoutBorderSize": -1,
"hyprlandResizeOnBorder": false,
"mangoLayoutGapsOverride": -1,
"mangoLayoutGapsOutOverride": -1,
"mangoLayoutRadiusOverride": -1,
"mangoLayoutBorderSize": -1,
"mangoTrackpadNaturalScrolling": true,
"firstDayOfWeek": -1,
"showWeekNumber": false,
"calendarBackend": "auto",
"use24HourClock": false,
"showSeconds": true,
"padHours12Hour": false,
@@ -36,6 +46,17 @@
"modalAnimationSpeed": 1,
"modalCustomAnimationDuration": 150,
"enableRippleEffects": true,
"animationVariant": 0,
"motionEffect": 0,
"m3ElevationEnabled": true,
"m3ElevationIntensity": 12,
"m3ElevationOpacity": 30,
"m3ElevationColorMode": "default",
"m3ElevationLightDirection": "top",
"m3ElevationCustomColor": "#000000",
"modalElevationEnabled": true,
"popoutElevationEnabled": true,
"barElevationEnabled": false,
"blurEnabled": false,
"blurForegroundLayers": true,
"blurLayerOutlineOpacity": 0.12,
@@ -45,6 +66,8 @@
"wallpaperFillMode": "Fill",
"blurredWallpaperLayer": false,
"blurWallpaperOnOverview": false,
"wallpaperBackgroundColorMode": "black",
"wallpaperBackgroundCustomColor": "#000000",
"showLauncherButton": true,
"showWorkspaceSwitcher": true,
"showFocusedWindow": true,
@@ -64,6 +87,12 @@
"showClock": true,
"showNotificationButton": true,
"showBattery": true,
"showBatteryPercent": true,
"showBatteryPercentOnlyOnBattery": false,
"showBatteryTime": false,
"showBatteryTimeOnlyOnBattery": false,
"batteryPillStyle": false,
"batteryPillPercentSign": false,
"showControlCenterButton": true,
"showCapsLockIndicator": true,
"controlCenterShowNetworkIcon": true,
@@ -78,6 +107,8 @@
"controlCenterShowBatteryIcon": false,
"controlCenterShowPrinterIcon": false,
"controlCenterShowScreenSharingIcon": true,
"controlCenterShowIdleInhibitorIcon": false,
"controlCenterShowDoNotDisturbIcon": false,
"showPrivacyButton": true,
"privacyShowMicIcon": false,
"privacyShowCameraIcon": false,
@@ -133,29 +164,57 @@
"maxWorkspaceIcons": 9,
"workspaceAppIconSizeOffset": 1,
"groupWorkspaceApps": true,
"groupActiveWorkspaceApps": false,
"workspaceFollowFocus": false,
"showOccupiedWorkspacesOnly": true,
"reverseScrolling": false,
"dwlShowAllTags": false,
"workspaceActiveAppHighlightEnabled": false,
"workspaceColorMode": "default",
"workspaceFocusedCustomColor": "#6750A4",
"workspaceOccupiedColorMode": "none",
"workspaceOccupiedCustomColor": "#625B71",
"workspaceUnfocusedColorMode": "default",
"workspaceUnfocusedCustomColor": "#49454E",
"workspaceUrgentColorMode": "default",
"workspaceUrgentCustomColor": "#B3261E",
"workspaceFocusedBorderEnabled": false,
"workspaceFocusedBorderColor": "primary",
"workspaceFocusedBorderCustomColor": "#6750A4",
"workspaceFocusedBorderThickness": 2,
"workspaceUnfocusedMonitorSeparateAppearance": false,
"workspaceUnfocusedMonitorColorMode": "default",
"workspaceUnfocusedMonitorFocusedCustomColor": "#6750A4",
"workspaceUnfocusedMonitorOccupiedColorMode": "none",
"workspaceUnfocusedMonitorOccupiedCustomColor": "#625B71",
"workspaceUnfocusedMonitorUnfocusedColorMode": "default",
"workspaceUnfocusedMonitorUnfocusedCustomColor": "#49454E",
"workspaceUnfocusedMonitorUrgentColorMode": "default",
"workspaceUnfocusedMonitorUrgentCustomColor": "#B3261E",
"workspaceUnfocusedMonitorBorderEnabled": false,
"workspaceUnfocusedMonitorBorderColor": "primary",
"workspaceUnfocusedMonitorBorderCustomColor": "#6750A4",
"workspaceUnfocusedMonitorBorderThickness": 2,
"workspaceNameIcons": {},
"waveProgressEnabled": true,
"scrollTitleEnabled": true,
"mediaAdaptiveWidthEnabled": true,
"audioVisualizerEnabled": true,
"audioScrollMode": "volume",
"audioWheelScrollAmount": 5,
"audioDeviceScrollVolumeEnabled": false,
"mediaExcludePlayers": [],
"clockCompactMode": false,
"focusedWindowCompactMode": false,
"focusedWindowSize": 1,
"focusedWindowShowIcon": true,
"runningAppsCompactMode": true,
"barMaxVisibleApps": 0,
"barMaxVisibleRunningApps": 0,
"barShowOverflowBadge": true,
"trayAutoOverflow": true,
"trayPopupSingleLine": true,
"trayMaxVisibleItems": 0,
"appsDockHideIndicators": false,
"appsDockColorizeActive": false,
"appsDockActiveColorMode": "primary",
@@ -163,6 +222,7 @@
"appsDockEnlargePercentage": 125,
"appsDockIconSizePercentage": 100,
"keyboardLayoutNameCompactMode": false,
"keyboardLayoutNameShowIcon": false,
"runningAppsCurrentWorkspace": true,
"runningAppsGroupByApp": false,
"runningAppsCurrentMonitor": false,
@@ -198,9 +258,18 @@
"lockDateFormat": "dddd, MMMM d",
"greeterRememberLastSession": true,
"greeterRememberLastUser": true,
"greeterAutoLogin": false,
"greeterEnableFprint": false,
"greeterEnableU2f": false,
"greeterWallpaperPath": "",
"greeterUse24HourClock": true,
"greeterShowSeconds": false,
"greeterPadHours12Hour": false,
"greeterLockDateFormat": "",
"greeterFontFamily": "",
"greeterWallpaperFillMode": "",
"greeterSyncPending": false,
"greeterSyncBaseline": {},
"mediaSize": 1,
"appLauncherViewMode": "list",
"spotlightModalViewMode": "list",
@@ -211,19 +280,53 @@
"sortAppsAlphabetically": false,
"appLauncherGridColumns": 4,
"spotlightCloseNiriOverview": true,
"rememberLastQuery": false,
"rememberLastMode": true,
"spotlightSectionViewModes": {},
"appDrawerSectionViewModes": {},
"niriOverviewOverlayEnabled": true,
"dankLauncherV2Size": "compact",
"dankLauncherV2Size": "medium",
"dankLauncherV2ShowSourceBadges": true,
"dankLauncherV2BorderEnabled": false,
"dankLauncherV2BorderThickness": 2,
"dankLauncherV2BorderColor": "primary",
"dankLauncherV2ShowFooter": true,
"dankLauncherV2UnloadOnClose": false,
"dankLauncherV2UnloadOnClose": true,
"dankLauncherV2IncludeFilesInAll": false,
"dankLauncherV2IncludeFoldersInAll": false,
"launcherUseOverlayLayer": false,
"launcherStyle": "spotlight",
"spotlightBarShowModeChips": true,
"keybindsFloatingWindow": false,
"useAutoLocation": false,
"weatherEnabled": true,
"networkPreference": "auto",
"iconTheme": "WhiteSur-dark",
"dashTabs": [
{
"id": "overview",
"enabled": true
},
{
"id": "media",
"enabled": true
},
{
"id": "wallpaper",
"enabled": true
},
{
"id": "weather",
"enabled": true
},
{
"id": "settings",
"enabled": true
}
],
"networkPreference": "ethernet",
"iconThemeDark": "System Default",
"iconThemeLight": "System Default",
"iconThemePerMode": false,
"lastAppliedIconTheme": "",
"cursorSettings": {
"theme": "System Default",
"size": 24,
@@ -240,9 +343,9 @@
"cursorHideTimeout": 0
}
},
"launcherLogoMode": "apps",
"launcherLogoMode": "os",
"launcherLogoCustomPath": "",
"launcherLogoColorOverride": "",
"launcherLogoColorOverride": "primary",
"launcherLogoColorInvertOnMode": false,
"launcherLogoBrightness": 0.5,
"launcherLogoContrast": 1,
@@ -251,28 +354,50 @@
"monoFontFamily": "Fira Code",
"fontWeight": 400,
"fontScale": 1,
"textRenderType": 0,
"textRenderQuality": 0,
"notepadUseMonospace": true,
"notepadFontFamily": "",
"notepadFontSize": 14,
"notificationSummaryFontSize": 0,
"notificationBodyFontSize": 0,
"notepadShowLineNumbers": false,
"notepadAutoSave": false,
"notepadSlideoutSide": "right",
"notepadDefaultMode": "slideout",
"notepadTransparencyOverride": -1,
"notepadLastCustomTransparency": 0.7,
"notepadUseCompositorGap": false,
"notepadEdgeGap": 0,
"soundsEnabled": true,
"useSystemSoundTheme": false,
"soundLogin": false,
"soundNewNotification": true,
"soundVolumeChanged": true,
"soundPluggedIn": true,
"muteSoundsWhenMediaPlaying": true,
"acMonitorTimeout": 0,
"acLockTimeout": 0,
"acSuspendTimeout": 0,
"acSuspendBehavior": 0,
"acProfileName": "",
"acPostLockMonitorTimeout": 0,
"batteryMonitorTimeout": 0,
"batteryLockTimeout": 0,
"batterySuspendTimeout": 0,
"batterySuspendBehavior": 0,
"batteryProfileName": "",
"batteryPostLockMonitorTimeout": 0,
"batteryChargeLimit": 100,
"batteryNotifyChargeLimit": false,
"batteryCriticalThreshold": 10,
"batteryNotifyCritical": true,
"batteryLowThreshold": 20,
"batteryNotifyLow": false,
"batteryChargeLimitNotificationType": 0,
"batteryLowNotificationType": 0,
"batteryCriticalNotificationType": 1,
"batteryAutoPowerSaver": false,
"lockBeforeSuspend": false,
"loginctlLockIntegration": true,
"fadeToLockEnabled": true,
@@ -304,6 +429,7 @@
"matugenTemplatePywalfox": true,
"matugenTemplateZenBrowser": true,
"matugenTemplateVesktop": false,
"matugenTemplateVencord": true,
"matugenTemplateEquibop": true,
"matugenTemplateGhostty": true,
"matugenTemplateKitty": true,
@@ -316,10 +442,23 @@
"matugenTemplateVscode": true,
"matugenTemplateEmacs": true,
"matugenTemplateZed": true,
"matugenTemplateNeovimSettings": {
"dark": {
"baseTheme": "github_dark",
"harmony": 0.5
},
"light": {
"baseTheme": "github_light",
"harmony": 0.5
}
},
"matugenTemplateNeovimSetBackground": true,
"showDock": false,
"dockAutoHide": false,
"dockSmartAutoHide": false,
"dockUseOverlayLayer": false,
"dockGroupByApp": false,
"dockRestoreSpecialWorkspaceOnClick": false,
"dockOpenOnOverview": false,
"dockPosition": 1,
"dockSpacing": 4,
@@ -342,6 +481,9 @@
"dockMaxVisibleApps": 0,
"dockMaxVisibleRunningApps": 0,
"dockShowOverflowBadge": true,
"dockShowTrash": false,
"dockTrashFileManager": "default",
"dockTrashCustomCommand": "",
"notificationOverlayEnabled": false,
"notificationPopupShadowEnabled": true,
"notificationPopupPrivacyMode": false,
@@ -359,14 +501,21 @@
"maxFprintTries": 15,
"enableU2f": false,
"u2fMode": "or",
"lockScreenActiveMonitor": "all",
"lockScreenInactiveColor": "#000000",
"lockScreenNotificationMode": 0,
"lockScreenVideoEnabled": false,
"lockScreenVideoPath": "",
"lockScreenVideoCycling": false,
"lockScreenWallpaperPath": "",
"lockScreenWallpaperFillMode": "",
"lockScreenFontFamily": "",
"hideBrightnessSlider": false,
"notificationTimeoutLow": 5000,
"notificationTimeoutNormal": 5000,
"notificationTimeoutCritical": 0,
"notificationCompactMode": false,
"notificationShowTimeoutBar": false,
"notificationDedupeEnabled": true,
"notificationPopupPosition": 0,
"notificationAnimationSpeed": 1,
"notificationCustomAnimationDuration": 400,
@@ -377,6 +526,7 @@
"notificationHistorySaveNormal": true,
"notificationHistorySaveCritical": true,
"notificationRules": [],
"notificationFocusedMonitor": false,
"osdAlwaysShowValue": false,
"osdPosition": 5,
"osdVolumeEnabled": true,
@@ -407,9 +557,13 @@
"customPowerActionReboot": "",
"customPowerActionPowerOff": "",
"updaterHideWidget": false,
"updaterCheckOnStart": false,
"updaterUseCustomCommand": false,
"updaterCustomCommand": "",
"updaterTerminalAdditionalParams": "",
"updaterIntervalSeconds": 1800,
"updaterIncludeFlatpak": true,
"updaterAllowAUR": true,
"displayNameMode": "system",
"screenPreferences": {},
"showOnLastDisplay": {},
@@ -420,6 +574,7 @@
"displayProfileAutoSelect": false,
"displayShowDisconnected": false,
"displaySnapToEdge": true,
"connectedFrameBarStyleBackups": {},
"barConfigs": [
{
"bottomGap": 0,
@@ -486,10 +641,6 @@
{
"name": "DP-3",
"model": "34GL750"
},
{
"name": "Virtual-1",
"model": "Virtual-1"
}
],
"showOnLastDisplay": false,
@@ -517,7 +668,7 @@
"id": "gpuTemp",
"enabled": true,
"selectedGpuIndex": 0,
"pciId": "",
"pciId": "10de:1f07",
"minimumWidth": true
},
{
@@ -685,6 +836,14 @@
"desktopWidgetPositions": {},
"desktopWidgetGridSettings": {
"DP-3": {
"enabled": true,
"size": 40
},
"HDMI-A-3": {
"enabled": true,
"size": 50
},
"DP-4": {
"enabled": true,
"size": 50
}
@@ -696,7 +855,7 @@
"name": "Desktop Clock",
"enabled": true,
"config": {
"style": "analog",
"style": "digital",
"transparency": 0.8,
"colorMode": "primary",
"customColor": "#ffffff",
@@ -704,347 +863,48 @@
"showAnalogNumbers": false,
"showAnalogSeconds": true,
"displayPreferences": [
"all"
]
},
"positions": {
"DP-3": {
"width": 300,
"height": 300,
"x": 1550,
"y": 50
},
"DP-4": {
"width": 200,
"height": 200,
"x": 860,
"y": 440
},
"HDMI-A-3": {
"width": 200,
"height": 200,
"x": 1708.3671875,
"y": 4.84765625
}
}
{
"name": "DP-2",
"model": "SUN-4K"
},
{
"id": "dw_1782021988600_vdg97np4g",
"widgetType": "systemMonitor",
"name": "System Monitor",
"enabled": true,
"config": {
"showHeader": true,
"transparency": 0.8,
"colorMode": "primary",
"customColor": "#ffffff",
"showCpu": true,
"showCpuGraph": true,
"showCpuTemp": true,
"showGpuTemp": false,
"gpuPciId": "",
"showMemory": true,
"showMemoryGraph": true,
"showNetwork": true,
"showNetworkGraph": true,
"showDisk": true,
"showTopProcesses": false,
"topProcessCount": 3,
"topProcessSortBy": "cpu",
"layoutMode": "auto",
"graphInterval": 60,
"displayPreferences": [
"all"
]
"name": "DP-3",
"model": "34GL750"
},
{
"name": "DP-4",
"model": "ZOWIE XL LCD"
}
],
"showDigitalSeconds": false,
"showOnOverlay": false,
"showOnOverviewOnly": false,
"showOnOverview": false
},
"positions": {
"DP-3": {
"width": 500,
"width": 240,
"height": 240,
"x": 2240,
"y": 40
},
"DP-4": {
"width": 240.484375,
"height": 240.484375,
"x": 1350,
"y": 50
},
"HDMI-A-3": {
"width": 250,
"height": 250,
"x": 2000,
"x": 1650,
"y": 50
},
"DP-4": {
"width": 320,
"height": 480,
"x": 800,
"y": 300
},
"HDMI-A-3": {
"width": 320,
"height": 480,
"x": 1595.0546875,
"y": 556.515625
}
}
},
{
"id": "dw_1782022081429_08ufxr98h",
"widgetType": "appShortcut",
"name": "Application Shortcut: Obsidian",
"enabled": true,
"config": {
"displayPreferences": [
"all"
],
"showOnOverlay": false,
"showOnOverview": false,
"showOnOverviewOnly": false,
"clickThrough": false,
"shortcutApp": "obsidian",
"followTheme": false,
"drawOutlines": false,
"syncPositionAcrossScreens": true
},
"positions": {
"DP-3": {
"width": 100,
"height": 100,
"x": 150,
"y": 200
},
"DP-4": {
"DP-2": {
"width": 200,
"height": 200,
"x": 860,
"y": 440
},
"HDMI-A-3": {
"width": 200,
"height": 200,
"x": 860,
"y": 440
},
"_synced": {
"x": 0.05859375,
"y": 0.18518518518518517,
"width": 100,
"height": 100
}
}
},
{
"id": "dw_1782022155947_1a4381zan",
"widgetType": "appShortcut",
"name": "Application Shortcut: Steam",
"enabled": true,
"config": {
"displayPreferences": [
"all"
],
"showOnOverlay": false,
"showOnOverview": false,
"showOnOverviewOnly": false,
"clickThrough": false,
"shortcutApp": "steam",
"followTheme": false,
"drawOutlines": false,
"syncPositionAcrossScreens": true
},
"positions": {
"DP-3": {
"width": 100,
"height": 100,
"x": 50,
"y": 900
},
"DP-4": {
"width": 200,
"height": 200,
"x": 860,
"y": 440
},
"HDMI-A-3": {
"width": 200,
"height": 200,
"x": 860,
"y": 440
},
"_synced": {
"x": 0.01953125,
"y": 0.8333333333333334,
"width": 100,
"height": 100
}
}
},
{
"id": "dw_1782022159047_a0w7xwkkz",
"widgetType": "appShortcut",
"name": "Application Shortcut: Vesktop",
"enabled": true,
"config": {
"displayPreferences": [
"all"
],
"showOnOverlay": false,
"showOnOverview": false,
"showOnOverviewOnly": false,
"clickThrough": false,
"shortcutApp": "vesktop",
"followTheme": false,
"drawOutlines": false,
"syncPositionAcrossScreens": true
},
"positions": {
"DP-3": {
"width": 100,
"height": 100,
"x": 50,
"y": 200
},
"DP-4": {
"width": 200,
"height": 200,
"x": 860,
"y": 440
},
"HDMI-A-3": {
"width": 200,
"height": 200,
"x": 860,
"y": 440
},
"_synced": {
"x": 0.01953125,
"y": 0.18518518518518517,
"width": 100,
"height": 100
}
}
},
{
"id": "dw_1782022160265_i4vr22k1v",
"widgetType": "appShortcut",
"name": "Application Shortcut: PrismLauncher",
"enabled": true,
"config": {
"displayPreferences": [
"all"
],
"showOnOverlay": false,
"showOnOverview": false,
"showOnOverviewOnly": false,
"clickThrough": false,
"shortcutApp": "org.prismlauncher.PrismLauncher",
"followTheme": false,
"drawOutlines": false,
"syncPositionAcrossScreens": true
},
"positions": {
"DP-3": {
"width": 100,
"height": 100,
"x": 250,
"y": 900
},
"DP-4": {
"width": 200,
"height": 200,
"x": 860,
"y": 440
},
"HDMI-A-3": {
"width": 200,
"height": 200,
"x": 860,
"y": 440
},
"_synced": {
"x": 0.09765625,
"y": 0.8333333333333334,
"width": 100,
"height": 100
}
}
},
{
"id": "dw_1782022161442_3vfjq50gk",
"widgetType": "appShortcut",
"name": "Application Shortcut: Pokeclicker",
"enabled": true,
"config": {
"displayPreferences": [
"all"
],
"showOnOverlay": false,
"showOnOverview": false,
"showOnOverviewOnly": false,
"clickThrough": false,
"shortcutApp": "pokeclicker-desktop-with-scripts",
"followTheme": false,
"drawOutlines": false,
"syncPositionAcrossScreens": true
},
"positions": {
"DP-3": {
"width": 100,
"height": 100,
"x": 150,
"y": 900
},
"DP-4": {
"width": 200,
"height": 200,
"x": 860,
"y": 440
},
"HDMI-A-3": {
"width": 200,
"height": 200,
"x": 860,
"y": 440
},
"_synced": {
"x": 0.05859375,
"y": 0.8333333333333334,
"width": 100,
"height": 100
}
}
},
{
"id": "dw_1782022229421_fuo32iz2s",
"widgetType": "appShortcut",
"name": "Application Shortcut: Edge",
"enabled": true,
"config": {
"displayPreferences": [
"all"
],
"showOnOverlay": false,
"showOnOverview": false,
"showOnOverviewOnly": false,
"clickThrough": false,
"shortcutApp": "microsoft-edge",
"followTheme": false,
"drawOutlines": false,
"syncPositionAcrossScreens": true
},
"positions": {
"DP-3": {
"width": 150,
"height": 150,
"x": 50,
"y": 50
},
"DP-4": {
"width": 200,
"height": 200,
"x": 860,
"y": 440
},
"HDMI-A-3": {
"width": 200,
"height": 200,
"x": 860,
"y": 440
},
"_synced": {
"x": 0.01953125,
"y": 0.046296296296296294,
"width": 150,
"height": 150
"x": 412,
"y": 284
}
}
},
@@ -1052,7 +912,7 @@
"id": "dw_1782022922490_aiumfxf49",
"widgetType": "cavaVisualizer",
"name": "Cava Visualizer",
"enabled": true,
"enabled": false,
"config": {
"displayPreferences": [
"all"
@@ -1087,8 +947,8 @@
"y": 440
},
"_synced": {
"x": 0.2734375,
"y": 0.5092592592592593,
"x": 0.27313639322916666,
"y": 0.5089156539351852,
"width": 1100,
"height": 500
}
@@ -1101,16 +961,19 @@
"enabled": true,
"config": {
"displayPreferences": [
"all"
{
"name": "DP-4",
"model": "ZOWIE XL LCD"
}
],
"syncPositionAcrossScreens": true,
"viewMode": "detailed",
"viewMode": "compact",
"colorMode": "primary",
"backgroundOpacity": 80,
"showPressure": true,
"forecastDays": 7,
"showHourlyForecast": true,
"hourlyCount": 6,
"showHourlyForecast": false,
"hourlyCount": 12,
"showLocation": false,
"showCondition": true,
"showFeelsLike": true
@@ -1135,17 +998,95 @@
"y": 440
},
"_synced": {
"x": 0.78125,
"y": 0.32407407407407407,
"width": 500,
"height": 650
"x": 0.859375,
"y": 0.046296296296296294,
"width": 250,
"height": 250
}
}
},
{
"id": "dw_1782021988600_vdg97np4g",
"widgetType": "systemMonitor",
"name": "System Monitor",
"enabled": true,
"config": {
"showHeader": true,
"transparency": 0.8,
"colorMode": "primary",
"customColor": "#ffffff",
"showCpu": true,
"showCpuGraph": true,
"showCpuTemp": true,
"showGpuTemp": false,
"gpuPciId": "",
"showMemory": true,
"showMemoryGraph": true,
"showNetwork": true,
"showNetworkGraph": true,
"showDisk": true,
"showTopProcesses": false,
"topProcessCount": 3,
"topProcessSortBy": "cpu",
"layoutMode": "auto",
"graphInterval": 300,
"displayPreferences": [
{
"name": "DP-2",
"model": "SUN-4K"
},
{
"name": "DP-4",
"model": "ZOWIE XL LCD"
}
]
},
"positions": {
"DP-3": {
"width": 800,
"height": 350,
"x": 1650,
"y": 650
},
"DP-4": {
"width": 800,
"height": 350,
"x": 1100,
"y": 650
},
"HDMI-A-3": {
"width": 700,
"height": 350,
"x": 1200,
"y": 650
},
"DP-2": {
"width": 320,
"height": 480,
"x": 352,
"y": 144
}
}
}
],
"desktopWidgetGroups": [],
"builtInPluginSettings": {},
"builtInPluginSettings": {
"dms_settings_search": {
"trigger": "?"
},
"dms_clipboard_search": {
"trigger": "cb"
}
},
"clipboardClickToPaste": false,
"clipboardEnterToPaste": false,
"clipboardRememberTypeFilter": false,
"clipboardTypeFilter": "all",
"clipboardVisibleEntryActions": [
"pin",
"edit",
"delete"
],
"launcherPluginVisibility": {
"obsidianSearch": {
"allowWithoutTrigger": false
@@ -1155,5 +1096,24 @@
}
},
"launcherPluginOrder": [],
"configVersion": 5
"frameEnabled": false,
"frameThickness": 100,
"frameRounding": 100,
"frameColor": "surface",
"frameOpacity": 0.69,
"frameScreenPreferences": [
"all"
],
"frameBarSize": 100,
"frameShowOnOverview": false,
"frameBlurEnabled": true,
"frameCloseGaps": false,
"frameLauncherEmergeSide": "bottom",
"frameLauncherArcExtender": true,
"frameLauncherEdgeHover": true,
"frameMode": "connected",
"barInsetPaddingShared": -1,
"barInsetPaddingSyncAll": false,
"frameBarInsetPadding": 26,
"configVersion": 12
}
@@ -1,9 +1,5 @@
# Extra local disks. Declared here, not in hardware.nix (hardware.nix is generated).
{ config, lib, ... }:
let
# Stable UID so NTFS `uid=` matches `users.users.olivier` (override if your account is not 1000).
olivierUid = config.users.users.olivier.uid or 1000;
in
{
users.users.olivier.uid = lib.mkDefault 1000;
# LABEL="MediaLibrary" (btrfs on sda1 by UUID). No subvol=@ — this disk has no @ subvolume.
@@ -33,15 +29,4 @@ in
# LABEL="Deep Storage Unit". Owner olivier, group nfsmedia (990) so:
# - local logins write as user 1000 (owner rwx);
# - NFS (all_squash → uid/gid 990) matches group 990 → rwx (see jellyfin-nfs-export).
#fileSystems."/mnt/test" = {
# device = "/dev/disk/by-uuid/BC12E55E12E51DE0";
# fsType = "ntfs-3g";
# options = [
# "rw"
# "force"
# "uid=${toString olivierUid}"
# "gid=990"
# "umask=0002"
# ];
#};
}
@@ -1,14 +1,12 @@
# Headless 4K virtual output for Sunshine → Moonlight on a client TV.
#
# NVIDIA proprietary driver: both kernel params are required —
# NVIDIA: force-enable a spare DRM connector with a custom EDID via kernel params:
# video=<connector>:e force-enables the connector
# drm.edid_firmware=<connector>:… loads modes from the EDID blob
#
# Pick a connector that is disconnected on the host (check after boot):
# grep -H . /sys/class/drm/card*-*/status
#
# Reboot after changing connector or EDID. 4K@60 is realistic on RTX 2070;
# 4K@120 on a sink-less connector needs driver patches (see NVIDIA issue #1184).
# Capture is pinned to the connector by name (Sunshine `output_name`), which is
# stable across reboots/hotplug — unlike a numeric monitor index.
# Reboot after changing connector or EDID. 4K@60 is realistic on RTX 2070.
{ config, lib, pkgs, ... }:
let
cfg = config.chiasson.system.streamingDisplay;
@@ -16,14 +14,13 @@ let
edidFirmware = pkgs.runCommand "sunshine-virtual-4k-edid" {
nativeBuildInputs = [ pkgs.python3 ];
} ''
${pkgs.python3}/bin/python3 ${./generate-virtual-edid.py} $TMPDIR/${cfg.edidFileName}
mkdir -p $out/lib/firmware/edid
cp $TMPDIR/${cfg.edidFileName} $out/lib/firmware/edid/${cfg.edidFileName}
${pkgs.python3}/bin/python3 ${./generate-virtual-edid.py} $out/lib/firmware/edid/${cfg.edidFileName}
'';
# Sunshine 2025.924 wlr capture only accepts a numeric monitor index (not "DP-2").
# If the index drifts after hotplug, check:
# journalctl --user -u sunshine -b | rg 'Monitor [0-9]+ is'
# On stream start: scale + focus the virtual 4K output so streamed windows
# land there. Capture is pinned to the connector via Sunshine `output_name`,
# so no fragile numeric-index detection is needed.
streamDisplayUp = pkgs.writeShellScriptBin "stream-display-up" ''
${pkgs.niri}/bin/niri msg output ${cfg.connector} scale ${toString cfg.niriScale}
${pkgs.niri}/bin/niri msg action focus-monitor ${cfg.connector}
@@ -87,18 +84,6 @@ in
default = "DP-3";
description = "Niri monitor to refocus when a Moonlight session ends.";
};
sunshineOutputIndex = lib.mkOption {
type = lib.types.str;
default = "3";
example = "3";
description = ''
Wayland/wlr monitor index for Sunshine `output_name`. Sunshine 2025.924 ignores
connector strings like `DP-2` and falls back to monitor 0 (your primary).
Find the index with:
`journalctl --user -u sunshine -b | rg "Monitor [0-9]+ is ${cfg.connector}"`
'';
};
};
config = lib.mkIf cfg.enable (lib.mkMerge [
@@ -118,7 +103,11 @@ in
(lib.mkIf config.chiasson.system.gaming.sunshine.enable {
services.sunshine.settings = {
capture = "wlr";
output_name = cfg.sunshineOutputIndex;
# Pin capture to the virtual display by connector name (stable across
# reboots/hotplug). A numeric index drifted to the ultrawide before.
output_name = cfg.connector;
# RTX 2070 has no AV1 NVENC; avoid probing av1_nvenc.
encoder = "hevc_nvenc,h264_nvenc";
global_prep_cmd = "[{\"do\":\"${streamDisplayUp}/bin/stream-display-up\",\"undo\":\"${streamDisplayDown}/bin/stream-display-down\"}]";
};
})
-2
View File
@@ -4,8 +4,6 @@
system = "x86_64-linux";
specialArgs = {
inherit self inputs;
host = "14900k";
system = "x86_64-linux";
};
modules = [
self.nixosModules."14900kConfiguration"
+7
View File
@@ -23,6 +23,13 @@
pokeclicker.enable = true;
};
editors.vscode = {
enable = true;
extensions = with pkgs.vscode-extensions; [
alefragnani.project-manager
johnpapa.vscode-peacock
];
};
};
}
];
+1 -5
View File
@@ -17,10 +17,6 @@
userCli.enable = true;
};
chiasson.users.extraModules.olivier =
self.lib.wisdomCatalogExtraModules self
++ [
self.homeManagerModules.desktopHomeDefaults
];
chiasson.users.extraModules.olivier = self.lib.wisdomCatalogExtraModules self;
};
}
-77
View File
@@ -1,77 +0,0 @@
# Shared HM defaults for desktop hosts (git identity, wisdom catalog toggles).
{ ... }: {
flake.homeManagerModules.desktopHomeDefaults =
{ lib, pkgs, ... }:
let
aarch64 = pkgs.stdenv.hostPlatform.isAarch64;
in
{
programs.git = {
enable = lib.mkDefault true;
settings.user = {
name = "OlivierChiasson";
email = "olivierchiasson@hotmail.fr";
};
};
#TODO[epic=Moderate] Check if git-crypt should belong in desktop-home-base.
home.packages = [ pkgs.git-crypt pkgs.feishin ];
chiasson.home = {
shell = {
fish.enable = lib.mkDefault true;
ohMyPosh.enable = lib.mkDefault true;
yazi.enable = lib.mkDefault true;
};
terminals.kitty.enable = lib.mkDefault true;
filebrowsers.nemo.enable = lib.mkDefault true;
browsers = {
chrome.enable = lib.mkDefault false;
chromiumHevc = {
enable = lib.mkDefault true;
packages = lib.mkDefault (
if aarch64 then
[ "chromium" ]
else
[ "google-chrome" ]
);
} // lib.optionalAttrs (!aarch64) {
vaapi.gpu = lib.mkDefault "intel";
};
edge.enable = lib.mkDefault false;
flow.enable = lib.mkDefault false;
orion.enable = lib.mkDefault false;
zen.enable = lib.mkDefault false;
};
editors = {
cursor.enable = lib.mkDefault true;
kate.enable = lib.mkDefault false;
obsidian.enable = lib.mkDefault true;
};
apps = {
discord.enable = lib.mkDefault false;
localsend.enable = lib.mkDefault true;
pokeclicker.enable = lib.mkDefault false;
spotify = {
enable = lib.mkDefault false;
openDiscoveryFirewall = lib.mkDefault false;
};
};
desktop = {
screenshot = {
enable = lib.mkDefault true;
swiftshareApiKeyFile = "/run/secrets/swiftshare/API_KEY"; #TODO[epic=sops] redo this by passing sops file output directly
};
theming.enable = lib.mkDefault true;
};
hardware.uconsoleGamepad.enable = lib.mkDefault false;
};
};
}
+1 -6
View File
@@ -42,12 +42,7 @@
# rebuilds Mobile NixOS' script-loader against the patched mruby.
chiasson.system.ideapadMrubyOverlay.enable = true;
# Wi-Fi modem (qcom-wcn3990) + Bluetooth (QCA crnv32) need binary blobs.
nixpkgs.config.allowUnfreePredicate =
pkg: builtins.elem (lib.getName pkg) [
"chromeos-sc7180-unredistributable-firmware"
"chromeos-sc7180-unredistributable-firmware-zstd"
];
# Wi-Fi modem (qcom-wcn3990) + Bluetooth (QCA crnv32) need binary blobs
hardware.firmware = [ pkgs.chromeos-sc7180-unredistributable-firmware ];
hardware.enableRedistributableFirmware = true;
-2
View File
@@ -4,8 +4,6 @@
system = "aarch64-linux";
specialArgs = {
inherit self inputs;
host = "ideapad";
system = "aarch64-linux";
};
modules = [
self.nixosModules.ideapadConfiguration
-13
View File
@@ -4,18 +4,5 @@
{ self, ... }:
{
imports = [ self.nixosModules.desktopHomeBase ];
# Host-only HM overrides (tablet apps, toggles off from desktop-home-base, …).
chiasson.users.extraModules.olivier = [
# {
# chiasson.home = {
# # editors.kate.enable = true;
# };
# }
# (
# { pkgs, ... }:
# { home.packages = with pkgs; [ ]; }
# )
];
};
}
@@ -1,35 +0,0 @@
# NFS mounts of nixdesk (14900k) bulk storage for nix-server. Exports live in
# modules/hosts/14900k/_private/jellyfin-nfs-export.nix
#
# Jellyfin library paths (see also services/jellyfin.nix):
# Movies → /mnt/nixdesk-jellyfin/movies
# Shows → /mnt/nixdesk-jellyfin/tv
#
# If you see "Stale file handle" under /mnt after changing exports or fsid on nixdesk, drop the
# old client mount and let automount reattach, e.g.:
# sudo umount -l /mnt/nixdesk-jellyfin
# ls /mnt/nixdesk-jellyfin
# (or reboot nix-server.)
{ ... }:
let
nfsExportHost = "192.168.2.25";
# nfsvers+tcp: predictable Linux↔Linux; lookupcache=none: fewer stale dentries after export changes.
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;
};
}
-2
View File
@@ -3,8 +3,6 @@
system = "x86_64-linux";
specialArgs = {
inherit self inputs;
host = "nix-server";
system = "x86_64-linux";
};
modules = [
self.nixosModules.nix-serverConfiguration
@@ -1,28 +0,0 @@
# 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;
};
}
+1 -1
View File
@@ -17,7 +17,7 @@
./_private/media-disk.nix
./_private/media-paths.nix
./_services/docker-media.nix
./_services/nixdesk-nfs-client.nix
self.nixosModules.nixdeskJellyfin
./_services/jellyfin.nix
./_services/sonarr.nix
./_services/radarr.nix
-2
View File
@@ -3,8 +3,6 @@
system = "x86_64-linux";
specialArgs = {
inherit self inputs;
host = "r5500";
system = "x86_64-linux";
};
modules = [
self.nixosModules.r5500Configuration
-2
View File
@@ -4,8 +4,6 @@
system = "x86_64-linux";
specialArgs = {
inherit self inputs;
host = "t2mbp";
system = "x86_64-linux";
};
modules = [
self.nixosModules.t2mbpConfiguration
+10 -5
View File
@@ -1,24 +1,29 @@
{ self, inputs, ... }:
{
flake.nixosModules.t2mbpHome =
{ self, ... }:
{ self, pkgs, ... }:
{
imports = [ self.nixosModules.desktopHomeBase ];
chiasson.users.extraModules.olivier = [
{
chiasson.home = {
browsers = {
edge.enable = true;
};
browsers.edge.enable = true;
apps = {
discord.enable = true;
pokeclicker.enable = true;
};
editors.vscode = {
enable = true;
extensions = with pkgs.vscode-extensions; [
alefragnani.project-manager
johnpapa.vscode-peacock
];
};
};
}
];
};
}
-2
View File
@@ -4,8 +4,6 @@
specialArgs = inputs // {
inherit self;
inputs = inputs;
host = "uConsole";
system = "aarch64-linux";
};
trustCaches = false;
modules = [
+9 -3
View File
@@ -1,14 +1,20 @@
{ self, inputs, ... }:
{
flake.nixosModules.uConsoleHome =
{ self, ... }:
{ self, pkgs, ... }:
{
imports = [ self.nixosModules.desktopHomeBase ];
chiasson.users.extraModules.olivier = [
{
chiasson.home = {
hardware.uconsoleGamepad.enable = true;
chiasson.home.hardware.uconsoleGamepad.enable = true;
chiasson.home.editors.vscode = {
enable = true;
extensions = with pkgs.vscode-extensions; [
alefragnani.project-manager
johnpapa.vscode-peacock
];
};
}
];
-33
View File
@@ -1,33 +0,0 @@
# 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;
}
-31
View File
@@ -1,31 +0,0 @@
{ ... }: {
flake.lib.rbwSshSocket =
let
# Bash/Fish init snippets — used by wisdom/shells/{bash,fish}.nix.
bashSnippet = ''
if [ -z "''${SSH_AUTH_SOCK:-}" ]; then
if [ -n "''${XDG_RUNTIME_DIR:-}" ]; then
export SSH_AUTH_SOCK="$XDG_RUNTIME_DIR/rbw/ssh-agent-socket"
else
export SSH_AUTH_SOCK="/run/user/$(id -u)/rbw/ssh-agent-socket"
fi
fi
'';
fishSnippet =
pkgs: ''
if test -z "$SSH_AUTH_SOCK"
if test -n "$XDG_RUNTIME_DIR"
set -gx SSH_AUTH_SOCK "$XDG_RUNTIME_DIR/rbw/ssh-agent-socket"
else
set -l _hm_uid (${pkgs.coreutils}/bin/id -u)
set -gx SSH_AUTH_SOCK "/run/user/$_hm_uid/rbw/ssh-agent-socket"
end
end
'';
in {
inherit bashSnippet fishSnippet;
# Used by home-manager modules to expose SSH_AUTH_SOCK to GUI apps
# and HM-managed shells declaratively (no activation script needed).
sessionVariable = "$XDG_RUNTIME_DIR/rbw/ssh-agent-socket";
};
}
-99
View File
@@ -1,99 +0,0 @@
# Pure helpers: catalog → NixOS/HM/SSH shapes (`self.lib.usersMerge lib`).
{ ... }: {
flake.lib.usersMerge =
lib:
let
userHm = user: user.homeManager or { };
userSsh = user: user.ssh or { };
in
rec {
resolveHomeManagerModule =
moduleSpec:
let
t = builtins.typeOf moduleSpec;
in
if t == "path" || t == "string" then import moduleSpec else moduleSpec;
selectedUsersAttr =
{ catalog, enabled, hostOverrides }:
lib.listToAttrs (
map (name: {
inherit name;
value = lib.recursiveUpdate catalog.${name} (hostOverrides.${name} or { });
}) enabled
);
missingEnabledNames = catalog: enabled: builtins.filter (name: !(builtins.hasAttr name catalog)) enabled;
strayHomeUserKeys = homeUsers: enabled:
builtins.filter (k: !(builtins.elem k enabled)) (builtins.attrNames homeUsers);
mkNixosUser =
name: user:
{
isNormalUser = user.isNormalUser or true;
description = user.description or name;
extraGroups = user.extraGroups or [ ];
}
// lib.optionalAttrs (user ? hashedPasswordFile && user.hashedPasswordFile != null) {
hashedPasswordFile = user.hashedPasswordFile;
};
hmWiredNames =
selectedUsers:
lib.attrNames (
lib.filterAttrs (_: user:
let
hm = userHm user;
in
(hm.enable or false) && (hm.module or null) != null
) selectedUsers
);
rbwOutboundSnippet =
name: user:
let
outboundCfg = ((userSsh user).outbound or { }).rbw or { };
in
lib.mkIf (outboundCfg.enable or false) {
chiasson.ssh.outbound.rbw.enable = true;
chiasson.ssh.outbound.rbw.user = name;
chiasson.ssh.outbound.rbw.hosts =
if (outboundCfg.hosts or "all") == "all" then [ "all" ] else outboundCfg.hosts;
};
mkHmUserModule =
{ name, user, hostExtraModules }:
let
hm = userHm user;
hmModule = resolveHomeManagerModule hm.module;
in
lib.mkMerge (
[
hmModule
(rbwOutboundSnippet name user)
]
++ (hm.extraModules or [ ])
++ hostExtraModules
);
inboundAuthorizedOrNull =
user:
let
inboundCfg = (userSsh user).inbound or { };
in
if !(inboundCfg.enable or false) then
null
else if (inboundCfg.authorizedHosts or "all") == "all" then
"all"
else
inboundCfg.authorizedHosts;
inboundHostsAttr =
selectedUsers:
lib.pipe selectedUsers [
(lib.mapAttrs (_: inboundAuthorizedOrNull))
(lib.filterAttrs (_: v: v != null))
];
};
}
-17
View File
@@ -1,17 +0,0 @@
# Resolve `wisdom*` HM slices for NixOS `extraModules` (`self` is not an HM specialArg).
# Slices are gated by `chiasson.home.*.enable`; hosts only flip toggles in `home.nix`.
{ lib, ... }: {
flake.lib.wisdomCatalogExtraModules =
self:
let
names = lib.sort builtins.lessThan (
lib.filter (
n:
lib.hasPrefix "wisdom" n
&& n != "wisdom"
&& n != "wisdomShellBash"
) (builtins.attrNames self.homeManagerModules)
);
in
map (name: self.homeManagerModules.${name}) names;
}
+35 -22
View File
@@ -7,24 +7,29 @@
# Resolve which hosts to emit blocks for, and which user identity to
# use as the IdentityFile filter against the rbw agent.
selectedHostNames =
if cfg.hosts == [ "all" ] then
builtins.attrNames inventory.activeHosts
else
cfg.hosts;
if cfg.hosts == [ "all" ] then builtins.attrNames inventory.activeHosts else cfg.hosts;
missingHosts = builtins.filter (name: !(builtins.hasAttr name inventory.hosts)) selectedHostNames;
# Resolve to the actual host attrs for the template. The "all" case
# reuses `activeHosts` directly instead of rebuilding the same attrset.
selectedHosts =
if cfg.hosts == [ "all" ] then
inventory.activeHosts
else
builtins.listToAttrs (
map (n: {
name = n;
value = inventory.hosts.${n};
}) selectedHostNames
);
# All identities this HM user may authenticate AS: cfg.user
# extraIdentities. `cfg.user` can be null (e.g. user not in inventory
# yet) — in that case the Host-block IdentityFile line is suppressed
# and cross-account SSH is wired from extraIdentities alone.
effectiveIdentities = lib.filter (n: n != null) ([ cfg.user ] ++ cfg.extraIdentities);
defaultUserHasKey =
cfg.user == null
|| (inventory.users.${cfg.user}.publicKey or "") != "";
defaultUserHasKey = cfg.user == null || (inventory.users.${cfg.user}.publicKey or "") != "";
# Each extra identity must have a pubkey in the inventory. Dropping
# it silently would produce a dead Match block — fail the build.
missingExtraKeys = lib.filter
(n: (inventory.users.${n}.publicKey or "") == "")
cfg.extraIdentities;
missingExtraKeys = lib.filter (n: (inventory.users.${n}.publicKey or "") == "") cfg.extraIdentities;
in
{
options.chiasson.ssh.outbound.rbw = {
@@ -57,11 +62,14 @@
extraIdentities = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
example = [ "server" "builder" ];
example = [
"server"
"builder"
];
description = ''
Catalog accounts this HM user may authenticate AS, in addition
to `cfg.user`. Each entry must have a publicKey pasted into
`modules/lib/ssh-inventory.nix`. Typical for the operator's
`modules/ssh/inventory.nix`. Typical for the operator's
primary laptop: `[ "server" "builder" ]` so fleet/ops commands
like `ssh server@r5500` or `ssh builder@nix-server` work
without touching `/etc/passwd` on each host.
@@ -69,7 +77,8 @@
};
};
config = lib.mkIf cfg.enable (lib.mkMerge [
config = lib.mkIf cfg.enable (
lib.mkMerge [
{
assertions = [
{
@@ -78,11 +87,11 @@
}
{
assertion = defaultUserHasKey;
message = "ssh.outbound.rbw: no publicKey for inventory user `${cfg.user}` in `modules/lib/ssh-inventory.nix`.";
message = "ssh.outbound.rbw: no publicKey for inventory user `${cfg.user}` in `modules/ssh/inventory.nix`.";
}
{
assertion = missingExtraKeys == [ ];
message = "ssh.outbound.rbw: extraIdentities has entries with no publicKey in `modules/lib/ssh-inventory.nix`: ${builtins.concatStringsSep ", " missingExtraKeys}";
message = "ssh.outbound.rbw: extraIdentities has entries with no publicKey in `modules/ssh/inventory.nix`: ${builtins.concatStringsSep ", " missingExtraKeys}";
}
# `enable=true` with neither a default user nor any extras gives the
# agent no IdentityFile at all; with IdentitiesOnly yes the user
@@ -95,24 +104,27 @@
}
(lib.mkMerge [
{
home.packages = with pkgs; [ rbw pinentry-gtk2 ];
home.packages = with pkgs; [
rbw
pinentry-gtk2
];
home.sessionVariables.SSH_AUTH_SOCK = self.lib.rbwSshSocket.sessionVariable;
# Write a `.pub` file per identity this HM user may act as
# (cfg.user extraIdentities). OpenSSH reads each one via
# IdentityFile/Match to filter agent keys — they never hold
# private key material, so 0644 (HM's default) is fine.
# OpenSSH's StrictModes only rejects group/other-writable files.
home.file = lib.listToAttrs (map
(n: {
home.file = lib.listToAttrs (
map (n: {
name = inventory.mkIdentityFileName n;
value.text = "${inventory.users.${n}.publicKey}\n";
})
effectiveIdentities);
}) effectiveIdentities
);
programs.ssh.enable = lib.mkIf cfg.manageSshConfig false;
}
(lib.mkIf cfg.manageSshConfig {
home.file.".ssh/config".text = inventory.mkSshConfigTemplate {
selectedHosts = builtins.listToAttrs (map (n: { name = n; value = inventory.hosts.${n}; }) selectedHostNames);
inherit selectedHosts;
user = cfg.user;
inherit (cfg) extraIdentities;
};
@@ -136,6 +148,7 @@
'';
}
])
]);
]
);
};
}
@@ -57,7 +57,7 @@
# parses as an IPv4 dotted-quad. Filters out scratchpad/non-routable
# entries (e.g. `hostName = "test"`).
activeHosts = lib.filterAttrs
(_: h: lib.match "[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+" h.hostName != null)
(_: h: lib.match "^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$" h.hostName != null)
hosts;
# .pub filename convention. OpenSSH 7.3+ accepts a `.pub` file as
@@ -78,6 +78,10 @@
identityAgent ? "SSH_AUTH_SOCK",
}:
let
# Shared `IdentityFile` filter line (when `user` is set). Used by
# both the per-host and gitea blocks so the two can't drift apart.
idLine = lib.optionalString (user != null) " IdentityFile ~/${mkIdentityFileName user}\n";
# Match blocks for cross-account SSH. Filter out cfg.user since
# the matching pub is already loaded via the Host block's
# IdentityFile line — re-emitting it would just duplicate.
@@ -97,7 +101,6 @@
let
e = selectedHosts.${hostName};
hostPatterns = lib.concatStringsSep " " (e.aliases ++ [ e.hostName ]);
idLine = if user == null then "" else " IdentityFile ~/${mkIdentityFileName user}\n";
in ''
Host ${hostPatterns}
HostName ${e.hostName}
@@ -114,7 +117,7 @@
HostName 192.168.2.238
Port 222
User git
${if user == null then "" else " IdentityFile ~/${mkIdentityFileName user}\n"} IdentityAgent ${identityAgent}
${idLine} IdentityAgent ${identityAgent}
IdentitiesOnly yes
'';
+1 -1
View File
@@ -36,7 +36,7 @@
{
assertions = [{
assertion = cfg.enable -> missingKeys == [ ];
message = "chiasson.ssh.inbound: users enabled but missing public key in `modules/lib/ssh-inventory.nix`: ${lib.concatStringsSep ", " missingKeys}";
message = "chiasson.ssh.inbound: users enabled but missing public key in `modules/ssh/inventory.nix`: ${lib.concatStringsSep ", " missingKeys}";
}];
}
(lib.mkIf cfg.enable {
+39
View File
@@ -0,0 +1,39 @@
# rbw (Bitwarden) SSH-agent socket wiring, shared across shells and the
# outbound-rbw Home Manager module. Exposed as `flake.lib.rbwSshSocket`.
{ ... }: {
flake.lib.rbwSshSocket =
let
socketPath = "$XDG_RUNTIME_DIR/rbw/ssh-agent-socket";
# Bash init snippet — used by wisdom/shells/bash.nix (profile + interactive).
bashSnippet = ''
if [ -z "''${SSH_AUTH_SOCK:-}" ]; then
if [ -n "''${XDG_RUNTIME_DIR:-}" ]; then
export SSH_AUTH_SOCK="${socketPath}"
else
export SSH_AUTH_SOCK="/run/user/$(id -u)/rbw/ssh-agent-socket"
fi
fi
'';
# Fish init snippet — used by wisdom/shells/fish.nix.
fishSnippet = pkgs: ''
if test -z "$SSH_AUTH_SOCK"
if test -n "$XDG_RUNTIME_DIR"
set -gx SSH_AUTH_SOCK "${socketPath}"
else
set -l _hm_uid (${pkgs.coreutils}/bin/id -u)
set -gx SSH_AUTH_SOCK "/run/user/$_hm_uid/rbw/ssh-agent-socket"
end
end
'';
in
{
inherit bashSnippet fishSnippet;
# Used by home-manager modules to expose SSH_AUTH_SOCK to GUI apps
# and HM-managed shells declaratively (no activation script needed).
sessionVariable = socketPath;
};
}
+5 -6
View File
@@ -17,10 +17,9 @@
++ lib.optionals pkgs.stdenv.isx86_64 [ heroic ];
steamExtraPkgs =
if !cfg.steam.steamTinkerLaunch.enable then
[ ]
else
lib.optional (lib.meta.availableOn pkgs.stdenv.hostPlatform pkgs.steamtinkerlaunch) pkgs.steamtinkerlaunch;
lib.optional (cfg.steam.steamTinkerLaunch.enable && lib.meta.availableOn pkgs.stdenv.hostPlatform pkgs.steamtinkerlaunch) pkgs.steamtinkerlaunch;
launcherSet = launcherPkgs ++ steamExtraPkgs ++ cfg.launchers.extraPackages;
in
{
options.chiasson.system.gaming = {
@@ -194,11 +193,11 @@
];
}
(lib.mkIf (cfg.launchers.forUsers == null) {
environment.systemPackages = launcherPkgs ++ steamExtraPkgs ++ cfg.launchers.extraPackages;
environment.systemPackages = launcherSet;
})
(lib.mkIf (cfg.launchers.forUsers != null) {
users.users = lib.genAttrs cfg.launchers.forUsers (_: {
packages = launcherPkgs ++ steamExtraPkgs ++ cfg.launchers.extraPackages;
packages = launcherSet;
});
})
]);
+38
View File
@@ -0,0 +1,38 @@
# NFS mount of nixdesk (14900k) bulk storage for clients. Exports live in
# modules/hosts/14900k/_private/jellyfin-nfs-export.nix
#
# Jellyfin library paths (see also jellyfin.nix):
# Movies → /mnt/nixdesk-jellyfin/movies
# Shows → /mnt/nixdesk-jellyfin/tv
#
# If you see "Stale file handle" under /mnt after changing exports or fsid on nixdesk, drop the
# old client mount and let automount reattach, e.g.:
# sudo umount -l /mnt/nixdesk-jellyfin
# ls /mnt/nixdesk-jellyfin
{ ... }:
{
flake.nixosModules.nixdeskJellyfin =
{ lib, ... }:
let
nfsExportHost = "192.168.2.25";
# nfsvers+tcp: predictable Linux↔Linux; lookupcache=none: fewer stale dentries after export changes.
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;
};
};
}
+152 -28
View File
@@ -8,18 +8,115 @@
{ config, options, lib, ... }:
let
cfg = config.chiasson.users;
usersLib = self.lib.usersMerge lib;
usersLib =
let
userHm = user: user.homeManager or { };
userSsh = user: user.ssh or { };
in
rec {
resolveHomeManagerModule =
moduleSpec:
let
t = builtins.typeOf moduleSpec;
in
if t == "path" || t == "string" then import moduleSpec else moduleSpec;
selectedUsersAttr =
{ catalog, enabled, hostOverrides }:
lib.listToAttrs (
map (name: {
inherit name;
value = lib.recursiveUpdate catalog.${name} (hostOverrides.${name} or { });
}) enabled
);
missingEnabledNames =
catalog: enabled: builtins.filter (name: !(builtins.hasAttr name catalog)) enabled;
strayHomeUserKeys =
homeUsers: enabled: builtins.filter (k: !(builtins.elem k enabled)) (builtins.attrNames homeUsers);
mkNixosUser =
name: user:
{
isNormalUser = user.isNormalUser or true;
description = user.description or name;
extraGroups = user.extraGroups or [ ];
}
// lib.optionalAttrs (user ? hashedPasswordFile && user.hashedPasswordFile != null) {
hashedPasswordFile = user.hashedPasswordFile;
};
hmWiredNames =
selectedUsers:
lib.attrNames (
lib.filterAttrs (
_: user:
let
hm = userHm user;
in
(hm.enable or false) && (hm.module or null) != null
) selectedUsers
);
rbwOutboundSnippet =
name: user:
let
outboundCfg = ((userSsh user).outbound or { }).rbw or { };
in
lib.mkIf (outboundCfg.enable or false) {
chiasson.ssh.outbound.rbw.enable = true;
chiasson.ssh.outbound.rbw.user = name;
chiasson.ssh.outbound.rbw.hosts =
if (outboundCfg.hosts or "all") == "all" then [ "all" ] else outboundCfg.hosts;
chiasson.ssh.outbound.rbw.extraIdentities = outboundCfg.extraIdentities or [ ];
};
mkHmUserModule =
{ name, user, hostExtraModules }:
let
hm = userHm user;
hmModule = resolveHomeManagerModule hm.module;
in
lib.mkMerge (
[
hmModule
(rbwOutboundSnippet name user)
]
++ (hm.extraModules or [ ])
++ hostExtraModules
);
inboundAuthorizedOrNull =
user:
let
inboundCfg = (userSsh user).inbound or { };
in
if !(inboundCfg.enable or false) then
null
else if (inboundCfg.authorizedHosts or "all") == "all" then
"all"
else
inboundCfg.authorizedHosts;
inboundHostsAttr =
selectedUsers:
lib.pipe selectedUsers [
(lib.mapAttrs (_: inboundAuthorizedOrNull))
(lib.filterAttrs (_: v: v != null))
];
};
inventory = self.lib.sshInventory;
olivierEnabled = lib.elem "olivier" cfg.enabled;
# Merge catalog + per-host overrides into the final user attrs.
selectedUsers = lib.listToAttrs (map
(name: {
selectedUsers = lib.listToAttrs (
map (name: {
inherit name;
value = lib.recursiveUpdate cfg.catalog.${name} (cfg.hostOverrides.${name} or { });
})
cfg.enabled);
}) cfg.enabled
);
names = usersLib.hmWiredNames selectedUsers;
missing = usersLib.missingEnabledNames cfg.catalog cfg.enabled;
@@ -27,26 +124,27 @@
hmAvailable = lib.hasAttrByPath [ "home-manager" "users" ] options;
inboundUsersAttr = usersLib.inboundHostsAttr selectedUsers;
hmUsersAttr = lib.listToAttrs (map
(name: {
hmUsersAttr = lib.listToAttrs (
map (name: {
inherit name;
value = usersLib.mkHmUserModule {
inherit name;
user = selectedUsers.${name};
hostExtraModules = cfg.extraModules.${name} or [ ];
};
})
names);
}) names
);
# Fish shell wiring: HM's fish module declares its package, but
# /etc/passwd + /etc/shells need updating so login shells work.
# Only fires for HM users that actually enable fish.
hmFishUsers =
if !hmAvailable then { }
if !hmAvailable then
{ }
else
lib.filterAttrs
(name: hmUser: (hmUser.programs.fish.enable or false) && builtins.elem name names)
config.home-manager.users;
lib.filterAttrs (
name: hmUser: (hmUser.programs.fish.enable or false) && builtins.elem name names
) config.home-manager.users;
in
{
imports = [
@@ -108,23 +206,41 @@
isNormalUser = true;
description = "Olivier";
extraGroups = [
"networkmanager" "wheel" "docker" "fuse" "uinput" "kvm"
"networkmanager"
"wheel"
"docker"
"fuse"
"uinput"
"kvm"
# `video` lets brightnessctl/light udev rules own /sys/backlight without sudo.
# Harmless on headless hosts (no devices).
"video"
# DRI render + input for gamescope / Steam on Wayland, no sudo.
"render" "input"
"render"
"input"
];
hashedPasswordFile = lib.mkIf olivierEnabled (
config.sops.secrets."users/olivier/hashedPassword".path
);
homeManager = {
enable = true;
module = { ... }: {
module = { pkgs, ... }: {
home.username = "olivier";
home.homeDirectory = "/home/olivier";
home.stateVersion = "25.11";
programs.home-manager.enable = true;
# Declarative rbw config — replaces per-host
# `rbw config set email|base_url`. Neither value is a
# secret (URL + login email), so plaintext is fine, same
# as the public keys committed in ssh-inventory.nix.
programs.rbw = {
enable = true;
settings = {
email = "olivierchiasson@hotmail.fr";
base_url = "https://bitwarden.chiasson.cloud";
};
};
};
};
ssh.inbound.enable = true;
@@ -135,14 +251,20 @@
# `.pub` filter for each of these and emits a `Match user`
# block, so `ssh server@r5500` / `ssh builder@nix-server`
# route the right private key from the rbw agent.
ssh.outbound.rbw.extraIdentities = [ "server" "builder" ];
ssh.outbound.rbw.extraIdentities = [
"server"
"builder"
];
};
server = {
isNormalUser = true;
description = "Server user";
extraGroups = [ "wheel" ];
homeManager = { enable = false; module = null; };
homeManager = {
enable = false;
module = null;
};
ssh.inbound.enable = true;
ssh.inbound.authorizedHosts = "all";
ssh.outbound.rbw.enable = false;
@@ -154,8 +276,12 @@
description = "Navi fleet deploy (push + activate only)";
extraGroups = [ ];
createHome = false;
homeManager = { enable = false; module = null; };
homeManager = {
enable = false;
module = null;
};
ssh.inbound.enable = true;
ssh.inbound.authorizedHosts = "all";
};
};
@@ -182,9 +308,7 @@
];
# NixOS user accounts (no HM, raw).
users.users = lib.mapAttrs
(name: user: usersLib.mkNixosUser name user)
selectedUsers;
users.users = lib.mapAttrs (name: user: usersLib.mkNixosUser name user) selectedUsers;
}
(lib.optionalAttrs hmAvailable {
"home-manager".useGlobalPkgs = lib.mkIf (cfg.homeManager.autoWire && names != [ ]) true;
@@ -203,12 +327,12 @@
# /etc/shells with `mkForce`. `mkNixosUser` doesn't emit `shell`, so
# the force is uncontested.
(lib.mkIf (hmFishUsers != { }) {
environment.shells = lib.mkAfter (lib.mapAttrsToList
(_: hmUser: lib.getExe hmUser.programs.fish.package)
hmFishUsers);
users.users = lib.mapAttrs
(name: hmUser: { shell = lib.mkForce (lib.getExe hmUser.programs.fish.package); })
hmFishUsers;
environment.shells = lib.mkAfter (
lib.mapAttrsToList (_: hmUser: lib.getExe hmUser.programs.fish.package) hmFishUsers
);
users.users = lib.mapAttrs (name: hmUser: {
shell = lib.mkForce (lib.getExe hmUser.programs.fish.package);
}) hmFishUsers;
})
];
};
+3 -1
View File
@@ -12,7 +12,9 @@
options.chiasson.home.apps.discord.enable = lib.mkEnableOption ''
Vesktop (nixcord). Needs nixpkgs `discord` breaks on e.g. aarch64-linux.
'';
'' // {
default = false;
};
config = lib.mkIf root.enable (lib.mkMerge [
{
+6 -19
View File
@@ -1,4 +1,4 @@
{ ... }: {
{ self, ... }: {
flake.nixosModules.systemLocalsend =
{ config, lib, ... }:
let
@@ -19,23 +19,10 @@
};
};
flake.homeManagerModules.wisdomAppsLocalsend =
{ config, lib, pkgs, ... }:
let
root = config.chiasson.home;
cfg = config.chiasson.home.apps.localsend;
in
{
options.chiasson.home.apps.localsend = {
enable = lib.mkEnableOption ''
LocalSend client; open the firewall on NixOS with `system.localsend` if you want inbound.
'';
package = lib.mkPackageOption pkgs "localsend" { };
};
config = lib.mkIf (root.enable && cfg.enable) {
home.packages = [ cfg.package ];
};
flake.homeManagerModules.wisdomAppsLocalsend = self.lib.wisdomSlice {
path = "apps.localsend";
default = true;
description = "LocalSend client; open the firewall on NixOS with `system.localsend` if you want inbound.";
packages = pkgs: [ pkgs.localsend ];
};
}
+6 -14
View File
@@ -1,16 +1,8 @@
{ ... }: {
flake.homeManagerModules.wisdomAppsPokeclicker =
{ config, lib, pkgs, ... }:
let
root = config.chiasson.home;
cfg = config.chiasson.home.apps.pokeclicker;
pokeclickerPkg = pkgs.callPackage ./package { };
in
{
options.chiasson.home.apps.pokeclicker.enable = lib.mkEnableOption "PokéClicker desktop (Farigh fork .deb nix).";
config = lib.mkIf (root.enable && cfg.enable) {
home.packages = [ pokeclickerPkg ];
};
{ self, ... }: {
flake.homeManagerModules.wisdomAppsPokeclicker = self.lib.wisdomSlice {
path = "apps.pokeclicker";
default = false;
description = "PokéClicker desktop (Farigh fork .deb nix).";
packages = pkgs: [ (pkgs.callPackage ./package { }) ];
};
}
+6 -17
View File
@@ -1,19 +1,8 @@
{ ... }: {
flake.homeManagerModules.wisdomBrowsersChrome =
{ config, lib, pkgs, ... }:
let
root = config.chiasson.home;
cfg = config.chiasson.home.browsers.chrome;
in
{
options.chiasson.home.browsers.chrome.enable = lib.mkEnableOption ''
Chrome (unfree, needs `allowUnfree`); skipped if nixpkgs has no build for this platform.
'';
config = lib.mkIf (root.enable && cfg.enable) {
home.packages = lib.optional (
lib.meta.availableOn pkgs.stdenv.hostPlatform pkgs.google-chrome
) pkgs.google-chrome;
};
{ self, ... }: {
flake.homeManagerModules.wisdomBrowsersChrome = self.lib.wisdomSlice {
path = "browsers.chrome";
default = false;
description = "Chrome (unfree, needs `allowUnfree`); skipped if nixpkgs has no build for this platform.";
packages = pkgs: [ pkgs.google-chrome ];
};
}
+49 -47
View File
@@ -5,25 +5,43 @@
cfg = config.chiasson.system.chromiumHevc;
in
{
options.chiasson.system.chromiumHevc.enable = lib.mkEnableOption ''
options.chiasson.system.chromiumHevc = {
enable = lib.mkEnableOption ''
VA-API packages for Chromium HEVC (Intel iHD + optional NVIDIA nvidia-vaapi-driver).
Pair with `wisdomBrowsersChromiumHevc` on the user side.
'';
gpu = lib.mkOption {
type = lib.types.enum [
"intel"
"nvidia"
];
default = "intel";
description = ''
VA-API stack for the `*-hevc` browser launchers. Use **intel** for Jellyfin
(Chromium + nvidia-vaapi-driver is unsupported upstream and fails with
`failed Initialize()ing the frame pool` in Jellyfin). **nvidia** adds the
nvidia-vaapi-driver and keep renderD129 + VaapiOnNvidiaGPUs for experiments only.
'';
};
};
config = lib.mkIf cfg.enable {
hardware.graphics.enable = lib.mkDefault true;
hardware.graphics.extraPackages = lib.mkAfter (
with pkgs;
[ nvidia-vaapi-driver ]
lib.optionals (cfg.gpu == "nvidia") [
pkgs.nvidia-vaapi-driver
]
);
};
};
flake.homeManagerModules.wisdomBrowsersChromiumHevc =
{ config, lib, pkgs, ... }:
{ config, lib, pkgs, osConfig ? { }, ... }:
let
root = config.chiasson.home;
cfg = config.chiasson.home.browsers.chromiumHevc;
gpu = lib.attrByPath [ "chiasson" "system" "chromiumHevc" "gpu" ] "intel" osConfig;
browserCatalog = {
"google-chrome" = {
@@ -53,7 +71,6 @@
intel = {
driver = "iHD";
drmDevice = "/dev/dri/renderD128";
nvdBackend = "direct";
enableFeatures = [
"VaapiVideoDecodeLinuxGL"
"VaapiVideoDecoder"
@@ -69,7 +86,6 @@
nvidia = {
driver = "nvidia";
drmDevice = "/dev/dri/renderD129";
nvdBackend = "direct";
enableFeatures = [
"VaapiVideoDecoder"
"VaapiIgnoreDriverChecks"
@@ -85,21 +101,17 @@
};
};
activeGpu = gpuProfiles.${cfg.vaapi.gpu};
mkChromiumHevc =
packageName:
let
spec = browserCatalog.${packageName};
browser = spec.package;
launcherName = spec.launcher;
activeGpu = gpuProfiles.${gpu};
enableFeatures = lib.concatStringsSep "," activeGpu.enableFeatures;
disableFeatures = lib.concatStringsSep "," activeGpu.disableFeatures;
desktopItem = pkgs.makeDesktopItem {
name = launcherName;
mkDesktopItem =
spec:
pkgs.makeDesktopItem {
name = spec.launcher;
desktopName = spec.desktopName;
genericName = "Web Browser";
exec = "${launcherName} %U";
exec = "${spec.launcher} %U";
icon = spec.icon;
categories = [
"Network"
@@ -113,6 +125,14 @@
"x-scheme-handler/https"
];
};
mkChromiumHevc =
packageName:
let
spec = browserCatalog.${packageName};
browser = spec.package;
launcherName = spec.launcher;
desktopItem = mkDesktopItem spec;
in
pkgs.runCommand launcherName
{
@@ -126,7 +146,7 @@
makeWrapper ${browser}/bin/${spec.binary} $out/bin/${launcherName} \
--set LIBVA_DRIVER_NAME ${lib.escapeShellArg activeGpu.driver} \
--set LIBVA_DRM_DEVICE ${lib.escapeShellArg activeGpu.drmDevice} \
--set NVD_BACKEND ${lib.escapeShellArg activeGpu.nvdBackend} \
--set NVD_BACKEND direct \
--add-flags "--enable-features=${enableFeatures}" \
--add-flags "--disable-features=${disableFeatures}" \
${lib.concatMapStringsSep " " (a: "--add-flags ${lib.escapeShellArg a}") cfg.extraCommandLineArgs}
@@ -136,46 +156,28 @@
'';
selectedPackages = lib.filter (
name:
let
spec = browserCatalog.${name};
in
lib.meta.availableOn pkgs.stdenv.hostPlatform spec.package
name: lib.meta.availableOn pkgs.stdenv.hostPlatform browserCatalog.${name}.package
) cfg.packages;
wrappers = map mkChromiumHevc selectedPackages;
in
{
options.chiasson.home.browsers.chromiumHevc = {
enable = lib.mkEnableOption ''
enable =
lib.mkEnableOption ''
`google-chrome-hevc`: Chromium with VA-API HEVC for Jellyfin / MSE playback.
Default GPU is **Intel** (`vaapi.gpu = "intel"`): Chromium + NVIDIA VA-API is
unsupported upstream (`nvidia-vaapi-driver` README) and fails with
`failed Initialize()ing the frame pool` in Jellyfin.
GPU stack comes from `chiasson.system.chromiumHevc.gpu` (default **intel**).
Requires `chiasson.system.chromiumHevc.enable` on NixOS.
'';
packages = lib.mkOption {
type = lib.types.listOf (
lib.types.enum (lib.attrNames browserCatalog)
);
default = [ "google-chrome" ];
description = "Chromium-based browsers to wrap.";
''
// {
default = true;
};
vaapi.gpu = lib.mkOption {
type = lib.types.enum [
"intel"
"nvidia"
];
default = "intel";
description = ''
VA-API stack for `google-chrome-hevc`. Use **intel** for Jellyfin (Chromium +
nvidia-vaapi-driver is unsupported and hits frame-pool init errors). **nvidia**
keeps renderD129 + VaapiOnNvidiaGPUs for experiments only.
'';
packages = lib.mkOption {
type = lib.types.listOf (lib.types.enum (lib.attrNames browserCatalog));
default = if pkgs.stdenv.hostPlatform.isAarch64 then [ "chromium" ] else [ "google-chrome" ];
description = "Chromium-based browsers to wrap.";
};
extraCommandLineArgs = lib.mkOption {
+6 -15
View File
@@ -1,17 +1,8 @@
{ ... }: {
flake.homeManagerModules.wisdomBrowsersEdge =
{ config, lib, pkgs, ... }:
let
root = config.chiasson.home;
cfg = config.chiasson.home.browsers.edge;
in
{
options.chiasson.home.browsers.edge.enable = lib.mkEnableOption "Edge (unfree); skipped if unavailable on this platform.";
config = lib.mkIf (root.enable && cfg.enable) {
home.packages = lib.optional (
lib.meta.availableOn pkgs.stdenv.hostPlatform pkgs.microsoft-edge
) pkgs.microsoft-edge;
};
{ self, ... }: {
flake.homeManagerModules.wisdomBrowsersEdge = self.lib.wisdomSlice {
path = "browsers.edge";
default = false;
description = "Edge (unfree); skipped if unavailable on this platform.";
packages = pkgs: [ pkgs.microsoft-edge ];
};
}
+13 -23
View File
@@ -1,11 +1,10 @@
{ ... }: {
flake.homeManagerModules.wisdomBrowsersFlow =
{ config, lib, pkgs, ... }:
let
root = config.chiasson.home;
cfg = config.chiasson.home.browsers.flow;
flow-browser =
{ self, ... }: {
flake.homeManagerModules.wisdomBrowsersFlow = self.lib.wisdomSlice {
path = "browsers.flow";
default = false;
description = "[Flow](https://github.com/MultiboxLabs/flow-browser) upstream AppImage wrapped for NixOS.";
packages =
pkgs:
let
pname = "flow-browser";
version = "0.11.0";
@@ -25,7 +24,8 @@
appimageContents = pkgs.appimageTools.extractType2 { inherit pname version src; };
in
pkgs.appimageTools.wrapType2 {
[
(pkgs.appimageTools.wrapType2 {
inherit pname version src;
nativeBuildInputs = [ pkgs.makeWrapper ];
@@ -45,23 +45,13 @@
meta = {
description = "Chromium-based browser (upstream AppImage)";
homepage = "https://github.com/MultiboxLabs/flow-browser";
license = lib.licenses.gpl3Plus;
sourceProvenance = with lib.sourceTypes; [ binaryNativeCode ];
license = pkgs.lib.licenses.gpl3Plus;
sourceProvenance = with pkgs.lib.sourceTypes; [ binaryNativeCode ];
platforms = [ "x86_64-linux" "aarch64-linux" ];
mainProgram = pname;
maintainers = [ ];
};
};
in
{
options.chiasson.home.browsers.flow.enable = lib.mkEnableOption ''
[Flow](https://github.com/MultiboxLabs/flow-browser) upstream AppImage wrapped for NixOS.
'';
config = lib.mkIf (root.enable && cfg.enable) {
home.packages = lib.optional (
lib.meta.availableOn pkgs.stdenv.hostPlatform flow-browser
) flow-browser;
};
})
];
};
}
+3 -1
View File
@@ -31,7 +31,9 @@
{
options.chiasson.home.browsers.orion.enable = lib.mkEnableOption ''
[Orion](https://orionbrowser.com/) (Kagi) installs the upstream Flatpak bundle and provides `oriongtk`.
'';
'' // {
default = false;
};
config = lib.mkIf (root.enable && cfg.enable) {
home.packages = [ oriongtk ];
+3 -1
View File
@@ -8,7 +8,9 @@
{
imports = [ inputs.zen-browser.homeModules.beta ];
options.chiasson.home.browsers.zen.enable = lib.mkEnableOption "Zen Browser + locked-down policies / extensions.";
options.chiasson.home.browsers.zen.enable = lib.mkEnableOption "Zen Browser + locked-down policies / extensions." // {
default = false;
};
config = lib.mkIf (root.enable && cfg.enable) {
programs.zen-browser = {
+28 -10
View File
@@ -1,22 +1,21 @@
# HM side of the flake; option tree is `chiasson.home.*` (docs/conventions.md).
{ self, inputs, ... }: {
# Root module: chiasson.home.enable + bash. Other `wisdom*` slices auto-wire via
# `lib.wisdomCatalogExtraModules`; hosts flip `chiasson.home.*.enable` rather than re-importing.
# Root module: chiasson.home.enable + git identity. Other `wisdom*` slices (including
# the bash shell) auto-wire via `lib.wisdomCatalogExtraModules`; hosts flip
# `chiasson.home.*.enable` rather than re-importing.
flake.homeManagerModules.wisdom =
{ config, lib, ... }:
{ config, lib, pkgs, ... }:
let
cfg = config.chiasson.home;
in
{
imports = [
self.homeManagerModules.wisdomShellBash
];
options.chiasson.home = {
enable = lib.mkEnableOption ''
enable =
lib.mkEnableOption ''
HM profile root for this flake (bash on by default). Desktop hosts use
`lib.wisdomCatalogExtraModules` once per user and flip `chiasson.home.*.enable` on the host.
'' // {
''
// {
default = true;
};
@@ -29,6 +28,25 @@
};
};
config = lib.mkIf cfg.enable { home.packages = cfg.extraPackages; };
config = lib.mkIf cfg.enable (
lib.mkMerge [
{ home.packages = cfg.extraPackages; }
# Base git identity + encryption/player tools shared by every HM user.
{
programs.git = {
enable = true;
settings.user = {
name = "OlivierChiasson";
email = "olivierchiasson@hotmail.fr";
};
};
home.packages = [
pkgs.git-crypt
pkgs.feishin
];
}
]
);
};
}
+3 -1
View File
@@ -12,7 +12,9 @@
enable = lib.mkEnableOption ''
WhiteSur GTK + icon themes, Phinger cursor, and Qt via qt6ct (DMS) or KDE platform theme.
Matugen accent colors load via gtk3/gtk4 extraCss when matugenGtkColors is enabled.
'';
'' // {
default = true;
};
matugenGtkColors = lib.mkOption {
type = lib.types.bool;
+4 -2
View File
@@ -13,11 +13,13 @@
{
options.chiasson.home.desktop.screenshot.enable = lib.mkEnableOption ''
grim/slurp/swappy + SwiftShare helpers; Hyprland binds if HM Hyprland is on.
'';
'' // {
default = true;
};
options.chiasson.home.desktop.screenshot.swiftshareApiKeyFile = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
default = "/run/secrets/swiftshare/API_KEY";
description = ''
File with SwiftShare API key (sops path is fine). Required when screenshot module is on.
'';
+3 -1
View File
@@ -33,7 +33,9 @@
in
{
options.chiasson.home.editors.cursor = {
enable = lib.mkEnableOption "Cursor editor from the `cursor` flake input.";
enable = lib.mkEnableOption "Cursor editor from the `cursor` flake input." // {
default = true;
};
setAsDefaultEditor = lib.mkOption {
type = lib.types.bool;
default = true;
+6 -15
View File
@@ -1,17 +1,8 @@
{ ... }: {
flake.homeManagerModules.wisdomEditorsKate =
{ config, lib, pkgs, ... }:
let
root = config.chiasson.home;
cfg = config.chiasson.home.editors.kate;
in
{
options.chiasson.home.editors.kate.enable = lib.mkEnableOption "Kate.";
config = lib.mkIf (root.enable && cfg.enable) {
home.packages = lib.optional (
lib.meta.availableOn pkgs.stdenv.hostPlatform pkgs.kdePackages.kate
) pkgs.kdePackages.kate;
};
{ self, ... }: {
flake.homeManagerModules.wisdomEditorsKate = self.lib.wisdomSlice {
path = "editors.kate";
default = false;
description = "Kate.";
packages = pkgs: [ pkgs.kdePackages.kate ];
};
}
+6 -17
View File
@@ -1,19 +1,8 @@
{ ... }: {
flake.homeManagerModules.wisdomEditorsObsidian =
{ config, lib, pkgs, ... }:
let
root = config.chiasson.home;
cfg = config.chiasson.home.editors.obsidian;
in
{
options.chiasson.home.editors.obsidian.enable = lib.mkEnableOption ''
Obsidian (unfree); skipped if unavailable here.
'';
config = lib.mkIf (root.enable && cfg.enable) {
home.packages = lib.optional (
lib.meta.availableOn pkgs.stdenv.hostPlatform pkgs.obsidian
) pkgs.obsidian;
};
{ self, ... }: {
flake.homeManagerModules.wisdomEditorsObsidian = self.lib.wisdomSlice {
path = "editors.obsidian";
default = true;
description = "Obsidian (unfree); skipped if unavailable here.";
packages = pkgs: [ pkgs.obsidian ];
};
}
+100
View File
@@ -0,0 +1,100 @@
# Visual Studio Code (Microsoft build, unfree). Default extensions + settings shared by all
# hosts; per-host extras (peacock, comment-anchors, project-manager) go via
# `chiasson.home.editors.vscode.extensions`.
{ self, ... }: {
flake.homeManagerModules.wisdomEditorsVscode =
{ config, lib, pkgs, ... }:
let
root = config.chiasson.home;
cfg = config.chiasson.home.editors.vscode;
defaultExtensions = with pkgs.vscode-extensions; [
jnoortheen.nix-ide
yzhang.markdown-all-in-one
esbenp.prettier-vscode
vscjava.vscode-java-pack
redhat.java
];
# Wrap VS Code so a JDK is on PATH — the Red Hat Java language server (and
# other extension-bundled JVMs) need `java` to run, independent of the
# per-project `java.configuration.runtimes` setting.
vscodeWithJdk = pkgs.symlinkJoin {
name = "vscode-with-jdk";
paths = [ pkgs.vscode ];
nativeBuildInputs = [ pkgs.makeWrapper ];
postBuild = ''
wrapProgram "$out/bin/code" \
--prefix PATH : "${pkgs.jdk25}/bin"
'';
};
coreSettings = {
"nix.enableLanguageServer" = true;
"nix.serverPath" = "nixd";
"nix.serverSettings" = {
nixd = {
formatting = {
command = "nixfmt";
};
};
};
"diffEditor.ignoreTrimWhitespace" = false;
"chat.disableAIFeatures" = true;
"java.configuration.runtimes" = [
{
name = "JavaSE-21";
path = "${pkgs.jdk21}/lib/openjdk";
}
{
name = "JavaSE-25";
path = "${pkgs.jdk25}/lib/openjdk";
default = true;
}
];
};
# Seed a *writable* settings.json (not a store symlink) so extensions like
# kilo-code can edit it at runtime. `force = false` in the config means HM
# only writes it when absent, preserving kilo's later edits across `switch`.
settingsJson = pkgs.writeText "vscode-user-settings" (
builtins.toJSON coreSettings
);
in
{
options.chiasson.home.editors.vscode = {
enable = lib.mkEnableOption ''
Visual Studio Code (Microsoft build; needs `nixpkgs.config.allowUnfree`). Core
extensions (nix-ide, markdown-all-in-one, prettier) + nixd/nixfmt settings are included.
'' // {
default = false;
};
extensions = lib.mkOption {
type = lib.types.listOf lib.types.package;
default = [ ];
description = ''
Extra extensions not wanted on every host (e.g. peacock, comment-anchors,
project-manager). Per-host via `chiasson.users.extraModules`.
'';
};
};
config = lib.mkIf (root.enable && cfg.enable) {
home.packages = [ ];
# Writable, seed-once user settings (kilo-code can append to it).
home.file.".config/Code/User/settings.json" = {
source = settingsJson;
force = false;
};
programs.vscode = {
enable = true;
package = vscodeWithJdk;
extensions = defaultExtensions ++ cfg.extensions;
};
};
};
}
+3 -1
View File
@@ -8,7 +8,9 @@
{
options.chiasson.home.filebrowsers.nemo.enable = lib.mkEnableOption ''
Nemo file manager (GTK). Colors come from DMS matugen via GTK settings no KDE/Qt bridge needed.
'';
'' // {
default = true;
};
config = lib.mkIf (root.enable && cfg.enable) {
home.packages = [ pkgs.nemo ];
+3 -1
View File
@@ -8,7 +8,9 @@
{
options.chiasson.home.hardware.uconsoleGamepad.enable = lib.mkEnableOption ''
uConsole gamepad antimicrox profile + Hyprland exec-once when HM Hyprland is on.
'';
'' // {
default = false;
};
config = lib.mkIf (root.enable && cfg.enable) (lib.mkMerge [
{
+3 -1
View File
@@ -8,7 +8,9 @@
in
{
options.chiasson.home.shell.fish = {
enable = lib.mkEnableOption "Fish + grc, quiet greeting, rbw SSH socket defaults.";
enable = lib.mkEnableOption "Fish + grc, quiet greeting, rbw SSH socket defaults." // {
default = true;
};
nixYourShell = {
enable = lib.mkEnableOption ''
+3 -1
View File
@@ -7,7 +7,9 @@
in
{
options.chiasson.home.shell.ohMyPosh = {
enable = lib.mkEnableOption "[Oh My Posh](https://ohmyposh.dev/) prompt.";
enable = lib.mkEnableOption "[Oh My Posh](https://ohmyposh.dev/) prompt." // {
default = true;
};
builtinTheme = lib.mkOption {
type = lib.types.str;
+3 -1
View File
@@ -7,7 +7,9 @@
cfg = config.chiasson.home.shell.yazi;
in
{
options.chiasson.home.shell.yazi.enable = lib.mkEnableOption "Yazi as `y` + 7zz with rar.";
options.chiasson.home.shell.yazi.enable = lib.mkEnableOption "Yazi as `y` + 7zz with rar." // {
default = true;
};
config = lib.mkIf (root.enable && cfg.enable) {
programs.yazi = {
+3 -1
View File
@@ -15,7 +15,9 @@
{
options.chiasson.home.terminals.kitty.enable = lib.mkEnableOption ''
Kitty + DMS includes; activation strips `*-theme.auto.conf` so matugen colors stick.
'';
'' // {
default = true;
};
config = lib.mkIf (root.enable && cfg.enable) {
programs.kitty = {