Compare commits

..

No commits in common. "main" and "main" have entirely different histories.
main ... main

159 changed files with 1887 additions and 6988 deletions

View file

@ -71,12 +71,10 @@ nix multi user, multi system, configuration with `sops` secret management, `home
- [ ] monitor configuration in `~/.config/monitors.xml` should be sym linked to `/run/gdm/.config/monitors.xml` (https://www.reddit.com/r/NixOS/comments/u09cz9/home_manager_create_my_own_symlinks_automatically/) - [ ] monitor configuration in `~/.config/monitors.xml` should be sym linked to `/run/gdm/.config/monitors.xml` (https://www.reddit.com/r/NixOS/comments/u09cz9/home_manager_create_my_own_symlinks_automatically/)
- [ ] migrate away from flakes and move to npins - [ ] migrate away from flakes and move to npins
- [ ] `host.users` should be redone so that we just extend the base `users.users` object. Right now we cant quite do this because we have weird circular dependencies with disko/impermanence (not sure which one) and home manger enabling/disabling users per devices - [ ] `host.users` should be redone so that we just extend the base `users.users` object. Right now we cant quite do this because we have weird circular dependencies with disko/impermanence (not sure which one) and home manger enabling/disabling users per devices
- [ ] Home manager impermanence is preventing updates to the latest version of the module
## Broken things ## Broken things
- [ ] figure out steam vr things? - [ ] figure out steam vr things?
- [ ] whisper was having issues - [ ] whisper was having issues
- [ ] auto loading of ssh agent keys that we auto generate per system
## Data Integrity ## Data Integrity
- [ ] zfs email after scrubbing # TODO: test this - [ ] zfs email after scrubbing # TODO: test this
@ -104,9 +102,7 @@ nix multi user, multi system, configuration with `sops` secret management, `home
- [ ] Penpot services (need to make this custom) - [ ] Penpot services (need to make this custom)
- [ ] minecraft server with old world file - [ ] minecraft server with old world file
- [ ] storj server - [ ] storj server
- [ ] XMR miner used to heat home based on smart thermostat
- [ ] Create Tor guard/relay server - [ ] Create Tor guard/relay server
- [ ] Create i2P node
- [ ] screeps server - [ ] screeps server
- [ ] mastodon instance - [ ] mastodon instance

View file

@ -8,5 +8,6 @@
in { in {
leyla = lib.mkIf users.leyla.isNormalUser (import ./leyla); leyla = lib.mkIf users.leyla.isNormalUser (import ./leyla);
eve = lib.mkIf users.eve.isNormalUser (import ./eve); eve = lib.mkIf users.eve.isNormalUser (import ./eve);
ivy = lib.mkIf users.ivy.isNormalUser (import ./ivy);
git = lib.mkIf (osConfig.services.forgejo.enable or false) (import ./git); git = lib.mkIf (osConfig.services.forgejo.enable or false) (import ./git);
} }

View file

@ -60,7 +60,7 @@ in {
bitwarden.enable = true; bitwarden.enable = true;
discord.enable = true; discord.enable = true;
makemkv.enable = true; makemkv.enable = true;
signal-desktop.enable = true; signal-desktop-bin.enable = true;
steam.enable = true; steam.enable = true;
piper.enable = hardware.piperMouse.enable; piper.enable = hardware.piperMouse.enable;
krita.enable = true; krita.enable = true;
@ -75,9 +75,7 @@ in {
libreoffice.enable = true; libreoffice.enable = true;
noita-entangled-worlds.enable = true; noita-entangled-worlds.enable = true;
opencode.enable = osConfig.host.ai.enable; claude-code.enable = osConfig.host.ai.enable;
e621-downloader.enable = true;
# Windows applications that we need to figure out how to install # Windows applications that we need to figure out how to install
guild-wars-2.enable = false; guild-wars-2.enable = false;

View file

@ -0,0 +1,55 @@
{osConfig, ...}: let
userConfig = osConfig.host.users.ivy;
in {
imports = [
./packages.nix
];
home = {
username = userConfig.name;
homeDirectory = osConfig.users.users.ivy.home;
# This value determines the Home Manager release that your configuration is
# compatible with. This helps avoid breakage when a new Home Manager release
# introduces backwards incompatible changes.
#
# You should not change this value, even if you update Home Manager. If you do
# want to update the value, then make sure to first check the Home Manager
# release notes.
stateVersion = "23.11"; # Please read the comment before changing.
# Home Manager is pretty good at managing dotfiles. The primary way to manage
# plain files is through 'home.file'.
file = {
# # Building this configuration will create a copy of 'dotfiles/screenrc' in
# # the Nix store. Activating the configuration will then make '~/.screenrc' a
# # symlink to the Nix store copy.
# ".screenrc".source = dotfiles/screenrc;
# # You can also set the file content immediately.
# ".gradle/gradle.properties".text = ''
# org.gradle.console=verbose
# org.gradle.daemon.idletimeout=3600000
# '';
};
# Home Manager can also manage your environment variables through
# 'home.sessionVariables'. If you don't want to manage your shell through Home
# Manager then you have to manually source 'hm-session-vars.sh' located at
# either
#
# ~/.nix-profile/etc/profile.d/hm-session-vars.sh
#
# or
#
# ~/.local/state/nix/profiles/profile/etc/profile.d/hm-session-vars.sh
#
# or
#
# /etc/profiles/per-user/ivy/etc/profile.d/hm-session-vars.sh
#
sessionVariables = {
# EDITOR = "emacs";
};
};
}

View file

@ -0,0 +1,73 @@
{
lib,
pkgs,
config,
osConfig,
...
}: {
config = {
nixpkgs.config = {
allowUnfree = true;
};
# Programs that need to be installed with some extra configuration
programs = lib.mkMerge [
{
# Let Home Manager install and manage itself.
home-manager.enable = true;
}
(lib.mkIf (config.user.isDesktopUser || config.user.isTerminalUser) {
# git = {
# enable = true;
# userName = "Ivy";
# userEmail = "ivy@example.com"; # Update this with actual email
# extraConfig.init.defaultBranch = "main";
# };
openssh = {
enable = true;
hostKeys = [
{
type = "ed25519";
path = "${config.home.username}_${osConfig.networking.hostName}_ed25519";
}
];
};
})
(lib.mkIf config.user.isDesktopUser {
vscode = {
enable = true;
package = pkgs.vscodium;
mutableExtensionsDir = false;
profiles.default = {
enableUpdateCheck = false;
enableExtensionUpdateCheck = false;
extraExtensions = {
# Cline extension (Claude AI assistant)
claudeDev.enable = true;
# Auto Rename Tag
autoRenameTag.enable = true;
# Live Server
liveServer.enable = true;
};
extensions = let
extension-pkgs = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version;
in (
with extension-pkgs.open-vsx; [
streetsidesoftware.code-spell-checker
]
);
};
};
firefox.enable = true;
discord.enable = true;
signal-desktop-bin.enable = true;
claude-code.enable = true;
})
];
};
}

View file

@ -12,7 +12,7 @@
]; ];
config = { config = {
impermanence.enable = osConfig.storage.impermanence.enable; impermanence.enable = osConfig.host.impermanence.enable;
# Home Manager needs a bit of information about you and the paths it should # Home Manager needs a bit of information about you and the paths it should
# manage. # manage.

View file

@ -4,7 +4,7 @@
... ...
}: { }: {
config = lib.mkIf (config.impermanence.enable) { config = lib.mkIf (config.impermanence.enable) {
home.persistence."${config.impermanence.persistencePath}" = { home.persistence."/persist/home/leyla" = {
directories = [ directories = [
"desktop" "desktop"
"downloads" "downloads"
@ -14,6 +14,7 @@
".bash_history" # keep shell history around ".bash_history" # keep shell history around
"${config.xdg.dataHome}/recently-used.xbel" # gnome recently viewed files "${config.xdg.dataHome}/recently-used.xbel" # gnome recently viewed files
]; ];
allowOther = true;
}; };
}; };
} }

View file

@ -41,19 +41,17 @@ in {
openrgb.enable = hardware.openRGB.enable; openrgb.enable = hardware.openRGB.enable;
via.enable = hardware.viaKeyboard.enable; via.enable = hardware.viaKeyboard.enable;
claude-code.enable = osConfig.host.ai.enable; claude-code.enable = osConfig.host.ai.enable;
opencode.enable = osConfig.host.ai.enable;
davinci-resolve.enable = hardware.graphicsAcceleration.enable; davinci-resolve.enable = hardware.graphicsAcceleration.enable;
mfoc.enable = true; mfoc.enable = true;
}) })
(lib.mkIf (hardware.directAccess.enable && config.user.isDesktopUser) { (lib.mkIf (hardware.directAccess.enable && config.user.isDesktopUser) {
anki.enable = true; anki.enable = true;
android-studio.enable = true;
makemkv.enable = true; makemkv.enable = true;
discord.enable = true; discord.enable = true;
signal-desktop.enable = true; signal-desktop-bin.enable = true;
calibre.enable = true; calibre.enable = true;
obsidian.enable = true; obsidian.enable = true;
jetbrains.idea-oss.enable = true; jetbrains.idea-community.enable = true;
vscode.enable = true; vscode.enable = true;
firefox.enable = true; firefox.enable = true;
steam.enable = true; steam.enable = true;
@ -73,10 +71,6 @@ in {
noita-entangled-worlds.enable = true; noita-entangled-worlds.enable = true;
tor-browser.enable = true; tor-browser.enable = true;
gdx-liftoff.enable = true; gdx-liftoff.enable = true;
proton-mail-pwa.enable = true;
proton-calendar-pwa.enable = true;
matrix-cyberia-pwa.enable = true;
kicad.enable = true;
}) })
]; ];
} }

View file

@ -141,12 +141,6 @@
keyword = ""; keyword = "";
tags = [""]; tags = [""];
} }
{
name = "Cyberia Git";
url = "https://git.cyberia.club";
keyword = "";
tags = [""];
}
# Template # Template
# { # {
# name = ""; # name = "";

View file

@ -58,9 +58,6 @@ in {
nearley.enable = true; nearley.enable = true;
# graphql
graphql.enable = true;
# astro development # astro development
vscodeMdx.enable = true; vscodeMdx.enable = true;
astroVscode.enable = true; astroVscode.enable = true;
@ -75,12 +72,9 @@ in {
# rust development # rust development
rustAnalyzer.enable = true; rustAnalyzer.enable = true;
# arduino development
platformIO.enable = false;
# claude development # claude development
claudeDev = lib.mkIf ai-tooling-enabled { claudeDev = lib.mkIf ai-tooling-enabled {
enable = false; enable = true;
mcp = { mcp = {
nixos = { nixos = {
enable = true; enable = true;

View file

@ -6,7 +6,6 @@
config.programs.vscode.profiles.default.userSettings = { config.programs.vscode.profiles.default.userSettings = {
"cSpell.userWords" = [ "cSpell.userWords" = [
"leyla" "leyla"
"Cyberia"
]; ];
"cSpell.languageSettings" = [ "cSpell.languageSettings" = [

View file

@ -33,6 +33,44 @@
isPrincipleUser = true; isPrincipleUser = true;
}; };
}; };
impermanence.enable = true;
storage = {
enable = true;
encryption = true;
notifications = {
enable = true;
host = "smtp.protonmail.ch";
port = 587;
to = "leyla@jan-leila.com";
user = "noreply@jan-leila.com";
tokenFile = config.sops.secrets."services/zfs_smtp_token".path;
};
pool = {
# We are having to boot off of the nvm cache drive because I cant figure out how to boot via the HBA
bootDrives = ["nvme-Samsung_SSD_990_PRO_4TB_S7KGNU0X907881F"];
vdevs = [
[
"ata-ST18000NE000-3G6101_ZVTCXVEB"
"ata-ST18000NE000-3G6101_ZVTCXWSC"
"ata-ST18000NE000-3G6101_ZVTD10EH"
"ata-ST18000NT001-3NF101_ZVTE0S3Q"
"ata-ST18000NT001-3NF101_ZVTEF27J"
"ata-ST18000NE000-3G6101_ZVTJ7359"
]
[
"ata-ST4000NE001-2MA101_WS2275P3"
"ata-ST4000NE001-2MA101_WS227B9F"
"ata-ST4000NE001-2MA101_WS227CEW"
"ata-ST4000NE001-2MA101_WS227CYN"
"ata-ST4000NE001-2MA101_WS23TBWV"
"ata-ST4000NE001-2MA101_WS23TC5F"
]
];
cache = [
"nvme-Samsung_SSD_990_PRO_4TB_S7KGNU0X907881F"
];
};
};
network_storage = { network_storage = {
enable = true; enable = true;
directories = [ directories = [
@ -66,53 +104,6 @@
}; };
}; };
storage = {
zfs = {
enable = true;
notifications = {
enable = true;
host = "smtp.protonmail.ch";
port = 587;
to = "leyla@jan-leila.com";
user = "noreply@jan-leila.com";
tokenFile = config.sops.secrets."services/zfs_smtp_token".path;
};
pool = {
encryption = {
enable = true;
};
vdevs = [
[
"ata-ST18000NE000-3G6101_ZVTCXVEB"
"ata-ST18000NE000-3G6101_ZVTCXWSC"
"ata-ST18000NE000-3G6101_ZVTD10EH"
"ata-ST18000NT001-3NF101_ZVTE0S3Q"
"ata-ST18000NT001-3NF101_ZVTEF27J"
"ata-ST18000NE000-3G6101_ZVTJ7359"
]
[
"ata-ST4000NE001-2MA101_WS2275P3"
"ata-ST4000NE001-2MA101_WS227B9F"
"ata-ST4000NE001-2MA101_WS227CEW"
"ata-ST4000NE001-2MA101_WS227CYN"
"ata-ST4000NE001-2MA101_WS23TBWV"
"ata-ST4000NE001-2MA101_WS23TC5F"
]
];
# We are having to boot off of the nvm cache drive because I cant figure out how to boot via the HBA
cache = [
{
device = "nvme-Samsung_SSD_990_PRO_4TB_S7KGNU0X907881F";
boot = true;
}
];
};
};
impermanence = {
enable = true;
};
};
systemd.network = { systemd.network = {
enable = true; enable = true;
@ -143,7 +134,6 @@
Endpoint = "185.230.126.146:51820"; Endpoint = "185.230.126.146:51820";
# Allow all traffic but use policy routing to prevent system-wide VPN # Allow all traffic but use policy routing to prevent system-wide VPN
AllowedIPs = ["0.0.0.0/0"]; AllowedIPs = ["0.0.0.0/0"];
PersistentKeepalive = 25;
} }
]; ];
}; };
@ -227,7 +217,6 @@
postgresql = { postgresql = {
enable = true; enable = true;
adminUsers = ["leyla"]; adminUsers = ["leyla"];
impermanence.enable = false;
}; };
# temp enable desktop environment for setup # temp enable desktop environment for setup
@ -246,7 +235,6 @@
reverseProxy = { reverseProxy = {
enable = true; enable = true;
openFirewall = true; openFirewall = true;
impermanence.enable = false;
acme = { acme = {
enable = true; enable = true;
email = "jan-leila@protonmail.com"; email = "jan-leila@protonmail.com";
@ -256,7 +244,8 @@
ollama = { ollama = {
enable = true; enable = true;
exposePort = true; exposePort = true;
impermanence.enable = false;
acceleration = false;
environmentVariables = { environmentVariables = {
OLLAMA_KEEP_ALIVE = "24h"; OLLAMA_KEEP_ALIVE = "24h";
@ -291,7 +280,6 @@
enable = true; enable = true;
authKeyFile = config.sops.secrets."vpn-keys/tailscale-authkey/defiant".path; authKeyFile = config.sops.secrets."vpn-keys/tailscale-authkey/defiant".path;
useRoutingFeatures = "server"; useRoutingFeatures = "server";
impermanence.enable = false;
extraUpFlags = [ extraUpFlags = [
"--advertise-exit-node" "--advertise-exit-node"
"--advertise-routes=192.168.0.0/24" "--advertise-routes=192.168.0.0/24"
@ -304,33 +292,24 @@
]; ];
}; };
syncthing = { syncthing.enable = true;
enable = true;
impermanence.enable = false;
};
fail2ban = { fail2ban.enable = true;
enable = true;
impermanence.enable = false;
};
jellyfin = { jellyfin = {
enable = true; enable = true;
domain = "media.jan-leila.com"; domain = "media.jan-leila.com";
extraDomains = ["jellyfin.jan-leila.com"]; extraDomains = ["jellyfin.jan-leila.com"];
impermanence.enable = false;
}; };
immich = { immich = {
enable = true; enable = true;
domain = "photos.jan-leila.com"; domain = "photos.jan-leila.com";
impermanence.enable = false;
}; };
forgejo = { forgejo = {
enable = true; enable = true;
reverseProxy.domain = "git.jan-leila.com"; reverseProxy.domain = "git.jan-leila.com";
impermanence.enable = false;
}; };
searx = { searx = {
@ -339,9 +318,8 @@
}; };
actual = { actual = {
enable = false; enable = true;
domain = "budget.jan-leila.com"; domain = "budget.jan-leila.com";
impermanence.enable = false;
}; };
home-assistant = { home-assistant = {
@ -349,7 +327,6 @@
domain = "home.jan-leila.com"; domain = "home.jan-leila.com";
openFirewall = true; openFirewall = true;
postgres.enable = true; postgres.enable = true;
impermanence.enable = false;
extensions = { extensions = {
sonos.enable = true; sonos.enable = true;
@ -362,13 +339,11 @@
enable = true; enable = true;
domain = "documents.jan-leila.com"; domain = "documents.jan-leila.com";
passwordFile = config.sops.secrets."services/paperless_password".path; passwordFile = config.sops.secrets."services/paperless_password".path;
impermanence.enable = false;
}; };
panoramax = { panoramax = {
enable = false; enable = false;
openFirewall = true; openFirewall = true;
impermanence.enable = false;
}; };
crab-hole = { crab-hole = {
@ -376,7 +351,6 @@
port = 8085; port = 8085;
openFirewall = true; openFirewall = true;
show_doc = true; show_doc = true;
impermanence.enable = false;
downstreams = { downstreams = {
host = { host = {
enable = true; enable = true;
@ -392,38 +366,31 @@
mediaDir = "/srv/qbittorent"; mediaDir = "/srv/qbittorent";
openFirewall = true; openFirewall = true;
webuiPort = 8084; webuiPort = 8084;
impermanence.enable = false;
}; };
sonarr = { sonarr = {
enable = true; enable = true;
openFirewall = true; openFirewall = true;
impermanence.enable = false;
}; };
radarr = { radarr = {
enable = true; enable = true;
openFirewall = true; openFirewall = true;
impermanence.enable = false;
}; };
bazarr = { bazarr = {
enable = true; enable = true;
openFirewall = true; openFirewall = true;
impermanence.enable = false;
}; };
lidarr = { lidarr = {
enable = true; enable = true;
openFirewall = true; openFirewall = true;
impermanence.enable = false;
}; };
jackett = { jackett = {
enable = true; enable = true;
openFirewall = true; openFirewall = true;
impermanence.enable = false;
}; };
flaresolverr = { flaresolverr = {
enable = true; enable = true;
openFirewall = true; openFirewall = true;
impermanence.enable = false;
}; };
}; };

View file

@ -4,7 +4,5 @@
./hardware-configuration.nix ./hardware-configuration.nix
./configuration.nix ./configuration.nix
./packages.nix ./packages.nix
./legacy-storage.nix
./legacy-impermanence.nix
]; ];
} }

View file

@ -1,296 +0,0 @@
# Legacy impermanence module for defiant
# See legacy-storage.nix for the full incremental migration plan.
#
# This file is consumed in two phases:
#
# Phase 3 (after generateBase is enabled):
# Remove the SYSTEM-LEVEL entries marked [PHASE 3] below. These will be
# handled automatically by storage.nix, ssh.nix, and the impermanence module:
# - var-lib-private-permissions activation script
# - /etc/machine-id
# - SSH host keys
# - /var/lib/nixos
# - /var/lib/systemd/coredump
# - /persist/system/var/log persistence block
#
# Phase 4 (migrate services one at a time, any order):
# For each service:
# 1. Remove the service's section marked [PHASE 4] from this file
# 2. Remove `impermanence.enable = false` for that service in configuration.nix
# For jellyfin/qbittorrent, also remove the separate media persistence blocks.
#
# Phase 5: Delete this file once empty.
{
config,
lib,
...
}: {
config = lib.mkIf config.storage.impermanence.enable {
# [PHASE 3] Remove this activation script after enabling generateBase
system.activationScripts = {
"var-lib-private-permissions" = {
deps = ["specialfs"];
text = ''
mkdir -p /persist/system/root/var/lib/private
chmod 0700 /persist/system/root/var/lib/private
'';
};
};
environment.persistence."/persist/system/root" = {
enable = true;
hideMounts = true;
# [PHASE 3] Remove this files block after enabling generateBase
files = lib.mkMerge [
["/etc/machine-id"]
# SSH host keys
(lib.mkIf config.services.openssh.enable (
lib.lists.flatten (
builtins.map (hostKey: [
hostKey.path
"${hostKey.path}.pub"
])
config.services.openssh.hostKeys
)
))
];
directories = lib.mkMerge [
# [PHASE 3] Remove these system directories after enabling generateBase
[
"/var/lib/nixos"
"/var/lib/systemd/coredump"
]
# [PHASE 4] PostgreSQL
(lib.mkIf config.services.postgresql.enable [
{
directory = "/var/lib/postgresql/16";
user = "postgres";
group = "postgres";
}
])
# [PHASE 4] Reverse Proxy (ACME)
(lib.mkIf config.services.reverseProxy.enable [
{
directory = "/var/lib/acme";
user = "acme";
group = "acme";
}
])
# [PHASE 4] Ollama
(lib.mkIf config.services.ollama.enable [
{
directory = "/var/lib/private/ollama";
user = config.services.ollama.user;
group = config.services.ollama.group;
mode = "0700";
}
])
# [PHASE 4] Tailscale
(lib.mkIf config.services.tailscale.enable [
{
directory = "/var/lib/tailscale";
user = "root";
group = "root";
}
])
# [PHASE 4] Syncthing
(lib.mkIf config.services.syncthing.enable [
{
directory = "/mnt/sync";
user = "syncthing";
group = "syncthing";
}
{
directory = "/etc/syncthing";
user = "syncthing";
group = "syncthing";
}
])
# [PHASE 4] Fail2ban
(lib.mkIf config.services.fail2ban.enable [
{
directory = "/var/lib/fail2ban";
user = "fail2ban";
group = "fail2ban";
}
])
# [PHASE 4] Jellyfin (data/cache only - media is on separate dataset)
(lib.mkIf config.services.jellyfin.enable [
{
directory = "/var/lib/jellyfin";
user = "jellyfin";
group = "jellyfin";
}
{
directory = "/var/cache/jellyfin";
user = "jellyfin";
group = "jellyfin";
}
])
# [PHASE 4] Immich
(lib.mkIf config.services.immich.enable [
{
directory = "/var/lib/immich";
user = "immich";
group = "immich";
}
])
# [PHASE 4] Forgejo
(lib.mkIf config.services.forgejo.enable [
{
directory = "/var/lib/forgejo";
user = "forgejo";
group = "forgejo";
}
])
# [PHASE 4] Actual
(lib.mkIf config.services.actual.enable [
{
directory = "/var/lib/private/actual";
user = "actual";
group = "actual";
}
])
# [PHASE 4] Home Assistant
(lib.mkIf config.services.home-assistant.enable [
{
directory = "/var/lib/hass";
user = "hass";
group = "hass";
}
])
# [PHASE 4] Paperless
(lib.mkIf config.services.paperless.enable [
{
directory = "/var/lib/paperless";
user = "paperless";
group = "paperless";
}
])
# [PHASE 4] Crab-hole
(lib.mkIf config.services.crab-hole.enable [
{
directory = "/var/lib/private/crab-hole";
user = "crab-hole";
group = "crab-hole";
}
])
# [PHASE 4] qBittorrent (config only - media is on separate dataset)
(lib.mkIf config.services.qbittorrent.enable [
{
directory = "/var/lib/qBittorrent/";
user = "qbittorrent";
group = "qbittorrent";
}
])
# [PHASE 4] Sonarr
(lib.mkIf config.services.sonarr.enable [
{
directory = "/var/lib/sonarr/.config/NzbDrone";
user = "sonarr";
group = "sonarr";
}
])
# [PHASE 4] Radarr
(lib.mkIf config.services.radarr.enable [
{
directory = "/var/lib/radarr/.config/Radarr";
user = "radarr";
group = "radarr";
}
])
# [PHASE 4] Bazarr
(lib.mkIf config.services.bazarr.enable [
{
directory = "/var/lib/bazarr";
user = "bazarr";
group = "bazarr";
}
])
# [PHASE 4] Lidarr
(lib.mkIf config.services.lidarr.enable [
{
directory = "/var/lib/lidarr/.config/Lidarr";
user = "lidarr";
group = "lidarr";
}
])
# [PHASE 4] Jackett
(lib.mkIf config.services.jackett.enable [
{
directory = "/var/lib/jackett/.config/Jackett";
user = "jackett";
group = "jackett";
}
])
# [PHASE 4] FlareSolverr
(lib.mkIf config.services.flaresolverr.enable [
{
directory = "/var/lib/flaresolverr";
user = "flaresolverr";
group = "flaresolverr";
}
])
];
};
# [PHASE 4 - LAST] Jellyfin media on separate dataset
# Requires Phase 2 media dataset merge before migrating (several days of data copy)
environment.persistence."/persist/system/jellyfin" = lib.mkIf config.services.jellyfin.enable {
enable = true;
hideMounts = true;
directories = [
{
directory = config.services.jellyfin.media_directory;
user = "jellyfin";
group = "jellyfin_media";
mode = "1770";
}
];
};
# [PHASE 4 - LAST] qBittorrent media on separate dataset
# Requires Phase 2 media dataset merge before migrating (several days of data copy)
environment.persistence."/persist/system/qbittorrent" = lib.mkIf config.services.qbittorrent.enable {
enable = true;
hideMounts = true;
directories = [
{
directory = config.services.qbittorrent.mediaDir;
user = "qbittorrent";
group = "qbittorrent";
mode = "1775";
}
];
};
# [PHASE 3] /var/log persistence - handled by storage.nix after generateBase
environment.persistence."/persist/system/var/log" = {
enable = true;
hideMounts = true;
directories = [
"/var/log"
];
};
};
}

View file

@ -1,218 +0,0 @@
# Legacy storage configuration for defiant
# This file manually defines ZFS datasets matching the existing on-disk layout
# to allow incremental migration to the new storage module (generateBase = true).
#
# ============================================================================
# INCREMENTAL MIGRATION PLAN
# ============================================================================
#
# Current disk usage (for reference):
# rpool/local/system/nix ~26G (renamed in place, no copy)
# rpool/local/system/sops ~328K (renamed in place, no copy)
# rpool/persist/system/jellyfin ~32T (renamed in place, no copy)
# rpool/persist/system/qbittorrent ~6.5T (copied into media dataset, ~6.5T temp)
# rpool free space ~30T
#
# Phase 1: Migrate base datasets on disk (boot from live USB or rescue)
# All operations in this phase are instant renames -- no data is copied.
#
# Unlock the pool:
# zfs load-key -a
#
# Step 1a: Move nix and sops out of local/ (they go to persist/local/)
# The -p flag auto-creates the parent datasets.
#
# zfs rename -p rpool/local/system/nix rpool/persist/local/nix
# zfs rename -p rpool/local/system/sops rpool/persist/local/system/sops
#
# Step 1b: Rename local/ -> ephemeral/ (takes remaining children with it)
# zfs rename rpool/local rpool/ephemeral
# # This moves: local/system/root -> ephemeral/system/root
# # local/home/leyla -> ephemeral/home/leyla
#
# Step 1c: Recreate blank snapshots on ephemeral datasets
# zfs destroy rpool/ephemeral/system/root@blank
# zfs snapshot rpool/ephemeral/system/root@blank
# zfs destroy rpool/ephemeral/home/leyla@blank
# zfs snapshot rpool/ephemeral/home/leyla@blank
#
# Step 1d: Move persist/ children under persist/replicate/
# zfs create -o canmount=off rpool/persist/replicate
# zfs create -o canmount=off rpool/persist/replicate/system
# zfs rename rpool/persist/system/root rpool/persist/replicate/system/root
# zfs rename rpool/persist/system/var rpool/persist/replicate/system/var
# zfs rename rpool/persist/home/leyla rpool/persist/replicate/home
# # Clean up the now-empty home parent
# zfs destroy rpool/persist/home
# # NOTE: Do NOT destroy rpool/persist/system -- it still contains
# # persist/system/jellyfin and persist/system/qbittorrent which are
# # migrated in Phase 2.
#
# Verify the new layout:
# zfs list -r rpool -o name,used,mountpoint
#
# Phase 2: Merge media into a single dataset (do this last)
# Strategy: Rename the jellyfin dataset to become the shared media dataset
# (zero copy, instant), then copy qbittorrent data into it (~6.5T copy).
# This avoids duplicating the 32T jellyfin dataset.
#
# Step 2a: Rename jellyfin dataset to the shared media name
# zfs rename rpool/persist/system/jellyfin rpool/persist/replicate/system/media
#
# Step 2b: Copy qbittorrent data into the media dataset
# This copies ~6.5T and may take several hours/days depending on disk speed.
# The qbittorrent data is not critical to back up so no snapshot needed.
#
# systemctl stop qbittorrent
# rsync -avPHAX /persist/system/qbittorrent/ /persist/replicate/system/media/
#
# Step 2c: Verify the data and clean up
# ls -la /persist/replicate/system/media/
# zfs destroy rpool/persist/system/qbittorrent
# # persist/system should now be empty, clean it up:
# zfs destroy rpool/persist/system
#
# Phase 3: Enable generateBase
# In the nix config:
# - Delete this file (legacy-storage.nix) and remove its import from default.nix
# - Remove [PHASE 3] entries from legacy-impermanence.nix:
# - var-lib-private-permissions activation script
# - /etc/machine-id, SSH host keys (files block)
# - /var/lib/nixos, /var/lib/systemd/coredump (directories)
# - /persist/system/var/log persistence block
# These are now handled automatically by storage.nix and ssh.nix.
# Rebuild and verify:
# sudo nixos-rebuild switch --flake .#defiant
# # Verify mounts: findmnt -t fuse.bindfs,fuse
# # Verify persist: ls /persist/replicate/system/root/var/lib/nixos
# # Verify boot: reboot and confirm system comes up cleanly
#
# Phase 4: Migrate services (one at a time, any order)
# For each service (except jellyfin/qbittorrent):
# 1. Remove the service's [PHASE 4] section from legacy-impermanence.nix
# 2. Remove `impermanence.enable = false` for that service in configuration.nix
# 3. Rebuild: sudo nixos-rebuild switch --flake .#defiant
# 4. Verify: systemctl status <service>, check the service's data is intact
# No data migration is needed -- the data already lives on the renamed
# dataset at the new path.
#
# Migrate jellyfin and qbittorrent LAST (after Phase 2 media merge):
# 1. Remove [PHASE 4 - LAST] jellyfin entries from legacy-impermanence.nix
# 2. Remove [PHASE 4 - LAST] qbittorrent entries from legacy-impermanence.nix
# 3. Remove `impermanence.enable = false` for both in configuration.nix
# 4. Rebuild: sudo nixos-rebuild switch --flake .#defiant
# 5. Verify: systemctl status jellyfin qbittorrent
#
# Phase 5: Cleanup
# Once all services are migrated and legacy-impermanence.nix is empty:
# - Delete legacy-impermanence.nix and remove its import from default.nix
# - Rebuild: sudo nixos-rebuild switch --flake .#defiant
#
# ============================================================================
#
# Current on-disk dataset layout:
# rpool/local/ - ephemeral parent
# rpool/local/home/leyla - ephemeral user home (rolled back on boot)
# rpool/local/system/nix - nix store
# rpool/local/system/root - root filesystem (rolled back on boot)
# rpool/local/system/sops - sops age key
# rpool/persist/ - persistent parent
# rpool/persist/home/leyla - persistent user home
# rpool/persist/system/jellyfin - jellyfin media
# rpool/persist/system/qbittorrent - qbittorrent media
# rpool/persist/system/root - persistent root data
# rpool/persist/system/var/log - log persistence
{lib, ...}: {
# Disable automatic base dataset generation so we can define them manually
storage.generateBase = false;
# Manually define ZFS datasets matching main's structure
storage.zfs.datasets = {
# Ephemeral datasets (local/)
"local" = {
type = "zfs_fs";
mount = null;
};
"local/home/leyla" = {
type = "zfs_fs";
mount = "/home/leyla";
snapshot = {
blankSnapshot = true;
};
};
"local/system/nix" = {
type = "zfs_fs";
mount = "/nix";
atime = "off";
relatime = "off";
snapshot = {
autoSnapshot = false;
};
};
"local/system/root" = {
type = "zfs_fs";
mount = "/";
snapshot = {
blankSnapshot = true;
};
};
"local/system/sops" = {
type = "zfs_fs";
mount = "/var/lib/sops-nix";
};
# Persistent datasets (persist/)
"persist" = {
type = "zfs_fs";
mount = null;
};
"persist/home/leyla" = {
type = "zfs_fs";
mount = "/persist/home/leyla";
snapshot = {
autoSnapshot = true;
};
};
"persist/system/jellyfin" = {
type = "zfs_fs";
mount = "/persist/system/jellyfin";
atime = "off";
relatime = "off";
};
"persist/system/qbittorrent" = {
type = "zfs_fs";
mount = "/persist/system/qbittorrent";
atime = "off";
relatime = "off";
};
"persist/system/root" = {
type = "zfs_fs";
mount = "/persist/system/root";
snapshot = {
autoSnapshot = true;
};
};
"persist/system/var/log" = {
type = "zfs_fs";
mount = "/persist/system/var/log";
};
};
# Boot commands to rollback ephemeral root and user homes on boot
boot.initrd.postResumeCommands = lib.mkAfter ''
zfs rollback -r rpool/local/system/root@blank
zfs rollback -r rpool/local/home/leyla@blank
'';
# FileSystems needed for boot
fileSystems = {
"/".neededForBoot = true;
"/persist/system/root".neededForBoot = true;
"/persist/system/var/log".neededForBoot = true;
"/persist/system/jellyfin".neededForBoot = true;
"/persist/system/qbittorrent".neededForBoot = true;
"/var/lib/sops-nix".neededForBoot = true;
"/persist/home/leyla".neededForBoot = true;
"/home/leyla".neededForBoot = true;
};
}

View file

@ -59,22 +59,12 @@
hardware = { hardware = {
piperMouse.enable = true; piperMouse.enable = true;
}; };
};
storage = { storage = {
zfs = {
enable = true; enable = true;
pool = { pool = {
mode = "stripe"; mode = "";
vdevs = [ drives = ["wwn-0x5000039fd0cf05eb"];
[
{
device = "wwn-0x5000039fd0cf05eb";
boot = true;
}
]
];
cache = [];
}; };
}; };
}; };
@ -82,7 +72,7 @@
virtualisation.libvirtd.enable = true; virtualisation.libvirtd.enable = true;
users.users.eve = { users.users.eve = {
extraGroups = ["libvirtd"]; extraGroups = [ "libvirtd" ];
}; };
services.tailscale.enable = true; services.tailscale.enable = true;

View file

@ -3,6 +3,5 @@
imports = [ imports = [
./configuration.nix ./configuration.nix
./hardware-configuration.nix ./hardware-configuration.nix
./legacy-storage.nix
]; ];
} }

View file

@ -1,51 +0,0 @@
# Legacy storage configuration for emergent
# This file manually defines ZFS datasets matching the existing on-disk layout
# to allow incremental migration to the new storage module (generateBase = true).
#
# Current on-disk dataset layout:
# rpool/local/ - parent (canmount=off)
# rpool/local/system/nix - nix store
# rpool/local/system/root - root filesystem
#
# Migration plan:
# Phase 1: Rename datasets on disk (boot from live USB)
# zfs rename -p rpool/local/system/nix rpool/persist/local/nix
# zfs rename rpool/local rpool/persist/local
# # This moves: local/system/root -> persist/local/root (need to rename after)
# # Actually, since local/system/root needs to become persist/local/root:
# zfs rename rpool/persist/local/system/root rpool/persist/local/root
# zfs destroy rpool/persist/local/system # now empty
# # Recreate blank snapshot:
# zfs destroy rpool/persist/local/root@blank
# zfs snapshot rpool/persist/local/root@blank
#
# Phase 2: Delete this file, remove its import from default.nix, rebuild.
{...}: {
# Disable automatic base dataset generation so we can define them manually
storage.generateBase = false;
# Manually define ZFS datasets matching the existing on-disk layout
storage.zfs.datasets = {
"local" = {
type = "zfs_fs";
mount = null;
};
"local/system/nix" = {
type = "zfs_fs";
mount = "/nix";
atime = "off";
relatime = "off";
snapshot = {
autoSnapshot = false;
};
};
"local/system/root" = {
type = "zfs_fs";
mount = "/";
snapshot = {
blankSnapshot = true;
autoSnapshot = true;
};
};
};
}

View file

@ -1,4 +1,9 @@
{config, ...}: { {
config,
lib,
pkgs,
...
}: {
# Enable OpenGL # Enable OpenGL
hardware.graphics = { hardware.graphics = {
enable = true; enable = true;

View file

@ -32,6 +32,7 @@
isPrincipleUser = true; isPrincipleUser = true;
}; };
eve.isDesktopUser = true; eve.isDesktopUser = true;
ivy.isDesktopUser = true;
}; };
hardware = { hardware = {
@ -84,15 +85,16 @@
}; };
}; };
virtualisation.docker.enable = true;
environment.systemPackages = with pkgs; [ environment.systemPackages = with pkgs; [
cachefilesd cachefilesd
webtoon-dl webtoon-dl
android-tools
]; ];
services.cachefilesd.enable = true; services.cachefilesd.enable = true;
programs = {
adb.enable = true;
};
networking = { networking = {
networkmanager.enable = true; networkmanager.enable = true;
hostName = "horizon"; # Define your hostname. hostName = "horizon"; # Define your hostname.

View file

@ -4,6 +4,10 @@
pkgs, pkgs,
... ...
}: { }: {
imports = [
./monitors.nix
];
nixpkgs.config.allowUnfree = true; nixpkgs.config.allowUnfree = true;
boot.initrd.availableKernelModules = ["usb_storage"]; boot.initrd.availableKernelModules = ["usb_storage"];

View file

@ -18,12 +18,12 @@
fileSystems = { fileSystems = {
"/" = { "/" = {
device = "/dev/disk/by-id/nvme-Samsung_SSD_980_500GB_S64ENJ0RA06463Z-part2"; device = "/dev/disk/by-id/ata-TOSHIBA_DT01ACA100_77D21HVNS-part2";
fsType = "ext4"; fsType = "ext4";
}; };
"/boot" = { "/boot" = {
device = "/dev/disk/by-id/nvme-Samsung_SSD_980_500GB_S64ENJ0RA06463Z-part1"; device = "/dev/disk/by-id/ata-TOSHIBA_DT01ACA100_77D21HVNS-part1";
fsType = "vfat"; fsType = "vfat";
options = ["fmask=0022" "dmask=0022"]; options = ["fmask=0022" "dmask=0022"];
}; };

View file

@ -0,0 +1,199 @@
{pkgs, ...}: {
systemd.tmpfiles.rules = [
"L+ /run/gdm/.config/monitors.xml - - - - ${pkgs.writeText "gdm-monitors.xml" ''
<monitors version="2">
<configuration>
<logicalmonitor>
<x>0</x>
<y>156</y>
<scale>1</scale>
<monitor>
<monitorspec>
<connector>DP-4</connector>
<vendor>DEL</vendor>
<product>DELL U2719D</product>
<serial>8RGXNS2</serial>
</monitorspec>
<mode>
<width>2560</width>
<height>1440</height>
<rate>59.951</rate>
</mode>
</monitor>
</logicalmonitor>
<logicalmonitor>
<x>2560</x>
<y>324</y>
<scale>1</scale>
<primary>yes</primary>
<monitor>
<monitorspec>
<connector>DP-2</connector>
<vendor>GSM</vendor>
<product>LG ULTRAGEAR</product>
<serial>0x00068c96</serial>
</monitorspec>
<mode>
<width>1920</width>
<height>1080</height>
<rate>240.001</rate>
</mode>
</monitor>
</logicalmonitor>
<logicalmonitor>
<x>4480</x>
<y>0</y>
<scale>1</scale>
<transform>
<rotation>left</rotation>
<flipped>no</flipped>
</transform>
<monitor>
<monitorspec>
<connector>HDMI-0</connector>
<vendor>HWP</vendor>
<product>HP w2207</product>
<serial>CND7332S88</serial>
</monitorspec>
<mode>
<width>1600</width>
<height>1000</height>
<rate>59.999</rate>
</mode>
</monitor>
</logicalmonitor>
</configuration>
<configuration>
<logicalmonitor>
<x>0</x>
<y>0</y>
<scale>1</scale>
<primary>yes</primary>
<monitor>
<monitorspec>
<connector>DP-1</connector>
<vendor>DEL</vendor>
<product>DELL U2719D</product>
<serial>8RGXNS2</serial>
</monitorspec>
<mode>
<width>2560</width>
<height>1440</height>
<rate>59.951</rate>
</mode>
</monitor>
</logicalmonitor>
<logicalmonitor>
<x>4480</x>
<y>226</y>
<scale>1</scale>
<transform>
<rotation>left</rotation>
<flipped>no</flipped>
</transform>
<monitor>
<monitorspec>
<connector>HDMI-1</connector>
<vendor>HWP</vendor>
<product>HP w2207</product>
<serial>CND7332S88</serial>
</monitorspec>
<mode>
<width>1680</width>
<height>1050</height>
<rate>59.954</rate>
</mode>
</monitor>
</logicalmonitor>
<logicalmonitor>
<x>2560</x>
<y>226</y>
<scale>1</scale>
<monitor>
<monitorspec>
<connector>DP-2</connector>
<vendor>GSM</vendor>
<product>LG ULTRAGEAR</product>
<serial>0x00068c96</serial>
</monitorspec>
<mode>
<width>1920</width>
<height>1080</height>
<rate>240.001</rate>
</mode>
</monitor>
</logicalmonitor>
</configuration>
<configuration>
<logicalmonitor>
<x>2560</x>
<y>228</y>
<scale>1</scale>
<primary>yes</primary>
<monitor>
<monitorspec>
<connector>DP-2</connector>
<vendor>GSM</vendor>
<product>LG ULTRAGEAR</product>
<serial>0x00068c96</serial>
</monitorspec>
<mode>
<width>1920</width>
<height>1080</height>
<rate>240.001</rate>
</mode>
</monitor>
</logicalmonitor>
<logicalmonitor>
<x>4480</x>
<y>69</y>
<scale>1</scale>
<transform>
<rotation>left</rotation>
<flipped>no</flipped>
</transform>
<monitor>
<monitorspec>
<connector>HDMI-1</connector>
<vendor>HWP</vendor>
<product>HP w2207</product>
<serial>CND7332S88</serial>
</monitorspec>
<mode>
<width>1680</width>
<height>1050</height>
<rate>59.954</rate>
</mode>
</monitor>
</logicalmonitor>
<logicalmonitor>
<x>0</x>
<y>0</y>
<scale>1</scale>
<monitor>
<monitorspec>
<connector>DP-3</connector>
<vendor>DEL</vendor>
<product>DELL U2719D</product>
<serial>8RGXNS2</serial>
</monitorspec>
<mode>
<width>2560</width>
<height>1440</height>
<rate>59.951</rate>
</mode>
</monitor>
</logicalmonitor>
<disabled>
<monitorspec>
<connector>None-1</connector>
<vendor>unknown</vendor>
<product>unknown</product>
<serial>unknown</serial>
</monitorspec>
</disabled>
</configuration>
</monitors>
''}"
];
}

View file

@ -4,9 +4,8 @@
# Load nvidia driver for Xorg and Wayland # Load nvidia driver for Xorg and Wayland
videoDrivers = ["nvidia"]; videoDrivers = ["nvidia"];
}; };
# Temporarily enable wayland to fix boot issue # Use X instead of wayland for gaming reasons
# TODO: Investigate proper X11 session generation for gaming displayManager.gdm.wayland = false;
displayManager.gdm.wayland = true;
}; };
hardware = { hardware = {

197
flake.lock generated
View file

@ -1,5 +1,23 @@
{ {
"nodes": { "nodes": {
"devshell": {
"inputs": {
"nixpkgs": "nixpkgs"
},
"locked": {
"lastModified": 1741473158,
"narHash": "sha256-kWNaq6wQUbUMlPgw8Y+9/9wP0F8SHkjy24/mN3UAppg=",
"owner": "numtide",
"repo": "devshell",
"rev": "7c9e793ebe66bcba8292989a68c0419b737a22a0",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "devshell",
"type": "github"
}
},
"disko": { "disko": {
"inputs": { "inputs": {
"nixpkgs": [ "nixpkgs": [
@ -7,11 +25,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1772867152, "lastModified": 1763651264,
"narHash": "sha256-RIFgZ4O6Eg+5ysZ8Tqb3YvcqiRaNy440GEY22ltjRrs=", "narHash": "sha256-8vvwZbw0s7YvBMJeyPVpWke6lg6ROgtts5N2/SMCcv4=",
"owner": "nix-community", "owner": "nix-community",
"repo": "disko", "repo": "disko",
"rev": "eaafb89b56e948661d618eefd4757d9ea8d77514", "rev": "e86a89079587497174ccab6d0d142a65811a4fd9",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -28,11 +46,11 @@
}, },
"locked": { "locked": {
"dir": "pkgs/firefox-addons", "dir": "pkgs/firefox-addons",
"lastModified": 1772856163, "lastModified": 1763697825,
"narHash": "sha256-xD+d1+FVhKJ+oFYMTWOdVSBoXS4yeMyVZyDjMXqWEJE=", "narHash": "sha256-AgCCcVPOi1tuzuW5/StlwqBjRWSX62oL97qWuxrq5UA=",
"owner": "rycee", "owner": "rycee",
"repo": "nur-expressions", "repo": "nur-expressions",
"rev": "d358a550c7beac5f04fbc5a786e14af079606689", "rev": "cefce78793603231be226fa77e7ad58e0e4899b8",
"type": "gitlab" "type": "gitlab"
}, },
"original": { "original": {
@ -44,11 +62,11 @@
}, },
"flake-compat": { "flake-compat": {
"locked": { "locked": {
"lastModified": 1767039857, "lastModified": 1761588595,
"narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=", "narHash": "sha256-XKUZz9zewJNUj46b4AJdiRZJAvSZ0Dqj2BNfXvFlJC4=",
"owner": "edolstra", "owner": "edolstra",
"repo": "flake-compat", "repo": "flake-compat",
"rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab", "rev": "f387cd2afec9419c8ee37694406ca490c3f34ee5",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -57,24 +75,6 @@
"type": "github" "type": "github"
} }
}, },
"flake-parts": {
"inputs": {
"nixpkgs-lib": "nixpkgs-lib"
},
"locked": {
"lastModified": 1767609335,
"narHash": "sha256-feveD98mQpptwrAEggBQKJTYbvwwglSbOv53uCfH9PY=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "250481aafeb741edfe23d29195671c19b36b6dca",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"flake-utils": { "flake-utils": {
"inputs": { "inputs": {
"systems": "systems" "systems": "systems"
@ -93,6 +93,24 @@
"type": "github" "type": "github"
} }
}, },
"flake-utils_2": {
"inputs": {
"systems": "systems_2"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"flakey-profile": { "flakey-profile": {
"locked": { "locked": {
"lastModified": 1712898590, "lastModified": 1712898590,
@ -115,11 +133,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1772845525, "lastModified": 1763748372,
"narHash": "sha256-Dp5Ir2u4jJDGCgeMRviHvEQDe+U37hMxp6RSNOoMMPc=", "narHash": "sha256-AUc78Qv3sWir0hvbmfXoZ7Jzq9VVL97l+sP9Jgms+JU=",
"owner": "nix-community", "owner": "nix-community",
"repo": "home-manager", "repo": "home-manager",
"rev": "27b93804fbef1544cb07718d3f0a451f4c4cd6c0", "rev": "d10a9b16b2a3ee28433f3d1c603f4e9f1fecb8e1",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -129,20 +147,12 @@
} }
}, },
"impermanence": { "impermanence": {
"inputs": {
"home-manager": [
"home-manager"
],
"nixpkgs": [
"nixpkgs"
]
},
"locked": { "locked": {
"lastModified": 1769548169, "lastModified": 1737831083,
"narHash": "sha256-03+JxvzmfwRu+5JafM0DLbxgHttOQZkUtDWBmeUkN8Y=", "narHash": "sha256-LJggUHbpyeDvNagTUrdhe/pRVp4pnS6wVKALS782gRI=",
"owner": "nix-community", "owner": "nix-community",
"repo": "impermanence", "repo": "impermanence",
"rev": "7b1d382faf603b6d264f58627330f9faa5cba149", "rev": "4b3e914cdf97a5b536a889e939fb2fd2b043a170",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -175,11 +185,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1767364176, "lastModified": 1763435414,
"narHash": "sha256-l6YdEBYQxXjD8ujqvc0tKdwWc3K8UQOi+E4Y3DKQ318=", "narHash": "sha256-i2467FddWfd19q5Qoj+1/BAeg6LZmM5m4mYGRSQn/as=",
"ref": "refs/heads/main", "ref": "refs/heads/main",
"rev": "1688100bba140492658d597f6b307c327f35c780", "rev": "192c92b603731fbc1bade6c1b18c8d8a0086f703",
"revCount": 179, "revCount": 169,
"type": "git", "type": "git",
"url": "https://git.lix.systems/lix-project/nixos-module.git" "url": "https://git.lix.systems/lix-project/nixos-module.git"
}, },
@ -190,15 +200,18 @@
}, },
"mcp-nixos": { "mcp-nixos": {
"inputs": { "inputs": {
"flake-parts": "flake-parts", "devshell": "devshell",
"nixpkgs": "nixpkgs" "flake-utils": "flake-utils_2",
"nixpkgs": [
"nixpkgs"
]
}, },
"locked": { "locked": {
"lastModified": 1772769318, "lastModified": 1760821194,
"narHash": "sha256-RAyOW5JMXRhiREqxFPOzw80fVsYVBnOPFgBSjnJ6gbY=", "narHash": "sha256-UCsJ8eDuHL14u2GFIYEY/drtZ6jht5zN/G/6QNlEy2g=",
"owner": "utensils", "owner": "utensils",
"repo": "mcp-nixos", "repo": "mcp-nixos",
"rev": "60c1efbba0de1268b42f1144c904e6c8a9627dde", "rev": "0ae453f38d0f088c31d4678da3a12b183165986f",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -214,11 +227,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1772379624, "lastModified": 1763505477,
"narHash": "sha256-NG9LLTWlz4YiaTAiRGChbrzbVxBfX+Auq4Ab/SWmk4A=", "narHash": "sha256-nJRd4LY2kT3OELfHqdgWjvToNZ4w+zKCMzS2R6z4sXE=",
"owner": "LnL7", "owner": "LnL7",
"repo": "nix-darwin", "repo": "nix-darwin",
"rev": "52d061516108769656a8bd9c6e811c677ec5b462", "rev": "3bda9f6b14161becbd07b3c56411f1670e19b9b5",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -255,11 +268,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1772850876, "lastModified": 1763690163,
"narHash": "sha256-Ga19zlfMpakCY4GMwBSOljNLOF0nEYrYBXv0hP/d4rw=", "narHash": "sha256-MMl9P8f17unCvlk2IAinnMq/P72f51UUHVRIYnojT7w=",
"owner": "nix-community", "owner": "nix-community",
"repo": "nix-vscode-extensions", "repo": "nix-vscode-extensions",
"rev": "22f084d4c280dfc8a9d764f7b85af38e5d69c3dc", "rev": "590349d9faeb398a037205c2927ffbaede980539",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -270,11 +283,11 @@
}, },
"nixos-hardware": { "nixos-hardware": {
"locked": { "locked": {
"lastModified": 1771969195, "lastModified": 1762847253,
"narHash": "sha256-qwcDBtrRvJbrrnv1lf/pREQi8t2hWZxVAyeMo7/E9sw=", "narHash": "sha256-BWWnUUT01lPwCWUvS0p6Px5UOBFeXJ8jR+ZdLX8IbrU=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixos-hardware", "repo": "nixos-hardware",
"rev": "41c6b421bdc301b2624486e11905c9af7b8ec68e", "rev": "899dc449bc6428b9ee6b3b8f771ca2b0ef945ab9",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -286,42 +299,27 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1767640445, "lastModified": 1722073938,
"narHash": "sha256-UWYqmD7JFBEDBHWYcqE6s6c77pWdcU/i+bwD6XxMb8A=", "narHash": "sha256-OpX0StkL8vpXyWOGUD6G+MA26wAXK6SpT94kLJXo6B4=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "9f0c42f8bc7151b8e7e5840fb3bd454ad850d8c5", "rev": "e36e9f57337d0ff0cf77aceb58af4c805472bfae",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "NixOS", "owner": "NixOS",
"ref": "nixos-unstable", "ref": "nixpkgs-unstable",
"repo": "nixpkgs", "repo": "nixpkgs",
"type": "github" "type": "github"
} }
}, },
"nixpkgs-lib": {
"locked": {
"lastModified": 1765674936,
"narHash": "sha256-k00uTP4JNfmejrCLJOwdObYC9jHRrr/5M/a/8L2EIdo=",
"owner": "nix-community",
"repo": "nixpkgs.lib",
"rev": "2075416fcb47225d9b68ac469a5c4801a9c4dd85",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nixpkgs.lib",
"type": "github"
}
},
"nixpkgs_2": { "nixpkgs_2": {
"locked": { "locked": {
"lastModified": 1772773019, "lastModified": 1763421233,
"narHash": "sha256-E1bxHxNKfDoQUuvriG71+f+s/NT0qWkImXsYZNFFfCs=", "narHash": "sha256-Stk9ZYRkGrnnpyJ4eqt9eQtdFWRRIvMxpNRf4sIegnw=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "aca4d95fce4914b3892661bcb80b8087293536c6", "rev": "89c2b2330e733d6cdb5eae7b899326930c2c0648",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -351,14 +349,14 @@
"inputs": { "inputs": {
"nixpkgs": "nixpkgs_3", "nixpkgs": "nixpkgs_3",
"rust-overlay": "rust-overlay", "rust-overlay": "rust-overlay",
"systems": "systems_2" "systems": "systems_3"
}, },
"locked": { "locked": {
"lastModified": 1771445312, "lastModified": 1764204484,
"narHash": "sha256-8uOcu+ZurGx0LmGFCf87Zbj4ikhVPQtP+PuBscEBCv0=", "narHash": "sha256-S45ghD/YjcKDy8Mz3DYklLMaA/z6f6mTbx0i7pAktYk=",
"owner": "IntQuant", "owner": "IntQuant",
"repo": "noita_entangled_worlds", "repo": "noita_entangled_worlds",
"rev": "4a842f29d0e5fb8dc6df73d87f7bb8d2a16f0fc8", "rev": "ab2c2162157140ab519fa19f6737c044e1ed0e3b",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -411,11 +409,11 @@
"secrets": { "secrets": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1768867162, "lastModified": 1759945215,
"narHash": "sha256-NiW2gUcdhnUbYQw476HzgBz+uVjyLnz151hzCQbWBX8=", "narHash": "sha256-xmUzOuhJl6FtTjR5++OQvSoAnXe7/VA5QFCZDyFwBXo=",
"ref": "refs/heads/main", "ref": "refs/heads/main",
"rev": "22be81505a49cd205e9b5c91f51af69c0b885ed3", "rev": "444229a105445339fb028d15a8d866063c5f8141",
"revCount": 23, "revCount": 21,
"type": "git", "type": "git",
"url": "ssh://git@git.jan-leila.com/jan-leila/nix-config-secrets.git" "url": "ssh://git@git.jan-leila.com/jan-leila/nix-config-secrets.git"
}, },
@ -431,11 +429,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1772495394, "lastModified": 1763607916,
"narHash": "sha256-hmIvE/slLKEFKNEJz27IZ8BKlAaZDcjIHmkZ7GCEjfw=", "narHash": "sha256-VefBA1JWRXM929mBAFohFUtQJLUnEwZ2vmYUNkFnSjE=",
"owner": "Mic92", "owner": "Mic92",
"repo": "sops-nix", "repo": "sops-nix",
"rev": "1d9b98a29a45abe9c4d3174bd36de9f28755e3ff", "rev": "877bb495a6f8faf0d89fc10bd142c4b7ed2bcc0b",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -460,6 +458,21 @@
} }
}, },
"systems_2": { "systems_2": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"systems_3": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1681028828, "lastModified": 1681028828,

View file

@ -37,8 +37,6 @@
# delete your darlings # delete your darlings
impermanence = { impermanence = {
url = "github:nix-community/impermanence"; url = "github:nix-community/impermanence";
inputs.nixpkgs.follows = "nixpkgs";
inputs.home-manager.follows = "home-manager";
}; };
nix-darwin = { nix-darwin = {
@ -77,7 +75,7 @@
# MCP NixOS server for Claude Dev # MCP NixOS server for Claude Dev
mcp-nixos = { mcp-nixos = {
url = "github:utensils/mcp-nixos"; url = "github:utensils/mcp-nixos";
# Not following nixpkgs because aws-sam-translator doesn't support Python 3.14 yet inputs.nixpkgs.follows = "nixpkgs";
}; };
# Noita Entangled Worlds package # Noita Entangled Worlds package

File diff suppressed because it is too large Load diff

View file

@ -1,53 +0,0 @@
{
lib,
buildNpmPackage,
fetchurl,
ripgrep,
makeWrapper,
jq,
...
}:
buildNpmPackage rec {
pname = "cline";
version = "2.4.2";
src = fetchurl {
url = "https://registry.npmjs.org/cline/-/cline-${version}.tgz";
hash = "sha256-2utOBC0vhoj5fR+cG+Vdo3N6+i/pNW1E4mESF/dZS/c=";
};
sourceRoot = "package";
postPatch = ''
cp ${./cline-package-lock.json} package-lock.json
# Remove @vscode/ripgrep from package.json since it tries to download
# a binary from GitHub during install, which fails in the nix sandbox.
# We provide ripgrep from nixpkgs instead via PATH wrapping.
# Also remove the man field since the man page is not included in the npm tarball.
${jq}/bin/jq 'del(.dependencies["@vscode/ripgrep"]) | del(.man)' package.json > package.json.tmp
mv package.json.tmp package.json
'';
npmDepsHash = "sha256-oHo60ghR7A4SUT0cLmIe7glPDYBK3twJ0F71RKVrxQc=";
dontNpmBuild = true;
# Skip post-install scripts to be safe
npmFlags = ["--ignore-scripts"];
nativeBuildInputs = [makeWrapper jq];
# Provide ripgrep from nixpkgs since @vscode/ripgrep was removed
postInstall = ''
wrapProgram $out/bin/cline \
--prefix PATH : ${lib.makeBinPath [ripgrep]}
'';
meta = with lib; {
description = "Autonomous coding agent CLI - capable of creating/editing files, running commands, using the browser, and more";
homepage = "https://cline.bot";
license = licenses.asl20;
mainProgram = "cline";
};
}

View file

@ -44,11 +44,5 @@
# Override h3 C library to version 4.3.0 # Override h3 C library to version 4.3.0
h3 = pkgs.callPackage ./h3-c-lib.nix {}; h3 = pkgs.callPackage ./h3-c-lib.nix {};
}) })
(final: prev: {
cline = pkgs.callPackage ./cline/default.nix {};
})
(final: prev: {
e621-downloader = pkgs.callPackage ./e621-downloader.nix {};
})
]; ];
} }

View file

@ -1,36 +0,0 @@
{
lib,
rustPlatform,
fetchFromGitHub,
pkg-config,
openssl,
...
}:
rustPlatform.buildRustPackage rec {
pname = "e621-downloader";
version = "1.7.2";
src = fetchFromGitHub {
owner = "McSib";
repo = "e621_downloader";
rev = version;
hash = "sha256-4z+PrCv8Mlp0VOJ5Akv1TXrJir1Ws/+45a6VCZGuCtk=";
};
cargoHash = "sha256-/yqNYjP7BuFQWilL2Ty+E5rd8qXj30twteptHx7cLRo=";
nativeBuildInputs = [
pkg-config
];
buildInputs = [
openssl
];
meta = with lib; {
description = "E621 and E926 downloader made in Rust";
homepage = "https://github.com/McSib/e621_downloader";
license = licenses.asl20;
mainProgram = "e621_downloader";
};
}

View file

@ -4,12 +4,8 @@
makeWrapper, makeWrapper,
jdk, jdk,
lib, lib,
xorg,
libGL, libGL,
libx11,
libxcursor,
libxext,
libxrandr,
libxxf86vm,
... ...
}: }:
stdenv.mkDerivation rec { stdenv.mkDerivation rec {
@ -28,11 +24,11 @@ stdenv.mkDerivation rec {
runtimeDependencies = lib.makeLibraryPath [ runtimeDependencies = lib.makeLibraryPath [
# glfw # glfw
libGL libGL
libx11 xorg.libX11
libxcursor xorg.libXcursor
libxext xorg.libXext
libxrandr xorg.libXrandr
libxxf86vm xorg.libXxf86vm
]; ];
installPhase = '' installPhase = ''

View file

@ -12,29 +12,21 @@ in {
type = lib.types.bool; type = lib.types.bool;
default = true; default = true;
}; };
persistencePath = lib.mkOption {
type = lib.types.str;
default =
if osConfig.storage.generateBase
then "/persist/replicate/home"
else "/persist";
description = "The base path for user home persistence. The impermanence module will automatically append the user's home directory path. Automatically adapts based on whether the system uses the new dataset layout or the legacy one.";
};
}; };
config = lib.mkMerge [ config = lib.mkMerge [
(lib.mkIf config.impermanence.enable { (lib.mkIf config.impermanence.enable {
assertions = [ assertions = [
{ {
assertion = osConfig.storage.impermanence.enable; assertion = osConfig.host.impermanence.enable;
message = "impermanence can not be enabled for a user when it is not enabled for the system"; message = "impermanence can not be enabled for a user when it is not enabled for the system";
} }
]; ];
}) })
# If impermanence is not enabled for this user but system impermanence is enabled, # If impermanence is not enabled for this user but system impermanence is enabled,
# persist the entire home directory as fallback # persist the entire home directory as fallback
(lib.mkIf (osConfig.storage.impermanence.enable && !cfg.enable && cfg.fallbackPersistence.enable) { (lib.mkIf (osConfig.host.impermanence.enable && !cfg.enable && cfg.fallbackPersistence.enable) {
home.persistence."${cfg.persistencePath}" = { home.persistence."/persist/home/${config.home.username}" = {
directories = ["."]; directories = ["."];
allowOther = true; allowOther = true;
}; };

View file

@ -52,9 +52,9 @@
addKeysToAgent = "confirm"; addKeysToAgent = "confirm";
}; };
}; };
# extraConfig = lib.strings.concatLines ( extraConfig = lib.strings.concatLines (
# builtins.map (hostKey: "IdentityFile ~/.ssh/${hostKey.path}") config.programs.openssh.hostKeys builtins.map (hostKey: "IdentityFile ~/.ssh/${hostKey.path}") config.programs.openssh.hostKeys
# ); );
}; };
systemd.user.services = builtins.listToAttrs ( systemd.user.services = builtins.listToAttrs (
@ -96,7 +96,7 @@
} }
) )
(lib.mkIf config.impermanence.enable { (lib.mkIf config.impermanence.enable {
home.persistence."${config.impermanence.persistencePath}" = { home.persistence."/persist${config.home.homeDirectory}" = {
files = lib.lists.flatten ( files = lib.lists.flatten (
builtins.map (hostKey: [".ssh/${hostKey.path}" ".ssh/${hostKey.path}.pub"]) config.programs.openssh.hostKeys builtins.map (hostKey: [".ssh/${hostKey.path}" ".ssh/${hostKey.path}.pub"]) config.programs.openssh.hostKeys
); );

View file

@ -1,30 +0,0 @@
{
lib,
pkgs,
config,
...
}: {
options.programs.android-studio = {
enable = lib.mkEnableOption "enable android-studio";
};
config = lib.mkIf config.programs.android-studio.enable (lib.mkMerge [
{
home.packages = with pkgs; [
android-studio
];
}
(
lib.mkIf config.impermanence.enable {
home.persistence."${config.impermanence.persistencePath}" = {
directories = [
"${config.xdg.configHome}/Google/AndroidStudio"
".android"
".gradle"
"${config.xdg.cacheHome}/Google/AndroidStudio"
];
};
}
)
]);
}

View file

@ -1,13 +1,15 @@
{ {
lib, lib,
config, config,
osConfig,
... ...
}: { }: {
config = lib.mkIf (config.programs.anki.enable && config.impermanence.enable) { config = lib.mkIf (config.programs.anki.enable && osConfig.host.impermanence.enable) {
home.persistence."${config.impermanence.persistencePath}" = { home.persistence."/persist${config.home.homeDirectory}" = {
directories = [ directories = [
".local/share/Anki2" "${config.xdg.dataHome}/Anki2/"
]; ];
allowOther = true;
}; };
}; };
} }

View file

@ -16,10 +16,11 @@
} }
( (
lib.mkIf config.impermanence.enable { lib.mkIf config.impermanence.enable {
home.persistence."${config.impermanence.persistencePath}" = { home.persistence."/persist${config.home.homeDirectory}" = {
directories = [ directories = [
"${config.xdg.configHome}/Bitwarden" "${config.xdg.configHome}/Bitwarden"
]; ];
allowOther = true;
}; };
} }
) )

View file

@ -16,10 +16,11 @@
} }
( (
lib.mkIf config.impermanence.enable { lib.mkIf config.impermanence.enable {
home.persistence."${config.impermanence.persistencePath}" = { home.persistence."/persist${config.home.homeDirectory}" = {
directories = [ directories = [
"${config.xdg.configHome}/bruno/" "${config.xdg.configHome}/bruno/"
]; ];
allowOther = true;
}; };
} }
) )

View file

@ -4,6 +4,10 @@
config, config,
... ...
}: { }: {
options.programs.calibre = {
enable = lib.mkEnableOption "enable calibre";
};
config = lib.mkIf config.programs.calibre.enable (lib.mkMerge [ config = lib.mkIf config.programs.calibre.enable (lib.mkMerge [
{ {
home.packages = with pkgs; [ home.packages = with pkgs; [
@ -12,10 +16,11 @@
} }
( (
lib.mkIf config.impermanence.enable { lib.mkIf config.impermanence.enable {
home.persistence."${config.impermanence.persistencePath}" = { home.persistence."/persist${config.home.homeDirectory}" = {
directories = [ directories = [
"${config.xdg.configHome}/calibre" "${config.xdg.configHome}/calibre"
]; ];
allowOther = true;
}; };
} }
) )

View file

@ -16,11 +16,12 @@
} }
( (
lib.mkIf config.impermanence.enable { lib.mkIf config.impermanence.enable {
home.persistence."${config.impermanence.persistencePath}" = { home.persistence."/persist${config.home.homeDirectory}" = {
directories = [ directories = [
"${config.xdg.dataHome}/DaVinciResolve" "${config.xdg.dataHome}/DaVinciResolve"
"${config.xdg.configHome}/blackmagic" "${config.xdg.configHome}/blackmagic"
]; ];
allowOther = true;
}; };
} }
) )

View file

@ -16,10 +16,11 @@
} }
( (
lib.mkIf config.impermanence.enable { lib.mkIf config.impermanence.enable {
home.persistence."${config.impermanence.persistencePath}" = { home.persistence."/persist${config.home.homeDirectory}" = {
directories = [ directories = [
"${config.xdg.dataHome}/DBeaverData/" "${config.xdg.dataHome}/DBeaverData/"
]; ];
allowOther = true;
}; };
} }
) )

View file

@ -1,6 +1,5 @@
{...}: { {...}: {
imports = [ imports = [
./android-studio.nix
./firefox.nix ./firefox.nix
./signal.nix ./signal.nix
./bitwarden.nix ./bitwarden.nix
@ -14,7 +13,6 @@
./prostudiomasters.nix ./prostudiomasters.nix
./idea.nix ./idea.nix
./kdenlive.nix ./kdenlive.nix
./kicad.nix
./krita.nix ./krita.nix
./protonvpn.nix ./protonvpn.nix
./calibre.nix ./calibre.nix
@ -47,9 +45,5 @@
./gdx-liftoff.nix ./gdx-liftoff.nix
./tor-browser.nix ./tor-browser.nix
./vmware-workstation.nix ./vmware-workstation.nix
./proton-mail-pwa.nix
./proton-calendar-pwa.nix
./matrix-cyberia-pwa.nix
./e621-downloader.nix
]; ];
} }

View file

@ -6,10 +6,11 @@
config = lib.mkIf config.programs.discord.enable (lib.mkMerge [ config = lib.mkIf config.programs.discord.enable (lib.mkMerge [
( (
lib.mkIf config.impermanence.enable { lib.mkIf config.impermanence.enable {
home.persistence."${config.impermanence.persistencePath}" = { home.persistence."/persist${config.home.homeDirectory}" = {
directories = [ directories = [
"${config.xdg.configHome}/discord/" "${config.xdg.configHome}/discord/"
]; ];
allowOther = true;
}; };
} }
) )

View file

@ -1,16 +0,0 @@
{
lib,
pkgs,
config,
...
}: {
options.programs.e621-downloader = {
enable = lib.mkEnableOption "enable e621-downloader";
};
config = lib.mkIf config.programs.e621-downloader.enable {
home.packages = with pkgs; [
e621-downloader
];
};
}

View file

@ -22,10 +22,11 @@
# Extension configuration # Extension configuration
".mozilla/firefox/${profile}/extension-settings.json" ".mozilla/firefox/${profile}/extension-settings.json"
]; ];
allowOther = true;
}; };
in { in {
config = lib.mkIf (config.programs.firefox.enable && config.impermanence.enable) { config = lib.mkIf (config.programs.firefox.enable && config.impermanence.enable) {
home.persistence."${config.impermanence.persistencePath}" = lib.mkMerge ( home.persistence."/persist${config.home.homeDirectory}" = lib.mkMerge (
( (
lib.attrsets.mapAttrsToList lib.attrsets.mapAttrsToList
(profile: _: buildProfilePersistence profile) (profile: _: buildProfilePersistence profile)

View file

@ -16,10 +16,11 @@
} }
( (
lib.mkIf config.impermanence.enable { lib.mkIf config.impermanence.enable {
home.persistence."${config.impermanence.persistencePath}" = { home.persistence."/persist${config.home.homeDirectory}" = {
directories = [ directories = [
"${config.xdg.configHome}/FreeCAD" "${config.xdg.configHome}/FreeCAD"
]; ];
allowOther = true;
}; };
} }
) )

View file

@ -16,10 +16,11 @@
} }
( (
lib.mkIf config.impermanence.enable { lib.mkIf config.impermanence.enable {
home.persistence."${config.impermanence.persistencePath}" = { home.persistence."/persist${config.home.homeDirectory}" = {
directories = [ directories = [
"${config.xdg.configHome}/GIMP" "${config.xdg.configHome}/GIMP"
]; ];
allowOther = true;
}; };
} }
) )

View file

@ -4,19 +4,19 @@
config, config,
... ...
}: { }: {
options.programs.jetbrains.idea-oss = { options.programs.jetbrains.idea-community = {
enable = lib.mkEnableOption "enable idea-oss"; enable = lib.mkEnableOption "enable idea-community";
}; };
config = lib.mkIf config.programs.jetbrains.idea-oss.enable (lib.mkMerge [ config = lib.mkIf config.programs.jetbrains.idea-community.enable (lib.mkMerge [
{ {
home.packages = with pkgs; [ home.packages = with pkgs; [
jetbrains.idea-oss jetbrains.idea-community
]; ];
} }
( (
lib.mkIf config.impermanence.enable { lib.mkIf config.impermanence.enable {
home.persistence."${config.impermanence.persistencePath}" = { home.persistence."/persist${config.home.homeDirectory}" = {
directories = [ directories = [
# configuration # configuration
"${config.xdg.configHome}/JetBrains/" "${config.xdg.configHome}/JetBrains/"

View file

@ -16,10 +16,11 @@
} }
( (
lib.mkIf config.impermanence.enable { lib.mkIf config.impermanence.enable {
home.persistence."${config.impermanence.persistencePath}" = { home.persistence."/persist${config.home.homeDirectory}" = {
directories = [ directories = [
"${config.xdg.configHome}/inkscape" "${config.xdg.configHome}/inkscape"
]; ];
allowOther = true;
}; };
} }
) )

View file

@ -23,11 +23,12 @@ in {
} }
( (
lib.mkIf config.impermanence.enable { lib.mkIf config.impermanence.enable {
home.persistence."${config.impermanence.persistencePath}" = { home.persistence."/persist${config.home.homeDirectory}" = {
directories = [ directories = [
"${config.xdg.configHome}/kdenliverc" "${config.xdg.configHome}/kdenliverc"
"${config.xdg.dataHome}/kdenlive" "${config.xdg.dataHome}/kdenlive"
]; ];
allowOther = true;
}; };
} }
) )

View file

@ -1,23 +0,0 @@
{
lib,
pkgs,
config,
...
}: {
options.programs.kicad = {
enable = lib.mkEnableOption "enable kicad";
};
config = lib.mkIf config.programs.kicad.enable (lib.mkMerge [
{
home.packages = with pkgs; [
kicad
];
}
(
lib.mkIf config.impermanence.enable {
# TODO:
}
)
]);
}

View file

@ -16,11 +16,12 @@
} }
( (
lib.mkIf config.impermanence.enable { lib.mkIf config.impermanence.enable {
home.persistence."${config.impermanence.persistencePath}" = { home.persistence."/persist${config.home.homeDirectory}" = {
directories = [ directories = [
"${config.xdg.configHome}/kritarc" "${config.xdg.configHome}/kritarc"
"${config.xdg.dataHome}/krita" "${config.xdg.dataHome}/krita"
]; ];
allowOther = true;
}; };
} }
) )

View file

@ -16,10 +16,11 @@
} }
( (
lib.mkIf config.impermanence.enable { lib.mkIf config.impermanence.enable {
home.persistence."${config.impermanence.persistencePath}" = { home.persistence."/persist${config.home.homeDirectory}" = {
directories = [ directories = [
"${config.xdg.configHome}/libreoffice" "${config.xdg.configHome}/libreoffice"
]; ];
allowOther = true;
}; };
} }
) )

View file

@ -30,7 +30,7 @@
} }
( (
lib.mkIf config.impermanence.enable { lib.mkIf config.impermanence.enable {
home.persistence."${config.impermanence.persistencePath}" = { home.persistence."/persist${config.home.homeDirectory}" = {
directories = [ directories = [
".MakeMKV" ".MakeMKV"
]; ];

View file

@ -17,11 +17,12 @@ in {
} }
( (
mkIf config.impermanence.enable { mkIf config.impermanence.enable {
home.persistence."${config.impermanence.persistencePath}" = { home.persistence."/persist${config.home.homeDirectory}" = {
directories = [ directories = [
"${config.xdg.configHome}/mapillary-uploader" "${config.xdg.configHome}/mapillary-uploader"
"${config.xdg.dataHome}/mapillary-uploader" "${config.xdg.dataHome}/mapillary-uploader"
]; ];
allowOther = true;
}; };
} }
) )

View file

@ -1,56 +0,0 @@
{
lib,
pkgs,
config,
...
}: let
cfg = config.programs.matrix-cyberia-pwa;
isChromium = cfg.package == pkgs.chromium;
isBrowserImpermanenceSupported = cfg.package == pkgs.chromium;
in {
options.programs.matrix-cyberia-pwa = {
enable = lib.mkEnableOption "enable Matrix Cyberia PWA";
package = lib.mkOption {
type = lib.types.package;
default = pkgs.chromium;
description = "Browser package to use for the PWA";
};
impermanence = {
enable = lib.mkOption {
type = lib.types.bool;
default = isBrowserImpermanenceSupported;
description = "Enable impermanence configuration for the PWA. Only automatically enabled when using chromium.";
};
};
};
config = lib.mkIf cfg.enable (lib.mkMerge [
{
warnings =
lib.optional (config.impermanence.enable && !isBrowserImpermanenceSupported)
"matrix-cyberia-pwa: Using unsupported package. You will need to manually configure pwa for ${cfg.package.pname}. Supported package(s) ${pkgs.chromium.pname}";
}
(
lib.mkIf isChromium {
xdg.desktopEntries.matrix-cyberia-pwa = {
name = "Matrix (Cyberia)";
type = "Application";
exec = "${cfg.package}/bin/${cfg.package.pname} --app=https://chat.cyberia.club/";
icon = "matrix";
terminal = false;
categories = ["Network" "InstantMessaging"];
};
}
)
(
lib.mkIf (config.impermanence.enable && cfg.impermanence.enable && isChromium) {
home.persistence."/persist${config.home.homeDirectory}" = {
directories = [
"${config.xdg.configHome}/chromium"
];
allowOther = true;
};
}
)
]);
}

View file

@ -6,10 +6,11 @@
config = lib.mkIf config.programs.obs-studio.enable (lib.mkMerge [ config = lib.mkIf config.programs.obs-studio.enable (lib.mkMerge [
( (
lib.mkIf config.impermanence.enable { lib.mkIf config.impermanence.enable {
home.persistence."${config.impermanence.persistencePath}" = { home.persistence."/persist${config.home.homeDirectory}" = {
directories = [ directories = [
"${config.xdg.configHome}/obs-studio" "${config.xdg.configHome}/obs-studio"
]; ];
allowOther = true;
}; };
} }
) )

View file

@ -6,7 +6,7 @@
config = lib.mkIf config.programs.obsidian.enable (lib.mkMerge [ config = lib.mkIf config.programs.obsidian.enable (lib.mkMerge [
( (
lib.mkIf config.impermanence.enable { lib.mkIf config.impermanence.enable {
home.persistence."${config.impermanence.persistencePath}" = { home.persistence."/persist${config.home.homeDirectory}" = {
directories = [ directories = [
"${config.xdg.configHome}/obsidian" "${config.xdg.configHome}/obsidian"
]; ];

View file

@ -23,11 +23,12 @@ in {
} }
( (
lib.mkIf config.impermanence.enable { lib.mkIf config.impermanence.enable {
home.persistence."${config.impermanence.persistencePath}" = { home.persistence."/persist${config.home.homeDirectory}" = {
directories = [ directories = [
"${config.xdg.configHome}/olympus" "${config.xdg.configHome}/olympus"
"${config.xdg.dataHome}/olympus" "${config.xdg.dataHome}/olympus"
]; ];
allowOther = true;
}; };
} }
) )

View file

@ -16,10 +16,11 @@
} }
( (
lib.mkIf config.impermanence.enable { lib.mkIf config.impermanence.enable {
home.persistence."${config.impermanence.persistencePath}" = { home.persistence."/persist${config.home.homeDirectory}" = {
directories = [ directories = [
"${config.xdg.configHome}/OpenRGB" "${config.xdg.configHome}/OpenRGB"
]; ];
allowOther = true;
}; };
} }
) )

View file

@ -16,10 +16,11 @@
} }
( (
lib.mkIf config.impermanence.enable { lib.mkIf config.impermanence.enable {
home.persistence."${config.impermanence.persistencePath}" = { home.persistence."/persist${config.home.homeDirectory}" = {
directories = [ directories = [
"${config.xdg.configHome}/MusicBrainz" "${config.xdg.configHome}/MusicBrainz"
]; ];
allowOther = true;
}; };
} }
) )

View file

@ -16,7 +16,7 @@
} }
( (
lib.mkIf config.impermanence.enable { lib.mkIf config.impermanence.enable {
home.persistence."${config.impermanence.persistencePath}" = { home.persistence."/persist${config.home.homeDirectory}" = {
directories = [ directories = [
"${config.xdg.configHome}/ProStudioMasters" "${config.xdg.configHome}/ProStudioMasters"
]; ];

View file

@ -1,55 +0,0 @@
{
lib,
pkgs,
config,
...
}: let
cfg = config.programs.proton-calendar-pwa;
isChromium = cfg.package == pkgs.chromium;
isBrowserImpermanenceSupported = cfg.package == pkgs.chromium;
in {
options.programs.proton-calendar-pwa = {
enable = lib.mkEnableOption "enable Proton Calendar PWA";
package = lib.mkOption {
type = lib.types.package;
default = pkgs.chromium;
description = "Browser package to use for the PWA";
};
impermanence = {
enable = lib.mkOption {
type = lib.types.bool;
default = isBrowserImpermanenceSupported;
description = "Enable impermanence configuration for the PWA. Only automatically enabled when using chromium.";
};
};
};
config = lib.mkIf cfg.enable (lib.mkMerge [
{
warnings =
lib.optional (config.impermanence.enable && !isBrowserImpermanenceSupported)
"proton-calendar-pwa: Using unsupported package. You will need to manually configure pwa for ${cfg.package.pname}. Supported package(s) ${pkgs.chromium.pname}";
}
(
lib.mkIf isChromium {
xdg.desktopEntries.proton-calendar-pwa = {
name = "Proton Calendar";
type = "Application";
exec = "${cfg.package}/bin/${cfg.package.pname} --app=https://calendar.proton.me";
icon = "chrome-ojibjkjikcpjonjjngfkegflhmffeemk-Default";
terminal = false;
};
}
)
(
lib.mkIf (config.impermanence.enable && cfg.impermanence.enable && isChromium) {
home.persistence."/persist${config.home.homeDirectory}" = {
directories = [
"${config.xdg.configHome}/chromium"
];
allowOther = true;
};
}
)
]);
}

View file

@ -1,55 +0,0 @@
{
lib,
pkgs,
config,
...
}: let
cfg = config.programs.proton-mail-pwa;
isChromium = cfg.package == pkgs.chromium;
isBrowserImpermanenceSupported = cfg.package == pkgs.chromium;
in {
options.programs.proton-mail-pwa = {
enable = lib.mkEnableOption "enable Proton Mail PWA";
package = lib.mkOption {
type = lib.types.package;
default = pkgs.chromium;
description = "Browser package to use for the PWA";
};
impermanence = {
enable = lib.mkOption {
type = lib.types.bool;
default = isBrowserImpermanenceSupported;
description = "Enable impermanence configuration for the PWA. Only automatically enabled when using chromium.";
};
};
};
config = lib.mkIf cfg.enable (lib.mkMerge [
{
warnings =
lib.optional (config.impermanence.enable && !isBrowserImpermanenceSupported)
"proton-mail-pwa: Using unsupported package. You will need to manually configure pwa for ${cfg.package.pname}. Supported package(s) ${pkgs.chromium.pname}";
}
(
lib.mkIf isChromium {
xdg.desktopEntries.proton-mail-pwa = {
name = "Proton Mail";
type = "Application";
exec = "${cfg.package}/bin/${cfg.package.pname} --app=https://mail.proton.me";
icon = "chrome-jnpecgipniidlgicjocehkhajgdnjekh-Default";
terminal = false;
};
}
)
(
lib.mkIf (config.impermanence.enable && cfg.impermanence.enable && isChromium) {
home.persistence."/persist${config.home.homeDirectory}" = {
directories = [
"${config.xdg.configHome}/chromium"
];
allowOther = true;
};
}
)
]);
}

View file

@ -16,7 +16,7 @@
} }
( (
lib.mkIf config.impermanence.enable { lib.mkIf config.impermanence.enable {
home.persistence."${config.impermanence.persistencePath}" = { home.persistence."/persist${config.home.homeDirectory}" = {
directories = [ directories = [
"${config.xdg.configHome}/protonvpn" "${config.xdg.configHome}/protonvpn"
"${config.xdg.configHome}/Proton" "${config.xdg.configHome}/Proton"

View file

@ -16,7 +16,7 @@
} }
( (
lib.mkIf config.impermanence.enable { lib.mkIf config.impermanence.enable {
home.persistence."${config.impermanence.persistencePath}" = { home.persistence."/persist${config.home.homeDirectory}" = {
directories = [ directories = [
"${config.xdg.configHome}/qBittorrent" "${config.xdg.configHome}/qBittorrent"
]; ];

View file

@ -16,10 +16,11 @@
} }
( (
lib.mkIf config.impermanence.enable { lib.mkIf config.impermanence.enable {
home.persistence."${config.impermanence.persistencePath}" = { home.persistence."/persist${config.home.homeDirectory}" = {
directories = [ directories = [
"${config.xdg.configHome}/qFlipper" "${config.xdg.configHome}/qFlipper"
]; ];
allowOther = true;
}; };
} }
) )

View file

@ -4,19 +4,19 @@
config, config,
... ...
}: { }: {
options.programs.signal-desktop = { options.programs.signal-desktop-bin = {
enable = lib.mkEnableOption "enable signal"; enable = lib.mkEnableOption "enable signal";
}; };
config = lib.mkIf config.programs.signal-desktop.enable (lib.mkMerge [ config = lib.mkIf config.programs.signal-desktop-bin.enable (lib.mkMerge [
{ {
home.packages = with pkgs; [ home.packages = with pkgs; [
signal-desktop signal-desktop-bin
]; ];
} }
( (
lib.mkIf config.impermanence.enable { lib.mkIf config.impermanence.enable {
home.persistence."${config.impermanence.persistencePath}" = { home.persistence."/persist${config.home.homeDirectory}" = {
directories = [ directories = [
"${config.xdg.configHome}/Signal" "${config.xdg.configHome}/Signal"
]; ];

View file

@ -18,13 +18,14 @@
} }
( (
lib.mkIf config.impermanence.enable { lib.mkIf config.impermanence.enable {
home.persistence."${config.impermanence.persistencePath}" = { home.persistence."/persist${config.home.homeDirectory}" = {
directories = [ directories = [
{ {
directory = "${config.xdg.dataHome}/Steam"; directory = "${config.xdg.dataHome}/Steam";
method = "symlink"; method = "symlink";
} }
]; ];
allowOther = true;
}; };
} }
) )

View file

@ -16,10 +16,11 @@
} }
( (
lib.mkIf config.impermanence.enable { lib.mkIf config.impermanence.enable {
home.persistence."${config.impermanence.persistencePath}" = { home.persistence."/persist${config.home.homeDirectory}" = {
directories = [ directories = [
"${config.xdg.dataHome}/torbrowser" "${config.xdg.dataHome}/torbrowser"
]; ];
allowOther = true;
}; };
} }
) )

View file

@ -16,10 +16,11 @@
} }
( (
lib.mkIf config.impermanence.enable { lib.mkIf config.impermanence.enable {
home.persistence."${config.impermanence.persistencePath}" = { home.persistence."/persist${config.home.homeDirectory}" = {
directories = [ directories = [
"${config.xdg.configHome}/chromium" "${config.xdg.configHome}/chromium"
]; ];
allowOther = true;
}; };
} }
) )

View file

@ -16,11 +16,12 @@
} }
( (
lib.mkIf config.impermanence.enable { lib.mkIf config.impermanence.enable {
home.persistence."${config.impermanence.persistencePath}" = { home.persistence."/persist${config.home.homeDirectory}" = {
directories = [ directories = [
"${config.xdg.configHome}/via" "${config.xdg.configHome}/via"
"${config.xdg.dataHome}/via" "${config.xdg.dataHome}/via"
]; ];
allowOther = true;
}; };
} }
) )

View file

@ -17,7 +17,7 @@
} }
( (
lib.mkIf config.impermanence.enable { lib.mkIf config.impermanence.enable {
home.persistence."${config.impermanence.persistencePath}" = { home.persistence."/persist${config.home.homeDirectory}" = {
directories = [ directories = [
{ {
directory = ".vmware"; directory = ".vmware";
@ -28,6 +28,7 @@
method = "symlink"; method = "symlink";
} }
]; ];
allowOther = true;
}; };
} }
) )

View file

@ -10,19 +10,6 @@
mcp-nixos = inputs.mcp-nixos.packages.${pkgs.stdenv.hostPlatform.system}.default; mcp-nixos = inputs.mcp-nixos.packages.${pkgs.stdenv.hostPlatform.system}.default;
anyProfileHasInstallTool = lib.any (
profile:
profile.extraExtensions.claudeDev.enable
&& profile.extraExtensions.claudeDev.installTool
) (lib.attrValues config.programs.vscode.profiles);
getInstallToolPackage = lib.findFirst (package: package != null) pkgs.cline (map (
profile:
if profile.extraExtensions.claudeDev.enable && profile.extraExtensions.claudeDev.installTool
then profile.extraExtensions.claudeDev.package
else null
) (lib.attrValues config.programs.vscode.profiles));
anyProfileHasMcpNixos = lib.any ( anyProfileHasMcpNixos = lib.any (
profile: profile:
profile.extraExtensions.claudeDev.enable profile.extraExtensions.claudeDev.enable
@ -82,17 +69,6 @@ in {
default = ["saoudrizwan" "claude-dev"]; default = ["saoudrizwan" "claude-dev"];
}; };
installTool = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Whether to install the cline CLI tool for subagent support when the extension is enabled";
};
package = lib.mkOption {
type = lib.types.package;
default = pkgs.cline;
description = "The package to install for the cline CLI tool";
};
mcp = { mcp = {
nixos = { nixos = {
enable = lib.mkEnableOption "enable NixOS MCP server for Claude Dev"; enable = lib.mkEnableOption "enable NixOS MCP server for Claude Dev";
@ -169,12 +145,6 @@ in {
}; };
config = lib.mkMerge [ config = lib.mkMerge [
(lib.mkIf anyProfileHasInstallTool {
home.packages = [
getInstallToolPackage
];
})
(lib.mkIf anyProfileHasMcpNixos { (lib.mkIf anyProfileHasMcpNixos {
home.packages = [ home.packages = [
mcp-nixos mcp-nixos

View file

@ -16,7 +16,6 @@
./go.nix ./go.nix
./evenBetterToml.nix ./evenBetterToml.nix
./openRemoteSsh.nix ./openRemoteSsh.nix
./platformIO.nix
./rustAnalyzer.nix ./rustAnalyzer.nix
./astroVscode.nix ./astroVscode.nix
./vscodeMdx.nix ./vscodeMdx.nix
@ -26,6 +25,5 @@
./direnv.nix ./direnv.nix
./conventionalCommits.nix ./conventionalCommits.nix
./openDyslexicFont.nix ./openDyslexicFont.nix
./graphql.nix
]; ];
} }

View file

@ -21,13 +21,6 @@ in {
extensions = [ extensions = [
config.extraExtensions.go.extension config.extraExtensions.go.extension
]; ];
userSettings = {
"go.alternateTools" = {
"gopls" = "gopls";
};
"go.toolsManagement.autoUpdate" = false;
"go.useLanguageServer" = true;
};
}; };
})); }));
}; };

View file

@ -1,27 +0,0 @@
{
lib,
pkgs,
config,
...
}: let
pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version;
pkgsRepository = pkgsRepositories.open-vsx;
in {
options.programs.vscode.profiles = lib.mkOption {
type = lib.types.attrsOf (lib.types.submodule ({config, ...}: {
options = {
extraExtensions.graphql = {
enable = lib.mkEnableOption "should the graphql highlighting extension for vscode be enabled";
extension = lib.mkPackageOption pkgsRepository "vscode-graphql" {
default = ["graphql" "vscode-graphql-syntax"];
};
};
};
config = lib.mkIf config.extraExtensions.graphql.enable {
extensions = [
config.extraExtensions.graphql.extension
];
};
}));
};
}

View file

@ -1,30 +0,0 @@
{
lib,
pkgs,
config,
...
}: let
pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version;
pkgsRepository = pkgsRepositories.open-vsx;
in {
options.programs.vscode.profiles = lib.mkOption {
type = lib.types.attrsOf (lib.types.submodule ({config, ...}: {
options = {
extraExtensions.platformIO = {
enable = lib.mkEnableOption "should the platformIO extension for vscode be enabled";
extension = lib.mkPackageOption pkgsRepository "platformIO" {
default = ["pioarduino" "pioarduino-ide"];
};
};
};
config = lib.mkIf config.extraExtensions.platformIO.enable {
extensions = [
config.extraExtensions.platformIO.extension
];
userSettings = {
"platformio-ide.useBuiltinPIOCore" = false;
};
};
}));
};
}

View file

@ -8,13 +8,14 @@
./desktop.nix ./desktop.nix
./ssh.nix ./ssh.nix
./i18n.nix ./i18n.nix
./sync ./sync.nix
./ollama ./impermanence.nix
./disko.nix
./ollama.nix
./ai.nix ./ai.nix
./tailscale ./tailscale.nix
./steam.nix ./steam.nix
./server ./server
./storage
]; ];
nixpkgs.config.permittedInsecurePackages = [ nixpkgs.config.permittedInsecurePackages = [

View file

@ -47,9 +47,6 @@
# Get rid of xTerm # Get rid of xTerm
desktopManager.xterm.enable = false; desktopManager.xterm.enable = false;
excludePackages = with pkgs; [
xterm
];
}; };
# Enable the GNOME Desktop Environment. # Enable the GNOME Desktop Environment.

View file

@ -0,0 +1,267 @@
{
lib,
pkgs,
config,
inputs,
...
}: let
# there currently is a bug with disko that causes long disk names to be generated improperly this hash function should alleviate it when used for disk names instead of what we are defaulting to
# max gpt length is 36 and disk adds formats it like disk-xxxx-zfs which means we need to be 9 characters under that
hashDisk = drive: (builtins.substring 0 27 (builtins.hashString "sha256" drive));
vdevs =
builtins.map (
disks:
builtins.map (disk: lib.attrsets.nameValuePair (hashDisk disk) disk) disks
)
config.host.storage.pool.vdevs;
cache =
builtins.map (
disk: lib.attrsets.nameValuePair (hashDisk disk) disk
)
config.host.storage.pool.cache;
datasets = config.host.storage.pool.datasets // config.host.storage.pool.extraDatasets;
in {
options.host.storage = {
enable = lib.mkEnableOption "are we going create zfs disks with disko on this device";
encryption = lib.mkEnableOption "is the vdev going to be encrypted";
notifications = {
enable = lib.mkEnableOption "are notifications enabled";
host = lib.mkOption {
type = lib.types.str;
description = "what is the host that we are going to send the email to";
};
port = lib.mkOption {
type = lib.types.port;
description = "what port is the host using to receive mail on";
};
to = lib.mkOption {
type = lib.types.str;
description = "what account is the email going to be sent to";
};
user = lib.mkOption {
type = lib.types.str;
description = "what user is the email going to be set from";
};
tokenFile = lib.mkOption {
type = lib.types.str;
description = "file containing the password to be used by msmtp for notifications";
};
};
pool = {
mode = lib.mkOption {
type = lib.types.str;
default = "raidz2";
description = "what level of redundancy should this pool have";
};
# list of drives in pool that will have a boot partition put onto them
bootDrives = lib.mkOption {
type = lib.types.listOf lib.types.str;
description = "list of disks that are going to have a boot partition installed on them";
default = lib.lists.flatten config.host.storage.pool.vdevs;
};
# shorthand for vdevs if you only have 1 vdev
drives = lib.mkOption {
type = lib.types.listOf lib.types.str;
description = "list of drives that are going to be in the vdev";
default = [];
};
# list of all drives in each vdev
vdevs = lib.mkOption {
type = lib.types.listOf (lib.types.listOf lib.types.str);
description = "list of disks that are going to be in";
default = [config.host.storage.pool.drives];
};
# list of cache drives for pool
cache = lib.mkOption {
type = lib.types.listOf lib.types.str;
description = "list of drives that are going to be used as cache";
default = [];
};
# Default datasets that are needed to make a functioning system
datasets = lib.mkOption {
type = lib.types.attrsOf (inputs.disko.lib.subType {
types = {inherit (inputs.disko.lib.types) zfs_fs zfs_volume;};
});
default = {
"local" = {
type = "zfs_fs";
options.canmount = "off";
};
# nix directory needs to be available pre persist and doesn't need to be snapshotted or backed up
"local/system/nix" = {
type = "zfs_fs";
mountpoint = "/nix";
options = {
atime = "off";
relatime = "off";
canmount = "on";
};
};
# dataset for root that gets rolled back on every boot
"local/system/root" = {
type = "zfs_fs";
mountpoint = "/";
options = {
canmount = "on";
};
postCreateHook = ''
zfs snapshot rpool/local/system/root@blank
'';
};
};
};
extraDatasets = lib.mkOption {
type = lib.types.attrsOf (inputs.disko.lib.subType {
types = {inherit (inputs.disko.lib.types) zfs_fs zfs_volume;};
});
description = "List of datasets to define";
default = {};
};
};
};
config = lib.mkIf config.host.storage.enable {
programs.msmtp = lib.mkIf config.host.storage.notifications.enable {
enable = true;
setSendmail = true;
defaults = {
aliases = "/etc/aliases";
port = config.host.storage.notifications.port;
tls_trust_file = "/etc/ssl/certs/ca-certificates.crt";
tls = "on";
auth = "login";
tls_starttls = "off";
};
accounts = {
zfs_notifications = {
auth = true;
tls = true;
host = config.host.storage.notifications.host;
passwordeval = "cat ${config.host.storage.notifications.tokenFile}";
user = config.host.storage.notifications.user;
from = config.host.storage.notifications.user;
};
};
};
services.zfs = {
autoScrub.enable = true;
autoSnapshot.enable = true;
zed = lib.mkIf config.host.storage.notifications.enable {
enableMail = true;
settings = {
ZED_DEBUG_LOG = "/tmp/zed.debug.log";
ZED_EMAIL_ADDR = [config.host.storage.notifications.to];
ZED_EMAIL_PROG = "${pkgs.msmtp}/bin/msmtp";
ZED_EMAIL_OPTS = "-a zfs_notifications @ADDRESS@";
ZED_NOTIFY_INTERVAL_SECS = 3600;
ZED_NOTIFY_VERBOSE = true;
ZED_USE_ENCLOSURE_LEDS = true;
ZED_SCRUB_AFTER_RESILVER = true;
};
};
};
disko.devices = {
disk = (
builtins.listToAttrs (
builtins.map
(drive:
lib.attrsets.nameValuePair (drive.name) {
type = "disk";
device = "/dev/disk/by-id/${drive.value}";
content = {
type = "gpt";
partitions = {
ESP = lib.mkIf (builtins.elem drive.value config.host.storage.pool.bootDrives) {
# The 2GB here for the boot partition might be a bit overkill we probably only need like 1/4th of that but storage is cheap
size = "2G";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = ["umask=0077"];
};
};
zfs = {
size = "100%";
content = {
type = "zfs";
pool = "rpool";
};
};
};
};
})
(
(lib.lists.flatten vdevs) ++ cache
)
)
);
zpool = {
rpool = {
type = "zpool";
mode = {
topology = {
type = "topology";
vdev = (
builtins.map (disks: {
mode = config.host.storage.pool.mode;
members =
builtins.map (disk: disk.name) disks;
})
vdevs
);
cache = builtins.map (disk: disk.name) cache;
};
};
options = {
ashift = "12";
autotrim = "on";
};
rootFsOptions =
{
canmount = "off";
mountpoint = "none";
xattr = "sa";
acltype = "posixacl";
relatime = "on";
compression = "lz4";
"com.sun:auto-snapshot" = "false";
}
// (
lib.attrsets.optionalAttrs config.host.storage.encryption {
encryption = "on";
keyformat = "hex";
keylocation = "prompt";
}
);
datasets = lib.mkMerge [
(
lib.attrsets.mapAttrs (name: value: {
type = value.type;
options = value.options;
mountpoint = value.mountpoint;
postCreateHook = value.postCreateHook;
})
datasets
)
];
};
};
};
};
}

View file

@ -0,0 +1,134 @@
{
config,
lib,
...
}: {
# options.storage = {
# zfs = {
# # TODO: enable option
# # when this option is enabled we need to configure and enable disko things
# # TODO: we need some way of managing notifications
# # TODO: we need options to configure zfs pools
# # we should have warnings when the configured pool is missing drives
# # TODO: dataset option that is a submodule that adds datasets to the system
# # warnings for when a dataset was created in the past on a system but it is now missing some of the options defined for it
# # TODO: pools and datasets need to be passed to disko
# };
# impermanence = {
# # TODO: enable option
# # TODO: datasets option that is a submodule that will be used to define what datasets to add to the storage system
# # We should by default create the `local`, `local/system/nix`, `local/system/root`, `persist` `persist/system/root`, and `persist/system/var/log` datasets
# # Then we should make a dataset for user folders local and persist
# # We should also create datasets for systemd modules that have have impermanence enabled for them
# # we need to figure out what options a dataset can have in zfs
# };
# # TODO: we should have an impermanence module for home manager that proxies its values namespaced to the user down here that matches the same interface
# # TODO: we should have a way of enabling impermanence for a systemd config
# # these should have an option to put their folder into their own dataset (this needs to support private vs non private)
# # options for features that can be added to the dataset
# };
options.host.impermanence.enable = lib.mkEnableOption "are we going to use impermanence on this device";
config = lib.mkMerge [
{
assertions = [
{
assertion = !(config.host.impermanence.enable && !config.host.storage.enable);
message = ''
Disko storage must be enabled to use impermanence.
'';
}
];
}
(
lib.mkIf config.host.impermanence.enable {
assertions = [
{
assertion = config.host.impermanence.enable && config.host.storage.enable;
message = "Impermanence can not be used without managed host storage.";
}
];
# fixes issues with /var/lib/private not having the correct permissions https://github.com/nix-community/impermanence/issues/254
system.activationScripts."createPersistentStorageDirs".deps = ["var-lib-private-permissions" "users" "groups"];
system.activationScripts = {
"var-lib-private-permissions" = {
deps = ["specialfs"];
text = ''
mkdir -p /persist/system/root/var/lib/private
chmod 0700 /persist/system/root/var/lib/private
'';
};
};
programs.fuse.userAllowOther = true;
boot.initrd.postResumeCommands = lib.mkAfter ''
zfs rollback -r rpool/local/system/root@blank
'';
fileSystems = {
"/".neededForBoot = true;
"/persist/system/root".neededForBoot = true;
"/persist/system/var/log".neededForBoot = true;
};
host.storage.pool.extraDatasets = {
# persist datasets are datasets that contain information that we would like to keep around
"persist" = {
type = "zfs_fs";
options.canmount = "off";
options = {
"com.sun:auto-snapshot" = "true";
};
};
# this is where root data actually lives
"persist/system/root" = {
type = "zfs_fs";
mountpoint = "/persist/system/root";
};
"persist/system/var/log" = {
type = "zfs_fs";
mountpoint = "/persist/system/var/log";
# logs should be append only so we shouldn't need to snapshot them
options = {
"com.sun:auto-snapshot" = "false";
};
};
};
environment.persistence."/persist/system/var/log" = {
enable = true;
hideMounts = true;
directories = [
"/var/log"
];
};
environment.persistence."/persist/system/root" = {
enable = true;
hideMounts = true;
directories = [
"/var/lib/nixos"
"/var/lib/systemd/coredump"
];
files = [
"/etc/machine-id"
];
};
# TODO: this should live in leylas home manager configuration
security.sudo.extraConfig = "Defaults lecture=never";
}
)
];
}

View file

@ -27,6 +27,20 @@
allowedUDPPorts = ports; allowedUDPPorts = ports;
}; };
})) }))
(lib.mkIf config.host.impermanence.enable {
environment.persistence."/persist/system/root" = {
enable = true;
hideMounts = true;
directories = [
{
directory = "/var/lib/private/ollama";
user = config.services.ollama.user;
group = config.services.ollama.group;
mode = "0700";
}
];
};
})
] ]
); );
} }

View file

@ -1,6 +0,0 @@
{...}: {
imports = [
./ollama.nix
./storage.nix
];
}

View file

@ -1,37 +0,0 @@
{
config,
lib,
...
}: {
options = {
services.ollama.impermanence.enable = lib.mkOption {
type = lib.types.bool;
default = config.services.ollama.enable && config.storage.impermanence.enable;
};
};
config = lib.mkIf (config.services.ollama.enable) {
storage.datasets.replicate."system/root" = {
directories."/var/lib/private/ollama" = lib.mkIf config.services.ollama.impermanence.enable {
enable = true;
owner.name = config.services.ollama.user;
group.name = config.services.ollama.group;
owner.permissions = {
read = true;
write = true;
execute = false;
};
group.permissions = {
read = false;
write = false;
execute = false;
};
other.permissions = {
read = false;
write = false;
execute = false;
};
};
};
};
}

View file

@ -3,6 +3,6 @@
./actual.nix ./actual.nix
./proxy.nix ./proxy.nix
./fail2ban.nix ./fail2ban.nix
./storage.nix ./impermanence.nix
]; ];
} }

View file

@ -0,0 +1,37 @@
{
lib,
config,
...
}: let
const = import ./const.nix;
dataDirectory = const.dataDirectory;
in {
options.services.actual = {
impermanence.enable = lib.mkOption {
type = lib.types.bool;
default = config.services.actual.enable && config.host.impermanence.enable;
};
};
config = lib.mkIf config.services.actual.impermanence.enable {
assertions = [
{
assertion = config.services.actual.settings.dataDir == dataDirectory;
message = "actual data location does not match persistence\nconfig directory: ${config.services.actual.settings.dataDir}\npersistence directory: ${dataDirectory}";
}
{
assertion = config.systemd.services.actual.serviceConfig.DynamicUser or false;
message = "actual systemd service must have DynamicUser enabled to use private directory";
}
];
environment.persistence."/persist/system/root" = {
directories = [
{
directory = dataDirectory;
user = "actual";
group = "actual";
}
];
};
};
}

View file

@ -1,22 +0,0 @@
{
lib,
config,
...
}: let
const = import ./const.nix;
dataDirectory = const.dataDirectory;
in {
options.services.actual.impermanence.enable = lib.mkOption {
type = lib.types.bool;
default = config.services.actual.enable && config.storage.impermanence.enable;
};
config = lib.mkIf config.services.actual.enable {
storage.datasets.replicate."system/root" = {
directories."${dataDirectory}" = lib.mkIf config.services.actual.impermanence.enable {
owner.name = "actual";
group.name = "actual";
};
};
};
}

View file

@ -1,5 +1,5 @@
{...}: { {...}: {
imports = [ imports = [
./storage.nix ./impermanence.nix
]; ];
} }

View file

@ -0,0 +1,33 @@
{
lib,
config,
...
}: let
bazarr_data_directory = "/var/lib/bazarr";
in {
options.services.bazarr = {
impermanence.enable = lib.mkOption {
type = lib.types.bool;
default = config.services.bazarr.enable && config.host.impermanence.enable;
};
};
config = lib.mkIf config.services.bazarr.impermanence.enable {
assertions = [
{
assertion = config.services.bazarr.dataDir == bazarr_data_directory;
message = "bazarr data directory does not match persistence";
}
];
environment.persistence."/persist/system/root" = {
directories = [
{
directory = bazarr_data_directory;
user = "bazarr";
group = "bazarr";
}
];
};
};
}

View file

@ -1,21 +0,0 @@
{
lib,
config,
...
}: let
bazarr_data_directory = "/var/lib/bazarr";
in {
options.services.bazarr.impermanence.enable = lib.mkOption {
type = lib.types.bool;
default = config.services.bazarr.enable && config.storage.impermanence.enable;
};
config = lib.mkIf config.services.bazarr.enable {
storage.datasets.replicate."system/root" = {
directories."${bazarr_data_directory}" = lib.mkIf config.services.bazarr.impermanence.enable {
owner.name = "bazarr";
group.name = "bazarr";
};
};
};
}

View file

@ -1,6 +1,6 @@
{...}: { {...}: {
imports = [ imports = [
./crab-hole.nix ./crab-hole.nix
./storage.nix ./impermanence.nix
]; ];
} }

View file

@ -0,0 +1,33 @@
{
lib,
config,
...
}: let
workingDirectory = "/var/lib/private/crab-hole";
in {
options.services.crab-hole = {
impermanence.enable = lib.mkOption {
type = lib.types.bool;
default = config.services.crab-hole.enable && config.host.impermanence.enable;
};
};
config = lib.mkIf config.services.crab-hole.impermanence.enable {
assertions = [
{
assertion =
config.systemd.services.crab-hole.serviceConfig.WorkingDirectory == (builtins.replaceStrings ["/private"] [""] workingDirectory);
message = "crab-hole working directory does not match persistence";
}
];
environment.persistence."/persist/system/root" = {
directories = [
{
directory = workingDirectory;
user = "crab-hole";
group = "crab-hole";
}
];
};
};
}

View file

@ -1,21 +0,0 @@
{
lib,
config,
...
}: let
workingDirectory = "/var/lib/private/crab-hole";
in {
options.services.crab-hole.impermanence.enable = lib.mkOption {
type = lib.types.bool;
default = config.services.crab-hole.enable && config.storage.impermanence.enable;
};
config = lib.mkIf config.services.crab-hole.enable {
storage.datasets.replicate."system/root" = {
directories."${workingDirectory}" = lib.mkIf config.services.crab-hole.impermanence.enable {
owner.name = "crab-hole";
group.name = "crab-hole";
};
};
};
}

View file

@ -1,6 +1,6 @@
{...}: { {...}: {
imports = [ imports = [
./fail2ban.nix ./fail2ban.nix
./storage.nix ./impermanence.nix
]; ];
} }

View file

@ -0,0 +1,34 @@
{
lib,
config,
...
}: let
dataFolder = "/var/lib/fail2ban";
dataFile = "fail2ban.sqlite3";
in {
options.services.fail2ban = {
impermanence.enable = lib.mkOption {
type = lib.types.bool;
default = config.services.fail2ban.enable && config.host.impermanence.enable;
};
};
config = lib.mkIf config.services.fail2ban.impermanence.enable {
assertions = [
{
assertion = config.services.fail2ban.daemonSettings.Definition.dbfile == "${dataFolder}/${dataFile}";
message = "fail2ban data file does not match persistence";
}
];
environment.persistence."/persist/system/root" = {
directories = [
{
directory = dataFolder;
user = "fail2ban";
group = "fail2ban";
}
];
};
};
}

View file

@ -1,22 +0,0 @@
{
lib,
config,
...
}: let
dataFolder = "/var/lib/fail2ban";
dataFile = "fail2ban.sqlite3";
in {
options.services.fail2ban.impermanence.enable = lib.mkOption {
type = lib.types.bool;
default = config.services.fail2ban.enable && config.storage.impermanence.enable;
};
config = lib.mkIf config.services.fail2ban.enable {
storage.datasets.replicate."system/root" = {
directories."${dataFolder}" = lib.mkIf config.services.fail2ban.impermanence.enable {
owner.name = "fail2ban";
group.name = "fail2ban";
};
};
};
}

View file

@ -1,5 +1,5 @@
{...}: { {...}: {
imports = [ imports = [
./storage.nix ./impermanence.nix
]; ];
} }

View file

@ -0,0 +1,26 @@
{
lib,
config,
...
}: {
options.services.flaresolverr = {
impermanence.enable = lib.mkOption {
type = lib.types.bool;
default = config.services.flaresolverr.enable && config.host.impermanence.enable;
};
};
config = lib.mkIf config.services.flaresolverr.impermanence.enable {
# FlareSolverr typically doesn't need persistent storage as it's a proxy service
# but we'll add basic structure in case it's needed for logs or configuration
environment.persistence."/persist/system/root" = {
directories = [
{
directory = "/var/lib/flaresolverr";
user = "flaresolverr";
group = "flaresolverr";
}
];
};
};
}

View file

@ -1,19 +0,0 @@
{
lib,
config,
...
}: {
options.services.flaresolverr.impermanence.enable = lib.mkOption {
type = lib.types.bool;
default = config.services.flaresolverr.enable && config.storage.impermanence.enable;
};
config = lib.mkIf config.services.flaresolverr.enable {
storage.datasets.replicate."system/root" = {
directories."/var/lib/flaresolverr" = lib.mkIf config.services.flaresolverr.impermanence.enable {
owner.name = "flaresolverr";
group.name = "flaresolverr";
};
};
};
}

View file

@ -4,6 +4,6 @@
./proxy.nix ./proxy.nix
./database.nix ./database.nix
./fail2ban.nix ./fail2ban.nix
./storage.nix ./impermanence.nix
]; ];
} }

Some files were not shown because too many files have changed in this diff Show more