Merge pull request 'main' (#5) from jan-leila/nix-config:main into main

Reviewed-on: #5
This commit is contained in:
Lithospherical 2025-08-27 15:36:38 +00:00
commit 348fa835b8
62 changed files with 1582 additions and 894 deletions

View file

@ -41,30 +41,32 @@ nix multi user, multi system, configuration with `sops` secret management, `home
## Research topics ## Research topics
- Look into this for auto rotating sops keys `https://technotim.live/posts/rotate-sops-encryption-keys/` - Look into this for auto rotating sops keys `https://technotim.live/posts/rotate-sops-encryption-keys/`
- Look into this for flake templates https://nix.dev/manual/nix/2.22/command-ref/new-cli/nix3-flake-init - Look into this for npins https://jade.fyi/blog/pinning-nixos-with-npins/
- https://nixos-and-flakes.thiscute.world/ - https://nixos-and-flakes.thiscute.world/
- nix config mcp https://github.com/utensils/mcp-nixos
# Tasks: # Tasks:
## Tech Debt ## Tech Debt
- 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/)
- syncthing folder passwords
- nfs export should be backed by the same values for server and client - nfs export should be backed by the same values for server and client
## New Features ## New Features
- offline access for nfs mounts (overlay with rsync might be a good option here? https://www.spinics.net/lists/linux-unionfs/msg07105.html note about nfs4 and overlay fs) - crab-hole
- samba mounts - figure out why syncthing and jellyfins permissions don't propagate downwards
- figure out steam vr things? - figure out steam vr things?
- Open GL? - auto turn off on power loss - nut
- rotate sops encryption keys periodically (and somehow sync between devices?)
- zfs email after scrubbing # TODO: test this - zfs email after scrubbing # TODO: test this
- wake on LAN for updates - SMART test with email results
- ISO target that contains authorized keys for nixos-anywhere https://github.com/diegofariasm/yggdrasil/blob/4acc43ebc7bcbf2e41376d14268e382007e94d78/hosts/bootstrap/default.nix - fix nfs
- samba mounts
- offline access for nfs mounts (overlay with rsync might be a good option here? https://www.spinics.net/lists/linux-unionfs/msg07105.html note about nfs4 and overlay fs)
- Create Tor guard/relay server
- migrate away from flakes and move to npins
- whisper
- nix mcp
- zfs encryption FIDO2 2fa (look into shavee) - zfs encryption FIDO2 2fa (look into shavee)
- Secure Boot - https://github.com/nix-community/lanzaboote - Secure Boot - https://github.com/nix-community/lanzaboote
- SMART test with email results - rotate sops encryption keys periodically (and somehow sync between devices?)
- Create Tor guard/relay server - wake on LAN for updates
- remote distributed builds - https://nix.dev/tutorials/nixos/distributed-builds-setup.html - remote distributed builds - https://nix.dev/tutorials/nixos/distributed-builds-setup.html
- migrate away from flakes and move to npins - ISO target that contains authorized keys for nixos-anywhere https://github.com/diegofariasm/yggdrasil/blob/4acc43ebc7bcbf2e41376d14268e382007e94d78/hosts/bootstrap/default.nix
- fix nfs
- fix home assistant
- create adguard server

View file

@ -1,26 +1,23 @@
{pkgs, ...}: { {pkgs, ...}: {
config = { config = {
gnome = {
extraWindowControls = true;
colorScheme = "prefer-dark";
clockFormat = "24h";
extensions = [
pkgs.gnomeExtensions.dash-to-dock
];
hotkeys = {
"Open Terminal" = {
binding = "<Super>t";
command = "kgx";
};
};
};
dconf = { dconf = {
enable = true; enable = true;
settings = { settings = {
"org/gnome/desktop/interface".color-scheme = "prefer-dark";
"org/gnome/desktop/wm/preferences".button-layout = ":minimize,maximize,close";
"org/gnome/shell" = {
disable-user-extensions = false; # enables user extensions
enabled-extensions = [
# Put UUIDs of extensions that you want to enable here.
# If the extension you want to enable is packaged in nixpkgs,
# you can easily get its UUID by accessing its extensionUuid
# field (look at the following example).
pkgs.gnomeExtensions.dash-to-dock.extensionUuid
# Alternatively, you can manually pass UUID as a string.
# "dash-to-dock@micxgx.gmail.com"
];
};
"org/gnome/shell/extensions/dash-to-dock" = { "org/gnome/shell/extensions/dash-to-dock" = {
"dock-position" = "LEFT"; "dock-position" = "LEFT";
"intellihide-mode" = "ALL_WINDOWS"; "intellihide-mode" = "ALL_WINDOWS";
@ -29,18 +26,6 @@
"show-mounts" = false; "show-mounts" = false;
}; };
"org/gnome/settings-daemon/plugins/media-keys" = {
custom-keybindings = [
"/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom0/"
];
};
"org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom0" = {
binding = "<Super>t";
command = "kgx";
name = "Open Terminal";
};
"org/gnome/shell" = { "org/gnome/shell" = {
favorite-apps = ["org.gnome.Nautilus.desktop" "firefox.desktop" "codium.desktop" "steam.desktop" "org.gnome.Console.desktop"]; favorite-apps = ["org.gnome.Nautilus.desktop" "firefox.desktop" "codium.desktop" "steam.desktop" "org.gnome.Console.desktop"];
# app-picker-layout = # app-picker-layout =

View file

@ -1,4 +1,5 @@
{ {
pkgs,
config, config,
osConfig, osConfig,
... ...
@ -83,30 +84,11 @@
}; };
}; };
user = { # TODO: move this into a fonts module
continue = { home.packages = with pkgs; [
enable = true; aileron
docs = { nerd-fonts.open-dyslexic
"Continue Docs" = { ];
startUrl = "https://docs.continue.dev"; fonts.fontconfig.enable = true;
};
"Nixpkgs" = {
startUrl = "https://ryantm.github.io/nixpkgs/#preface";
};
"Nix Manual" = {
startUrl = "https://nixos.org/manual/nixos/stable/";
};
"Home manager Manual" = {
startUrl = "https://nix-community.github.io/home-manager/";
};
"Nix Docs" = {
startUrl = "https://nix.dev/index.html";
};
"Linux Man Page" = {
startUrl = "https://linux.die.net/man/";
};
};
};
};
}; };
} }

View file

@ -67,10 +67,6 @@ in {
home.packages = ( home.packages = (
(with pkgs; [ (with pkgs; [
aileron
gnomeExtensions.dash-to-dock
proxmark3 proxmark3
]) ])
++ ( ++ (
@ -112,6 +108,8 @@ in {
(lib.mkIf hardware.piperMouse.enable piper) (lib.mkIf hardware.piperMouse.enable piper)
(lib.mkIf hardware.openRGB.enable openrgb) (lib.mkIf hardware.openRGB.enable openrgb)
(lib.mkIf hardware.viaKeyboard.enable via) (lib.mkIf hardware.viaKeyboard.enable via)
(lib.mkIf osConfig.host.ai.enable claude-code)
]) ])
) )
); );

View file

@ -72,6 +72,8 @@
deutsch-de-language-pack deutsch-de-language-pack
dictionary-german dictionary-german
tab-session-manager
# ( # (
# buildFirefoxXpiAddon rec { # buildFirefoxXpiAddon rec {
# pname = "italiano-it-language-pack"; # pname = "italiano-it-language-pack";
@ -140,7 +142,6 @@
"placements" = { "placements" = {
"widget-overflow-fixed-list" = []; "widget-overflow-fixed-list" = [];
"unified-extensions-area" = [ "unified-extensions-area" = [
"privacy_privacy_com-browser-action"
# bitwarden # bitwarden
"_446900e4-71c2-419f-a6a7-df9c091e268b_-browser-action" "_446900e4-71c2-419f-a6a7-df9c091e268b_-browser-action"
"ublock0_raymondhill_net-browser-action" "ublock0_raymondhill_net-browser-action"

View file

@ -1,31 +1,24 @@
{ {
lib, lib,
pkgs, pkgs,
inputs,
config, config,
osConfig, osConfig,
... ...
}: let }: let
nix-development-enabled = osConfig.host.nix-development.enable; nix-development-enabled = osConfig.host.nix-development.enable;
ai-tooling-enabled = config.user.continue.enable && osConfig.host.ai.enable; ai-tooling-enabled = osConfig.host.ai.enable;
in { in {
config = lib.mkIf config.user.isDesktopUser { imports = [
nixpkgs = { ./user-words.nix
overlays = [ ];
inputs.nix-vscode-extensions.overlays.default
];
};
config = lib.mkIf config.user.isDesktopUser {
programs = { programs = {
bash.shellAliases = { bash.shellAliases = {
code = "codium"; code = "codium";
}; };
vscode = let vscode = {
extensions = inputs.nix-vscode-extensions.extensions.${pkgs.system};
open-vsx = extensions.open-vsx;
vscode-marketplace = extensions.vscode-marketplace;
in {
package = pkgs.vscodium; package = pkgs.vscodium;
mutableExtensionsDir = false; mutableExtensionsDir = false;
@ -36,80 +29,59 @@ in {
userSettings = lib.mkMerge [ userSettings = lib.mkMerge [
{ {
"workbench.colorTheme" = "Atom One Dark";
"cSpell.userWords" = import ./user-words.nix;
"javascript.updateImportsOnFileMove.enabled" = "always"; "javascript.updateImportsOnFileMove.enabled" = "always";
"editor.tabSize" = 2; "editor.tabSize" = 2;
"editor.insertSpaces" = false; "editor.insertSpaces" = false;
} }
(lib.mkIf nix-development-enabled {
"nix.enableLanguageServer" = true;
"nix.serverPath" = "nil";
"[nix]" = {
"editor.defaultFormatter" = "kamadorueda.alejandra";
"editor.formatOnPaste" = true;
"editor.formatOnSave" = true;
"editor.formatOnType" = true;
};
"alejandra.program" = "alejandra";
"nixpkgs" = {
"expr" = "import <nixpkgs> {}";
};
})
(lib.mkIf ai-tooling-enabled {
"continue.telemetryEnabled" = false;
})
]; ];
extensions = ( extraExtensions = {
with open-vsx; # vs code feel
[ oneDark.enable = true;
# vs code feel extensions atomKeybindings.enable = true;
ms-vscode.atom-keybindings openRemoteSsh.enable = true;
akamud.vscode-theme-onedark
streetsidesoftware.code-spell-checker
streetsidesoftware.code-spell-checker-german
streetsidesoftware.code-spell-checker-italian
jeanp413.open-remote-ssh
# html extensions # html development
formulahendry.auto-rename-tag autoRenameTag.enable = true;
ms-vscode.live-server liveServer.enable = true;
# js extensions # js development
dsznajder.es7-react-js-snippets es7ReactJsSnippets.enable = true;
dbaeumer.vscode-eslint tauriVscode.enable = true;
standard.vscode-standard vscodeEslint.enable = true;
firsttris.vscode-jest-runner vscodeJest.enable = true;
stylelint.vscode-stylelint vscodeStandard.enable = true;
tauri-apps.tauri-vscode vscodeStylelint.enable = true;
# go extensions nearley.enable = true;
golang.go
# astro blog extensions # astro development
astro-build.astro-vscode vscodeMdx.enable = true;
unifiedjs.vscode-mdx astroVscode.enable = true;
# misc extensions # nix development
tamasfe.even-better-toml alejandra.enable = nix-development-enabled;
] nixIde.enable = nix-development-enabled;
++ (lib.lists.optionals nix-development-enabled [
# nix extensions # go development
pinage404.nix-extension-pack go.enable = true;
jnoortheen.nix-ide
kamadorueda.alejandra # claude development
]) claudeDev.enable = ai-tooling-enabled;
++ (
with vscode-marketplace; # misc extensions
[ evenBetterToml.enable = true;
# js extensions };
karyfoundation.nearley
] extensions = let
++ (lib.lists.optionals ai-tooling-enabled [ extension-pkgs = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version;
continue.continue in (
]) with extension-pkgs.open-vsx; [
) # vs code feel extensions
streetsidesoftware.code-spell-checker
streetsidesoftware.code-spell-checker-german
streetsidesoftware.code-spell-checker-italian
]
); );
}; };
}; };

View file

@ -1,6 +1,126 @@
[ {
"leyla" pkgs,
"webdav" lib,
"ollama" ...
"optimise" }: {
] config.programs.vscode.profiles.default.userSettings = {
"cSpell.userWords" = [
"leyla"
];
"cSpell.languageSettings" = [
{
"languageId" = "nix";
"locale" = "*";
"dictionaries" = [
"applications"
"ai-words"
"nix-words"
# We need to include all other dictionaries in the nix language settings because they exist in this file
# TODO: see if there is a way to make this only apply for this file
"js-words"
];
}
{
"languageId" = "javascript,typescript,js,ts";
"locale" = "*";
"dictionaries" = [
"js-words"
];
}
];
"cSpell.customDictionaries" = {
applications = {
name = "applications";
description = "application names";
path = pkgs.writeText "applications.txt" (lib.strings.concatLines [
"ollama"
"syncthing"
"immich"
"sonos"
"makemkv"
"hass"
"qbittorent"
"prostudiomasters"
"protonmail"
"pulseaudio"
]);
};
ai-words = {
name = "ai-words";
description = "common words used for ai development";
path = pkgs.writeText "ai-words.txt" (lib.strings.concatLines [
"ollama"
"deepseek"
"qwen"
]);
};
nix-words = {
name = "nix-words";
description = "words used in nix configurations";
path = pkgs.writeText "nix-words.txt" (lib.strings.concatLines [
"pname"
"direnv"
"tmpfiles"
"Networkd"
"networkmanager"
"dialout"
"adbusers"
"authkey"
"netdevs"
"atomix"
"geary"
"gedit"
"hitori"
"iagno"
"alsa"
"timezoned"
"pipewire"
"rtkit"
"disko"
"ashift"
"autotrim"
"canmount"
"mountpoint"
"xattr"
"acltype"
"relatime"
"keyformat"
"keylocation"
"vdevs"
# codium extensions
"akamud"
"onedark"
"jeanp"
"dsznajder"
"dbaeumer"
"orta"
"tauri"
"unifiedjs"
"tamasfe"
"pinage"
"jnoortheen"
"kamadorueda"
"karyfoundation"
"nearley"
# nix.optimise is spelled wrong
"optimise"
]);
};
js-words = {
name = "js-words";
description = "words used in js development";
path = pkgs.writeText "js-words.txt" (lib.strings.concatLines [
"webdav"
]);
};
};
};
}

View file

@ -17,6 +17,12 @@
"services/zfs_smtp_token" = { "services/zfs_smtp_token" = {
sopsFile = "${inputs.secrets}/defiant-services.yaml"; sopsFile = "${inputs.secrets}/defiant-services.yaml";
}; };
"services/paperless_password" = {
sopsFile = "${inputs.secrets}/defiant-services.yaml";
mode = "0700";
owner = "paperless";
group = "paperless";
};
}; };
host = { host = {
@ -36,7 +42,7 @@
host = "smtp.protonmail.ch"; host = "smtp.protonmail.ch";
port = 587; port = 587;
to = "leyla@jan-leila.com"; to = "leyla@jan-leila.com";
user = "leyla@jan-leila.com"; user = "noreply@jan-leila.com";
tokenFile = config.sops.secrets."services/zfs_smtp_token".path; tokenFile = config.sops.secrets."services/zfs_smtp_token".path;
}; };
pool = { pool = {
@ -109,24 +115,11 @@
}; };
}; };
}; };
# home-assistant = {
# enable = false;
# subdomain = "home";
# };
adguardhome = {
enable = false;
};
}; };
systemd.network = { systemd.network = {
enable = true; enable = true;
# config = {
# routeTables = {
# p2p = 1;
# };
# };
netdevs = { netdevs = {
"10-bond0" = { "10-bond0" = {
netdevConfig = { netdevConfig = {
@ -139,23 +132,20 @@
}; };
}; };
# "15-p2p0" = { # "20-wg0" = {
# netdevConfig = { # netdevConfig = {
# Kind = "wireguard"; # Kind = "wireguard";
# Name = "p2p0"; # Name = "wg0";
# MTUBytes = "1280";
# }; # };
# wireguardConfig = { # wireguardConfig = {
# PrivateKeyFile = config.sops.secrets."vpn-keys/proton-wireguard/defiant-p2p".path; # PrivateKeyFile = config.sops.secrets."vpn-keys/proton-wireguard/defiant-p2p".path;
# ListenPort = 51820; # ListenPort = 51820;
# # RouteTable = "p2p";
# }; # };
# wireguardPeers = [ # wireguardPeers = [
# { # {
# PublicKey = "rRO6yJim++Ezz6scCLMaizI+taDjU1pzR2nfW6qKbW0="; # PublicKey = "rRO6yJim++Ezz6scCLMaizI+taDjU1pzR2nfW6qKbW0=";
# Endpoint = "185.230.126.146:51820"; # Endpoint = "185.230.126.146:51820";
# AllowedIPs = ["0.0.0.0/0"]; # AllowedIPs = ["0.0.0.0/0"];
# RouteTable = "off";
# } # }
# ]; # ];
# }; # };
@ -177,17 +167,25 @@
dns = ["192.168.1.1"]; dns = ["192.168.1.1"];
}; };
# "45-p2p0" = { # For some reason this isn't working. It looks like traffic goes out and comes back but doesn't get correctly routed back to the wg interface on the return trip
# matchConfig.Name = "p2p0"; # debugging steps:
# try sending data on the interface `ping -I wg0 8.8.8.8`
# view all traffic on the interface `sudo tshark -i wg0`
# see what applications are listening to port 14666 (thats what we currently have qbittorent set up to use) `ss -tuln | grep 14666`
# "50-wg0" = {
# matchConfig.Name = "wg0";
# networkConfig = {
# DHCP = "no";
# };
# address = [ # address = [
# "10.2.0.2/32" # "10.2.0.2/32"
# ]; # ];
# routes = [ # # routes = [
# { # # {
# Destination = "0.0.0.0/0"; # # Destination = "10.2.0.2/32";
# } # # Gateway = "10.2.0.1";
# ]; # # }
# linkConfig.RequiredForOnline = false; # # ];
# }; # };
}; };
}; };
@ -196,13 +194,7 @@
boot.kernelParams = ["zfs.zfs_arc_max=53687091200"]; boot.kernelParams = ["zfs.zfs_arc_max=53687091200"];
services = { services = {
# TODO: move zfs scrubbing into module # temp enable desktop environment for setup
zfs = {
autoScrub.enable = true;
autoSnapshot.enable = true;
};
# temp enable desktop enviroment for setup
# Enable the X11 windowing system. # Enable the X11 windowing system.
xserver.enable = true; xserver.enable = true;
@ -220,6 +212,10 @@
acceleration = false; acceleration = false;
environmentVariables = {
OLLAMA_KEEP_ALIVE = "24h";
};
loadModels = [ loadModels = [
# conversation models # conversation models
"llama3.1:8b" "llama3.1:8b"
@ -236,6 +232,10 @@
# agent models # agent models
"qwen3:8b" "qwen3:8b"
"qwen3:32b" "qwen3:32b"
"qwen3:235b-a22b"
"qwen3-coder:30b"
"qwen3-coder:30b-a3b-fp16"
# embedding models # embedding models
"nomic-embed-text:latest" "nomic-embed-text:latest"
@ -282,17 +282,35 @@
subdomain = "search"; subdomain = "search";
}; };
virt-home-assistant = { actual = {
enable = false; enable = false;
networkBridge = "bond0"; subdomain = "budget";
hostDevice = "0x10c4:0xea60"; };
home-assistant = {
enable = true;
subdomain = "home";
openFirewall = true;
database = "postgres";
extensions = {
sonos.enable = true;
jellyfin.enable = true;
wyoming.enable = true;
};
};
paperless = {
enable = true;
subdomain = "documents";
passwordFile = config.sops.secrets."services/paperless_password".path;
}; };
qbittorrent = { qbittorrent = {
enable = true; enable = true;
mediaDir = "/srv/qbittorent"; mediaDir = "/srv/qbittorent";
openFirewall = true; openFirewall = true;
webPort = 8084; webuiPort = 8084;
}; };
}; };

View file

@ -126,6 +126,13 @@
}; };
syncthing.enable = true; syncthing.enable = true;
ollama = {
enable = true;
loadModels = [
"llama3.1:8b"
];
};
}; };
# Enable touchpad support (enabled default in most desktopManager). # Enable touchpad support (enabled default in most desktopManager).

142
flake.lock generated
View file

@ -7,11 +7,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1750680230, "lastModified": 1755519972,
"narHash": "sha256-kD88T/NqmcgfOBFAwphN30ccaUdj6K6+LG0XdM2w2LA=", "narHash": "sha256-bU4nqi3IpsUZJeyS8Jk85ytlX61i4b0KCxXX9YcOgVc=",
"owner": "nix-community", "owner": "nix-community",
"repo": "disko", "repo": "disko",
"rev": "8fd2d6c75009ac75f9a6fb18c33a239806778d01", "rev": "4073ff2f481f9ef3501678ff479ed81402caae6d",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -28,11 +28,11 @@
}, },
"locked": { "locked": {
"dir": "pkgs/firefox-addons", "dir": "pkgs/firefox-addons",
"lastModified": 1750737804, "lastModified": 1755921820,
"narHash": "sha256-wClGd2PhxdjjphR6wIgoiDcR+Gfg4/+FyseSOjIIzVU=", "narHash": "sha256-xTRXoaGtuIi4VvJNGuHC8DPHnEIJUqVtt7kqU8MdXes=",
"owner": "rycee", "owner": "rycee",
"repo": "nur-expressions", "repo": "nur-expressions",
"rev": "aaaf4fec792bad465ea4a35c0be5bc2a54f33095", "rev": "c43149f02063de9b0d75c2b45f54631bd82667b2",
"type": "gitlab" "type": "gitlab"
}, },
"original": { "original": {
@ -75,39 +75,6 @@
"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": {
"locked": {
"lastModified": 1712898590,
"narHash": "sha256-FhGIEU93VHAChKEXx905TSiPZKga69bWl1VB37FK//I=",
"owner": "lf-",
"repo": "flakey-profile",
"rev": "243c903fd8eadc0f63d205665a92d4df91d42d9d",
"type": "github"
},
"original": {
"owner": "lf-",
"repo": "flakey-profile",
"type": "github"
}
},
"home-manager": { "home-manager": {
"inputs": { "inputs": {
"nixpkgs": [ "nixpkgs": [
@ -115,11 +82,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1750730235, "lastModified": 1755914636,
"narHash": "sha256-rZErlxiV7ssvI8t7sPrKU+fRigNc2KvoKZG3gtUtK50=", "narHash": "sha256-VJ+Gm6YsHlPfUCpmRQxvdiZW7H3YPSrdVOewQHAhZN8=",
"owner": "nix-community", "owner": "nix-community",
"repo": "home-manager", "repo": "home-manager",
"rev": "d07e9cceb4994ed64a22b9b36f8b76923e87ac38", "rev": "8b55a6ac58b678199e5bba701aaff69e2b3281c0",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -143,41 +110,6 @@
"type": "github" "type": "github"
} }
}, },
"lix": {
"flake": false,
"locked": {
"lastModified": 1746827285,
"narHash": "sha256-hsFe4Tsqqg4l+FfQWphDtjC79WzNCZbEFhHI8j2KJzw=",
"rev": "47aad376c87e2e65967f17099277428e4b3f8e5a",
"type": "tarball",
"url": "https://git.lix.systems/api/v1/repos/lix-project/lix/archive/47aad376c87e2e65967f17099277428e4b3f8e5a.tar.gz?rev=47aad376c87e2e65967f17099277428e4b3f8e5a"
},
"original": {
"type": "tarball",
"url": "https://git.lix.systems/lix-project/lix/archive/2.93.0.tar.gz"
}
},
"lix-module": {
"inputs": {
"flake-utils": "flake-utils",
"flakey-profile": "flakey-profile",
"lix": "lix",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1746838955,
"narHash": "sha256-11R4K3iAx4tLXjUs+hQ5K90JwDABD/XHhsM9nkeS5N8=",
"rev": "cd2a9c028df820a83ca2807dc6c6e7abc3dfa7fc",
"type": "tarball",
"url": "https://git.lix.systems/api/v1/repos/lix-project/nixos-module/archive/cd2a9c028df820a83ca2807dc6c6e7abc3dfa7fc.tar.gz?rev=cd2a9c028df820a83ca2807dc6c6e7abc3dfa7fc"
},
"original": {
"type": "tarball",
"url": "https://git.lix.systems/lix-project/nixos-module/archive/2.93.0.tar.gz"
}
},
"nix-darwin": { "nix-darwin": {
"inputs": { "inputs": {
"nixpkgs": [ "nixpkgs": [
@ -185,11 +117,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1750618568, "lastModified": 1755825449,
"narHash": "sha256-w9EG5FOXrjXGfbqCcQg9x1lMnTwzNDW5BMXp8ddy15E=", "narHash": "sha256-XkiN4NM9Xdy59h69Pc+Vg4PxkSm9EWl6u7k6D5FZ5cM=",
"owner": "LnL7", "owner": "LnL7",
"repo": "nix-darwin", "repo": "nix-darwin",
"rev": "1dd19f19e4b53a1fd2e8e738a08dd5fe635ec7e5", "rev": "8df64f819698c1fee0c2969696f54a843b2231e8",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -221,17 +153,17 @@
}, },
"nix-vscode-extensions": { "nix-vscode-extensions": {
"inputs": { "inputs": {
"flake-utils": "flake-utils_2", "flake-utils": "flake-utils",
"nixpkgs": [ "nixpkgs": [
"nixpkgs" "nixpkgs"
] ]
}, },
"locked": { "locked": {
"lastModified": 1750730765, "lastModified": 1755914146,
"narHash": "sha256-MIcOcvxqAXUv2TJjf19aVXdtVrD8Gkcfi4W4pKkT0Lw=", "narHash": "sha256-ew98ilw4NTodKlILnr3ndsT0Aj9JhqC507JB3efa0pY=",
"owner": "nix-community", "owner": "nix-community",
"repo": "nix-vscode-extensions", "repo": "nix-vscode-extensions",
"rev": "1a1442e13dc1730de0443f80dcf02658365e999a", "rev": "ff42a421ff1d415caa0125e6af6f3bd82e642838",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -242,11 +174,11 @@
}, },
"nixos-hardware": { "nixos-hardware": {
"locked": { "locked": {
"lastModified": 1750431636, "lastModified": 1755330281,
"narHash": "sha256-vnzzBDbCGvInmfn2ijC4HsIY/3W1CWbwS/YQoFgdgPg=", "narHash": "sha256-aJHFJWP9AuI8jUGzI77LYcSlkA9wJnOIg4ZqftwNGXA=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixos-hardware", "repo": "nixos-hardware",
"rev": "1552a9f4513f3f0ceedcf90320e48d3d47165712", "rev": "3dac8a872557e0ca8c083cdcfc2f218d18e113b0",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -258,11 +190,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1750506804, "lastModified": 1755615617,
"narHash": "sha256-VLFNc4egNjovYVxDGyBYTrvVCgDYgENp5bVi9fPTDYc=", "narHash": "sha256-HMwfAJBdrr8wXAkbGhtcby1zGFvs+StOp19xNsbqdOg=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "4206c4cb56751df534751b058295ea61357bbbaa", "rev": "20075955deac2583bb12f07151c2df830ef346b4",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -279,7 +211,6 @@
"flake-compat": "flake-compat", "flake-compat": "flake-compat",
"home-manager": "home-manager", "home-manager": "home-manager",
"impermanence": "impermanence", "impermanence": "impermanence",
"lix-module": "lix-module",
"nix-darwin": "nix-darwin", "nix-darwin": "nix-darwin",
"nix-syncthing": "nix-syncthing", "nix-syncthing": "nix-syncthing",
"nix-vscode-extensions": "nix-vscode-extensions", "nix-vscode-extensions": "nix-vscode-extensions",
@ -293,11 +224,11 @@
"secrets": { "secrets": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1749061163, "lastModified": 1752531440,
"narHash": "sha256-WflcbitH7ErNZBFqZCdy1ODUqKF51xbu2zYfqA35+1M=", "narHash": "sha256-04tQ3EUrtmZ7g6fVUkZC4AbAG+Z7lng79qU3jsiqWJY=",
"ref": "refs/heads/main", "ref": "refs/heads/main",
"rev": "1c5c059c0c7b6ce691993262fe10a2b63e1c31ba", "rev": "f016767c13aa36dde91503f7a9f01bdd02468045",
"revCount": 19, "revCount": 20,
"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"
}, },
@ -313,11 +244,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1750119275, "lastModified": 1754988908,
"narHash": "sha256-Rr7Pooz9zQbhdVxux16h7URa6mA80Pb/G07T4lHvh0M=", "narHash": "sha256-t+voe2961vCgrzPFtZxha0/kmFSHFobzF00sT8p9h0U=",
"owner": "Mic92", "owner": "Mic92",
"repo": "sops-nix", "repo": "sops-nix",
"rev": "77c423a03b9b2b79709ea2cb63336312e78b72e2", "rev": "3223c7a92724b5d804e9988c6b447a0d09017d48",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -360,21 +291,6 @@
"repo": "default", "repo": "default",
"type": "github" "type": "github"
} }
},
"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"
}
} }
}, },
"root": "root", "root": "root",

View file

@ -5,10 +5,10 @@
# base packages # base packages
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
lix-module = { # lix-module = {
url = "https://git.lix.systems/lix-project/nixos-module/archive/2.93.0.tar.gz"; # url = "https://git.lix.systems/lix-project/nixos-module/archive/2.93.3-1.tar.gz";
inputs.nixpkgs.follows = "nixpkgs"; # inputs.nixpkgs.follows = "nixpkgs";
}; # };
# secret encryption # secret encryption
sops-nix = { sops-nix = {

View file

@ -2,5 +2,6 @@
{inputs, ...}: { {inputs, ...}: {
nixpkgs.overlays = [ nixpkgs.overlays = [
inputs.steam-fetcher.overlays.default inputs.steam-fetcher.overlays.default
inputs.nix-vscode-extensions.overlays.default
]; ];
} }

View file

@ -0,0 +1,42 @@
{
buildNpmPackage,
vscode-utils,
pkgs,
...
}: let
version = "0.0.1";
pname = "ai-code";
publisher = "jan-leila";
vsix = buildNpmPackage {
inherit version pname;
src = builtins.fetchGit {
url = "ssh://git@git.jan-leila.com/jan-leila/ai-code.git";
rev = "d48e01713021dbb30de0ebbee2cfaf99e4e9b5a6";
};
npmDepsHash = "sha256-kjMyEnT3dz0yH5Ydh+aGoFDocKpBYGRmfnwbEdvvgpY=";
nativeBuildInputs = with pkgs; [
vsce
];
buildPhase = ''
${pkgs.vsce}/bin/vsce package -o ${pname}.zip
'';
installPhase = ''
mkdir -p $out
mv ${pname}.zip $out/${pname}.zip
'';
};
in
vscode-utils.buildVscodeExtension {
inherit pname version;
src = "${vsix}/${pname}.zip";
vscodeExtUniqueId = "${publisher}.${pname}";
vscodeExtPublisher = publisher;
vscodeExtName = pname;
}

View file

@ -0,0 +1,3 @@
{pkgs, ...}: {
ai-code = pkgs.callPackage ./ai-code.nix {};
}

View file

@ -19,5 +19,8 @@
(final: prev: { (final: prev: {
gdx-liftoff = pkgs.callPackage ./gdx-liftoff.nix {}; gdx-liftoff = pkgs.callPackage ./gdx-liftoff.nix {};
}) })
(final: prev: {
codium-extensions = pkgs.callPackage ./codium-extensions {};
})
]; ];
} }

View file

@ -1,73 +0,0 @@
{
lib,
pkgs,
config,
osConfig,
...
}: let
ai-tooling-enabled = config.user.continue.enable && osConfig.host.ai.enable;
in {
options.user.continue = {
enable = lib.mkEnableOption "should continue be enabled on this machine";
docs = lib.mkOption {
type = lib.types.attrsOf (lib.types.submodule ({name, ...}: {
options = {
name = lib.mkOption {
type = lib.types.str;
default = name;
};
startUrl = lib.mkOption {
type = lib.types.str;
};
};
}));
};
context = lib.mkOption {
type = lib.types.attrsOf (lib.types.submodule ({name, ...}: {
options = {
provider = lib.mkOption {
type = lib.types.str;
default = name;
};
};
}));
default = {
"code" = {};
"docs" = {};
"diff" = {};
"terminal" = {};
"problems" = {};
"folder" = {};
"codebase" = {};
};
};
};
config =
lib.mkIf ai-tooling-enabled
(lib.mkMerge [
{
home = {
file = {
".continue/config.yaml".source = (pkgs.formats.yaml {}).generate "continue-config" {
name = "Assistant";
version = "1.0.0";
schema = "v1";
models = lib.attrsets.attrValues osConfig.host.ai.models;
context = lib.attrsets.attrValues config.user.continue.context;
docs = lib.attrsets.attrValues config.user.continue.docs;
};
};
};
}
(lib.mkIf osConfig.host.impermanence.enable {
home.persistence."/persist${config.home.homeDirectory}" = {
directories = [
".continue/index"
".continue/sessions"
];
allowOther = true;
};
})
]);
}

View file

@ -6,7 +6,7 @@
./flipperzero.nix ./flipperzero.nix
./i18n.nix ./i18n.nix
./openssh.nix ./openssh.nix
./continue.nix ./gnome.nix
./programs ./programs
]; ];
} }

View file

@ -0,0 +1,106 @@
{
lib,
config,
...
}: {
options.gnome = {
extraWindowControls = lib.mkEnableOption "Should we add back in the minimize and maximize window controls?";
clockFormat = lib.mkOption {
type = lib.types.enum [
"12h"
"24h"
];
default = "24h";
};
colorScheme = lib.mkOption {
type = lib.types.enum [
"default"
"prefer-dark"
"prefer-light"
];
default = "default";
};
accentColor = lib.mkOption {
type = lib.types.enum [
"blue"
"teal"
"green"
"yellow"
"orange"
"red"
"pink"
"purple"
"slate"
];
default = "blue";
};
extensions = lib.mkOption {
type = lib.types.listOf lib.types.package;
default = [];
description = "The set of extensions to install and enable in the user environment.";
};
hotkeys = lib.mkOption {
type = lib.types.attrsOf (lib.types.submodule ({name, ...}: {
options = {
key = lib.mkOption {
type = lib.types.strMatching "[a-zA-Z0-9-]+";
default = builtins.replaceStrings [" " "/" "_"] ["-" "-" "-"] name;
};
name = lib.mkOption {
type = lib.types.str;
default = name;
};
binding = lib.mkOption {
type = lib.types.str;
};
command = lib.mkOption {
type = lib.types.str;
};
};
}));
default = {};
};
};
config = {
home.packages = config.gnome.extensions;
dconf = {
settings = lib.mkMerge [
{
"org/gnome/shell" = {
disable-user-extensions = false; # enables user extensions
enabled-extensions = builtins.map (extension: extension.extensionUuid) config.gnome.extensions;
};
"org/gnome/desktop/wm/preferences".button-layout = lib.mkIf config.gnome.extraWindowControls ":minimize,maximize,close";
"org/gnome/desktop/interface".color-scheme = config.gnome.colorScheme;
"org/gnome/desktop/interface".accent-color = config.gnome.accentColor;
"org/gnome/desktop/interface".clock-format = config.gnome.clockFormat;
}
(
lib.mkMerge (
builtins.map (value: let
entry = "org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/${value.key}";
in {
${entry} = {
binding = value.binding;
command = value.command;
name = value.name;
};
"org/gnome/settings-daemon/plugins/media-keys" = {
custom-keybindings = [
"/${entry}/"
];
};
})
(
lib.attrsets.mapAttrsToList (_: value: value) config.gnome.hotkeys
)
)
)
];
};
};
}

View file

@ -1,29 +1,15 @@
{ {
lib, lib,
pkgs,
config, config,
osConfig, osConfig,
... ...
}: { }: {
options.programs.anki = { config = lib.mkIf (config.programs.anki.enable && osConfig.host.impermanence.enable) {
enable = lib.mkEnableOption "enable anki"; home.persistence."/persist${config.home.homeDirectory}" = {
}; directories = [
"${config.xdg.dataHome}/Anki2/"
config = lib.mkIf config.programs.anki.enable (lib.mkMerge [
{
home.packages = with pkgs; [
anki
]; ];
} allowOther = true;
( };
lib.mkIf osConfig.host.impermanence.enable { };
home.persistence."/persist${config.home.homeDirectory}" = {
directories = [
"${config.xdg.dataHome}/Anki2/"
];
allowOther = true;
};
}
)
]);
} }

View file

@ -16,5 +16,6 @@
./bruno.nix ./bruno.nix
./dbeaver.nix ./dbeaver.nix
./steam.nix ./steam.nix
./vscode
]; ];
} }

View file

@ -0,0 +1,45 @@
{
lib,
pkgs,
...
}: let
pkgsRepository = pkgs.codium-extensions;
in {
options.programs.vscode.profiles = lib.mkOption {
type = lib.types.attrsOf (lib.types.submodule ({config, ...}: {
options = {
extraExtensions.aiCode = {
enable = lib.mkEnableOption "should the ai code extension for vscode be enabled";
extension = lib.mkPackageOption pkgsRepository "ai-code" {};
ollamaHost = lib.mkOption {
type = lib.types.nullOr lib.types.str;
description = "what host should be used for ollama";
default = null;
};
inlineCompletion = {
enable = lib.mkOption {
type = lib.types.bool;
description = "should inline completion be enabled";
default = true;
};
model = lib.mkOption {
type = lib.types.nullOr lib.types.str;
description = "what model should be used for ollama";
default = null;
};
};
};
};
config = lib.mkIf config.extraExtensions.aiCode.enable {
extensions = [
config.extraExtensions.aiCode.extension
];
userSettings = {
"aiCode.ollamaHost" = lib.mkIf (config.extraExtensions.aiCode.ollamaHost != null) config.extraExtensions.aiCode.ollamaHost;
"aiCode.inlineCompletion.enable" = config.extraExtensions.aiCode.inlineCompletion.enable;
"aiCode.inlineCompletion.model" = lib.mkIf (config.extraExtensions.aiCode.inlineCompletion.model != null) config.extraExtensions.aiCode.inlineCompletion.model;
};
};
}));
};
}

View file

@ -0,0 +1,34 @@
{
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.alejandra = {
enable = lib.mkEnableOption "Enable Alejandra extension for Nix formatting";
extension = lib.mkPackageOption pkgsRepository "alejandra" {
default = ["kamadorueda" "alejandra"];
};
};
};
config = lib.mkIf config.extraExtensions.alejandra.enable {
extensions = [config.extraExtensions.alejandra.extension];
userSettings = {
"[nix]" = {
"editor.defaultFormatter" = "kamadorueda.alejandra";
"editor.formatOnPaste" = true;
"editor.formatOnSave" = true;
"editor.formatOnType" = true;
};
"alejandra.program" = "alejandra";
};
};
}));
};
}

View file

@ -0,0 +1,27 @@
{
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.astroVscode = {
enable = lib.mkEnableOption "should the astro-vscode extension for vscode be enabled";
extension = lib.mkPackageOption pkgsRepository "astro-vscode" {
default = ["astro-build" "astro-vscode"];
};
};
};
config = lib.mkIf config.extraExtensions.astroVscode.enable {
extensions = [
config.extraExtensions.astroVscode.extension
];
};
}));
};
}

View file

@ -0,0 +1,27 @@
{
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.atomKeybindings = {
enable = lib.mkEnableOption "should the atom keybindings extension for vscode be enabled";
extension = lib.mkPackageOption pkgsRepository "atom-keybindings" {
default = ["ms-vscode" "atom-keybindings"];
};
};
};
config = lib.mkIf config.extraExtensions.atomKeybindings.enable {
extensions = [
config.extraExtensions.atomKeybindings.extension
];
};
}));
};
}

View file

@ -0,0 +1,27 @@
{
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.autoRenameTag = {
enable = lib.mkEnableOption "should the auto-rename-tag extension for vscode be enabled";
extension = lib.mkPackageOption pkgsRepository "auto-rename-tag" {
default = ["formulahendry" "auto-rename-tag"];
};
};
};
config = lib.mkIf config.extraExtensions.autoRenameTag.enable {
extensions = [
config.extraExtensions.autoRenameTag.extension
];
};
}));
};
}

View file

@ -0,0 +1,27 @@
{
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.claudeDev = {
enable = lib.mkEnableOption "should the claude-dev extension for vscode be enabled";
extension = lib.mkPackageOption pkgsRepository "claude-dev" {
default = ["saoudrizwan" "claude-dev"];
};
};
};
config = lib.mkIf config.extraExtensions.claudeDev.enable {
extensions = [
config.extraExtensions.claudeDev.extension
];
};
}));
};
}

View file

@ -0,0 +1,24 @@
{...}: {
imports = [
./oneDark.nix
./atomKeybindings.nix
./aiCode.nix
./alejandra.nix
./nixIde.nix
./autoRenameTag.nix
./es7ReactJsSnippets.nix
./liveServer.nix
./tauriVscode.nix
./vscodeEslint.nix
./vscodeJest.nix
./vscodeStandard.nix
./vscodeStylelint.nix
./go.nix
./evenBetterToml.nix
./openRemoteSsh.nix
./astroVscode.nix
./vscodeMdx.nix
./claudeDev.nix
./nearley.nix
];
}

View file

@ -0,0 +1,27 @@
{
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.es7ReactJsSnippets = {
enable = lib.mkEnableOption "should the es7-react-js-snippets extension for vscode be enabled";
extension = lib.mkPackageOption pkgsRepository "es7-react-js-snippets" {
default = ["dsznajder" "es7-react-js-snippets"];
};
};
};
config = lib.mkIf config.extraExtensions.es7ReactJsSnippets.enable {
extensions = [
config.extraExtensions.es7ReactJsSnippets.extension
];
};
}));
};
}

View file

@ -0,0 +1,27 @@
{
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.evenBetterToml = {
enable = lib.mkEnableOption "should the even-better-toml extension for vscode be enabled";
extension = lib.mkPackageOption pkgsRepository "even-better-toml" {
default = ["tamasfe" "even-better-toml"];
};
};
};
config = lib.mkIf config.extraExtensions.evenBetterToml.enable {
extensions = [
config.extraExtensions.evenBetterToml.extension
];
};
}));
};
}

View file

@ -0,0 +1,27 @@
{
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.go = {
enable = lib.mkEnableOption "should the go extension for vscode be enabled";
extension = lib.mkPackageOption pkgsRepository "go" {
default = ["golang" "go"];
};
};
};
config = lib.mkIf config.extraExtensions.go.enable {
extensions = [
config.extraExtensions.go.extension
];
};
}));
};
}

View file

@ -0,0 +1,27 @@
{
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.liveServer = {
enable = lib.mkEnableOption "should the live-server extension for vscode be enabled";
extension = lib.mkPackageOption pkgsRepository "live-server" {
default = ["ms-vscode" "live-server"];
};
};
};
config = lib.mkIf config.extraExtensions.liveServer.enable {
extensions = [
config.extraExtensions.liveServer.extension
];
};
}));
};
}

View file

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

View file

@ -0,0 +1,29 @@
{
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.nixIde = {
enable = lib.mkEnableOption "Enable Nix IDE extension";
extension = lib.mkPackageOption pkgsRepository "nix-ide" {
default = ["jnoortheen" "nix-ide"];
};
};
};
config = lib.mkIf config.extraExtensions.nixIde.enable {
extensions = [config.extraExtensions.nixIde.extension];
userSettings = {
"nix.enableLanguageServer" = true;
"nix.serverPath" = "nil";
};
};
}));
};
}

View file

@ -0,0 +1,30 @@
{
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.oneDark = {
enable = lib.mkEnableOption "should the one dark theme for vscode be enabled";
extension = lib.mkPackageOption pkgsRepository "onedark" {
default = ["akamud" "vscode-theme-onedark"];
};
};
};
config = lib.mkIf config.extraExtensions.oneDark.enable {
extensions = [
config.extraExtensions.oneDark.extension
];
userSettings = {
"workbench.colorTheme" = "Atom One Dark";
};
};
}));
};
}

View file

@ -0,0 +1,27 @@
{
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.openRemoteSsh = {
enable = lib.mkEnableOption "should the open-remote-ssh extension for vscode be enabled";
extension = lib.mkPackageOption pkgsRepository "open-remote-ssh" {
default = ["jeanp413" "open-remote-ssh"];
};
};
};
config = lib.mkIf config.extraExtensions.openRemoteSsh.enable {
extensions = [
config.extraExtensions.openRemoteSsh.extension
];
};
}));
};
}

View file

@ -0,0 +1,27 @@
{
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.tauriVscode = {
enable = lib.mkEnableOption "should the tauri-vscode extension for vscode be enabled";
extension = lib.mkPackageOption pkgsRepository "tauri-vscode" {
default = ["tauri-apps" "tauri-vscode"];
};
};
};
config = lib.mkIf config.extraExtensions.tauriVscode.enable {
extensions = [
config.extraExtensions.tauriVscode.extension
];
};
}));
};
}

View file

@ -0,0 +1,27 @@
{
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.vscodeEslint = {
enable = lib.mkEnableOption "should the vscode-eslint extension for vscode be enabled";
extension = lib.mkPackageOption pkgsRepository "vscode-eslint" {
default = ["dbaeumer" "vscode-eslint"];
};
};
};
config = lib.mkIf config.extraExtensions.vscodeEslint.enable {
extensions = [
config.extraExtensions.vscodeEslint.extension
];
};
}));
};
}

View file

@ -0,0 +1,27 @@
{
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.vscodeJest = {
enable = lib.mkEnableOption "should the vscode-jest extension for vscode be enabled";
extension = lib.mkPackageOption pkgsRepository "vscode-jest" {
default = ["orta" "vscode-jest"];
};
};
};
config = lib.mkIf config.extraExtensions.vscodeJest.enable {
extensions = [
config.extraExtensions.vscodeJest.extension
];
};
}));
};
}

View file

@ -0,0 +1,27 @@
{
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.vscodeMdx = {
enable = lib.mkEnableOption "should the vscode-mdx extension for vscode be enabled";
extension = lib.mkPackageOption pkgsRepository "vscode-mdx" {
default = ["unifiedjs" "vscode-mdx"];
};
};
};
config = lib.mkIf config.extraExtensions.vscodeMdx.enable {
extensions = [
config.extraExtensions.vscodeMdx.extension
];
};
}));
};
}

View file

@ -0,0 +1,27 @@
{
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.vscodeStandard = {
enable = lib.mkEnableOption "should the vscode-standard extension for vscode be enabled";
extension = lib.mkPackageOption pkgsRepository "vscode-standard" {
default = ["standard" "vscode-standard"];
};
};
};
config = lib.mkIf config.extraExtensions.vscodeStandard.enable {
extensions = [
config.extraExtensions.vscodeStandard.extension
];
};
}));
};
}

View file

@ -0,0 +1,27 @@
{
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.vscodeStylelint = {
enable = lib.mkEnableOption "should the vscode-stylelint extension for vscode be enabled";
extension = lib.mkPackageOption pkgsRepository "vscode-stylelint" {
default = ["stylelint" "vscode-stylelint"];
};
};
};
config = lib.mkIf config.extraExtensions.vscodeStylelint.enable {
extensions = [
config.extraExtensions.vscodeStylelint.extension
];
};
}));
};
}

View file

@ -22,7 +22,7 @@
gnome-characters # character set viewer gnome-characters # character set viewer
gnome-music # music player gnome-music # music player
gnome-photos # photo viewer gnome-photos # photo viewer
gnome-logs # log viwer gnome-logs # log viewer
gnome-maps # map viewer gnome-maps # map viewer
gnome-tour # welcome tour gnome-tour # welcome tour
hitori # sudoku game hitori # sudoku game
@ -32,7 +32,14 @@
]; ];
services = { services = {
# Enable CUPS to print documents. # Enable CUPS to print documents.
printing.enable = true; printing = {
enable = true;
drivers = [
pkgs.hplip
pkgs.gutenprint
pkgs.gutenprintBin
];
};
xserver = { xserver = {
# Enable the X11 windowing system. # Enable the X11 windowing system.

View file

@ -136,6 +136,8 @@ in {
}; };
accounts = { accounts = {
zfs_notifications = { zfs_notifications = {
auth = true;
tls = true;
host = config.host.storage.notifications.host; host = config.host.storage.notifications.host;
passwordeval = "cat ${config.host.storage.notifications.tokenFile}"; passwordeval = "cat ${config.host.storage.notifications.tokenFile}";
user = config.host.storage.notifications.user; user = config.host.storage.notifications.user;
@ -149,14 +151,13 @@ in {
autoSnapshot.enable = true; autoSnapshot.enable = true;
zed = lib.mkIf config.host.storage.notifications.enable { zed = lib.mkIf config.host.storage.notifications.enable {
# this option is broken we are just going to disable it enableMail = true;
enableMail = false;
settings = { settings = {
ZED_DEBUG_LOG = "/tmp/zed.debug.log"; ZED_DEBUG_LOG = "/tmp/zed.debug.log";
ZED_EMAIL_ADDR = [config.host.storage.notifications.to]; ZED_EMAIL_ADDR = [config.host.storage.notifications.to];
ZED_EMAIL_PROG = "${pkgs.msmtp}/bin/msmtp"; ZED_EMAIL_PROG = "${pkgs.msmtp}/bin/msmtp";
ZED_EMAIL_OPTS = "@ADDRESS@"; ZED_EMAIL_OPTS = "-a zfs_notifications @ADDRESS@";
ZED_NOTIFY_INTERVAL_SECS = 3600; ZED_NOTIFY_INTERVAL_SECS = 3600;
ZED_NOTIFY_VERBOSE = true; ZED_NOTIFY_VERBOSE = true;

View file

@ -25,6 +25,18 @@
} }
]; ];
# 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; programs.fuse.userAllowOther = true;
boot.initrd.postResumeCommands = lib.mkAfter '' boot.initrd.postResumeCommands = lib.mkAfter ''

View file

@ -28,10 +28,6 @@
}; };
})) }))
(lib.mkIf config.host.impermanence.enable { (lib.mkIf config.host.impermanence.enable {
# TODO: move this somewhere common
systemd.tmpfiles.rules = [
"d /var/lib/private 0700 root root"
];
environment.persistence."/persist/system/root" = { environment.persistence."/persist/system/root" = {
enable = true; enable = true;
hideMounts = true; hideMounts = true;

View file

@ -0,0 +1,54 @@
{
lib,
config,
...
}: let
dataDirectory = "/var/lib/actual/";
in {
options.services.actual = {
subdomain = lib.mkOption {
type = lib.types.str;
default = "actual";
description = "subdomain of base domain that actual will be hosted at";
};
};
config = lib.mkIf config.services.actual.enable (lib.mkMerge [
{
systemd.tmpfiles.rules = [
"d ${dataDirectory} 2770 actual actual"
];
host = {
reverse_proxy.subdomains.${config.services.actual.subdomain} = {
target = "http://localhost:${toString config.services.actual.settings.port}";
};
};
services.actual = {
settings = {
ACTUAL_DATA_DIR = dataDirectory;
};
};
}
(lib.mkIf config.services.fail2ban.enable {
# TODO: configuration for fail2ban for actual
})
(lib.mkIf config.host.impermanence.enable {
assertions = [
{
assertion = config.services.actual.settings.ACTUAL_DATA_DIR == dataDirectory;
message = "actual data location does not match persistence";
}
];
environment.persistence."/persist/system/root" = {
directories = [
{
directory = dataDirectory;
user = "actual";
group = "actual";
}
];
};
})
]);
}

View file

@ -1,72 +0,0 @@
{
lib,
config,
...
}: let
dnsPort = 53;
in {
options.host.adguardhome = {
enable = lib.mkEnableOption "should home-assistant be enabled on this computer";
directory = lib.mkOption {
type = lib.types.str;
default = "/var/lib/AdGuardHome/";
};
};
config = lib.mkIf config.host.adguardhome.enable (lib.mkMerge [
{
services.adguardhome = {
enable = true;
mutableSettings = false;
settings = {
dns = {
bootstrap_dns = [
"1.1.1.1"
"9.9.9.9"
];
upstream_dns = [
"dns.quad9.net"
];
};
filtering = {
protection_enabled = true;
filtering_enabled = true;
parental_enabled = false; # Parental control-based DNS requests filtering.
safe_search = {
enabled = false; # Enforcing "Safe search" option for search engines, when possible.
};
};
# The following notation uses map
# to not have to manually create {enabled = true; url = "";} for every filter
# This is, however, fully optional
filters =
map (url: {
enabled = true;
url = url;
}) [
"https://adguardteam.github.io/HostlistsRegistry/assets/filter_1.txt"
"https://adguardteam.github.io/HostlistsRegistry/assets/filter_9.txt" # The Big List of Hacked Malware Web Sites
"https://adguardteam.github.io/HostlistsRegistry/assets/filter_11.txt" # malicious url blocklist
];
};
};
networking.firewall.allowedTCPPorts = [
dnsPort
];
}
(lib.mkIf config.host.impermanence.enable {
environment.persistence."/persist/system/root" = {
enable = true;
hideMounts = true;
directories = [
{
directory = config.host.adguardhome.directory;
user = "adguardhome";
group = "adguardhome";
}
];
};
})
]);
}

View file

@ -8,9 +8,11 @@
./jellyfin.nix ./jellyfin.nix
./forgejo.nix ./forgejo.nix
./searx.nix ./searx.nix
./virt-home-assistant.nix ./home-assistant.nix
./adguardhome.nix ./wyoming.nix
./immich.nix ./immich.nix
./qbittorent.nix ./qbittorent.nix
./paperless.nix
./actual.nix
]; ];
} }

View file

@ -16,20 +16,6 @@ in {
failregex = "limiting requests, excess:.* by zone.*client: <HOST>" failregex = "limiting requests, excess:.* by zone.*client: <HOST>"
'') '')
); );
# "fail2ban/filter.d/hass.local".text = lib.mkIf config.services.home-assistant.enable (
# pkgs.lib.mkDefault (pkgs.lib.mkAfter ''
# [INCLUDES]
# before = common.conf
# [Definition]
# failregex = ^%(__prefix_line)s.*Login attempt or request with invalid authentication from <HOST>.*$
# ignoreregex =
# [Init]
# datepattern = ^%%Y-%%m-%%d %%H:%%M:%%S
# '')
# );
}; };
services.fail2ban = { services.fail2ban = {
@ -61,16 +47,6 @@ in {
bantime = 600; bantime = 600;
maxretry = 5; maxretry = 5;
}; };
# home-assistant-iptables.settings = lib.mkIf config.services.home-assistant.enable {
# enabled = true;
# filter = "hass";
# action = ''iptables-multiport[name=HTTP, port="http,https"]'';
# logpath = "${config.services.home-assistant.configDir}/*.log";
# backend = "auto";
# findtime = 600;
# bantime = 600;
# maxretry = 5;
# };
# TODO; figure out if there is any fail2ban things we can do on searx # TODO; figure out if there is any fail2ban things we can do on searx
# searx-iptables.settings = lib.mkIf config.services.searx.enable {}; # searx-iptables.settings = lib.mkIf config.services.searx.enable {};
}; };

View file

@ -28,6 +28,12 @@ in {
extraUsers = { extraUsers = {
${db_user} = { ${db_user} = {
isClient = true; isClient = true;
createUser = true;
};
};
extraDatabases = {
${db_user} = {
name = db_user;
}; };
}; };
}; };

View file

@ -1,130 +1,229 @@
{ {
lib, lib,
pkgs,
config, config,
inputs,
... ...
}: let }: let
configDir = "/var/lib/hass"; configDir = "/var/lib/hass";
dbUser = "hass";
in { in {
options.host.home-assistant = { options.services.home-assistant = {
enable = lib.mkEnableOption "should home-assistant be enabled on this computer";
subdomain = lib.mkOption { subdomain = lib.mkOption {
type = lib.types.str; type = lib.types.str;
description = "subdomain of base domain that home-assistant will be hosted at"; description = "subdomain of base domain that home-assistant will be hosted at";
default = "home-assistant"; default = "home-assistant";
}; };
database = lib.mkOption {
type = lib.types.enum [
"builtin"
"postgres"
];
description = "what database do we want to use";
default = "builtin";
};
extensions = {
sonos = {
enable = lib.mkEnableOption "enable the sonos plugin";
port = lib.mkOption {
type = lib.types.int;
default = 1400;
description = "what port to use for sonos discovery";
};
};
jellyfin = {
enable = lib.mkEnableOption "enable the jellyfin plugin";
};
wyoming = {
enable = lib.mkEnableOption "enable wyoming";
};
};
}; };
config = lib.mkIf config.host.home-assistant.enable (lib.mkMerge [ config = lib.mkIf config.services.home-assistant.enable (lib.mkMerge [
{ {
virtualisation.libvirt = { host = {
swtpm.enable = true; reverse_proxy.subdomains.${config.services.home-assistant.subdomain} = {
connections."qemu:///session" = { target = "http://localhost:${toString config.services.home-assistant.config.http.server_port}";
networks = [
{ websockets.enable = true;
definition = inputs.nix-virt.lib.network.writeXML (inputs.nix-virt.lib.network.templates.bridge forwardHeaders.enable = true;
{
uuid = "d57e37e2-311f-4e5c-a484-97c2210c2770"; extraConfig = ''
subnet_byte = 71; add_header Upgrade $http_upgrade;
}); add_header Connection \"upgrade\";
active = true;
} proxy_buffering off;
];
domains = [ proxy_read_timeout 90;
{ '';
definition = inputs.nix-virt.lib.domain.writeXML (inputs.nix-virt.lib.domain.templates.linux
{
name = "Home Assistant";
uuid = "c5cc0efc-6101-4c1d-be31-acbba203ccde";
memory = {
count = 4;
unit = "GiB";
};
# storage_vol = {
# pool = "MyPool";
# volume = "Penguin.qcow2";
# };
});
}
];
}; };
}; };
# systemd.tmpfiles.rules = [ services.home-assistant = {
# "f ${config.services.home-assistant.configDir}/automations.yaml 0755 hass hass" configDir = configDir;
# ]; extraComponents = [
# services.home-assistant = { "default_config"
# enable = true; "esphome"
# configDir = configDir; "met"
# extraComponents = [ "radio_browser"
# "met" "isal"
# "radio_browser" "zha"
# "isal" "webostv"
# "zha" "tailscale"
# "jellyfin" "syncthing"
# "webostv" "analytics_insights"
# "tailscale" "unifi"
# "syncthing" "openweathermap"
# "sonos" "ollama"
# "analytics_insights" "mobile_app"
# "unifi" "logbook"
# "openweathermap" "ssdp"
# ]; "usb"
# config = { "webhook"
# http = { "bluetooth"
# server_port = 8082; "dhcp"
# use_x_forwarded_for = true; "energy"
# trusted_proxies = ["127.0.0.1" "::1"]; "history"
# ip_ban_enabled = true; "backup"
# login_attempts_threshold = 10; "assist_pipeline"
# }; "conversation"
# # recorder.db_url = "postgresql://@/${db_user}"; "sun"
# "automation manual" = []; "zeroconf"
# "automation ui" = "!include automations.yaml"; "cpuspeed"
# }; ];
# extraPackages = python3Packages: config = {
# with python3Packages; [ http = {
# hassil server_port = 8123;
# numpy use_x_forwarded_for = true;
# gtts trusted_proxies = ["127.0.0.1" "::1"];
# ]; ip_ban_enabled = true;
# }; login_attempts_threshold = 10;
# host = { };
# reverse_proxy.subdomains.${config.host.home-assistant.subdomain} = { homeassistant = {
# target = "http://localhost:${toString config.services.home-assistant.config.http.server_port}"; external_url = "https://${config.services.home-assistant.subdomain}.${config.host.reverse_proxy.hostname}";
# internal_url = "http://192.168.1.2:8123";
};
recorder.db_url = "postgresql://@/${dbUser}";
"automation manual" = [];
"automation ui" = "!include automations.yaml";
mobile_app = {};
};
extraPackages = python3Packages:
with python3Packages; [
hassil
numpy
gtts
];
};
# websockets.enable = true; # TODO: configure /var/lib/hass/secrets.yaml via sops
# forwardHeaders.enable = true;
# extraConfig = '' networking.firewall.allowedUDPPorts = [
# add_header Upgrade $http_upgrade; 1900
# add_header Connection \"upgrade\"; ];
# proxy_buffering off; systemd.tmpfiles.rules = [
"f ${config.services.home-assistant.configDir}/automations.yaml 0755 hass hass"
# proxy_read_timeout 90; ];
# '';
# };
# };
} }
(lib.mkIf (config.services.home-assistant.extensions.sonos.enable) {
services.home-assistant.extraComponents = ["sonos"];
networking.firewall.allowedTCPPorts = [
config.services.home-assistant.extensions.sonos.port
];
})
(lib.mkIf (config.services.home-assistant.extensions.jellyfin.enable) {
services.home-assistant.extraComponents = ["jellyfin"];
# TODO: configure port, address, and login information here
})
(lib.mkIf (config.services.home-assistant.extensions.wyoming.enable) {
services.home-assistant.extraComponents = ["wyoming"];
services.wyoming.enable = true;
})
(lib.mkIf (config.services.home-assistant.database == "postgres") {
host = {
postgres = {
enable = true;
extraUsers = {
${dbUser} = {
isClient = true;
createUser = true;
};
};
extraDatabases = {
${dbUser} = {
name = dbUser;
};
};
};
};
services.home-assistant = {
extraPackages = python3Packages:
with python3Packages; [
psycopg2
];
};
systemd.services.home-assistant = {
requires = [
config.systemd.services.postgresql.name
];
};
})
(lib.mkIf config.services.fail2ban.enable {
environment.etc = {
"fail2ban/filter.d/hass.local".text = lib.mkIf config.services.home-assistant.enable (
pkgs.lib.mkDefault (pkgs.lib.mkAfter ''
[INCLUDES]
before = common.conf
[Definition]
failregex = ^%(__prefix_line)s.*Login attempt or request with invalid authentication from <HOST>.*$
ignoreregex =
[Init]
datepattern = ^%%Y-%%m-%%d %%H:%%M:%%S
'')
);
};
services.fail2ban = {
jails = {
home-assistant-iptables.settings = lib.mkIf config.services.home-assistant.enable {
enabled = true;
filter = "hass";
action = ''iptables-multiport[name=HTTP, port="http,https"]'';
logpath = "${config.services.home-assistant.configDir}/*.log";
backend = "auto";
findtime = 600;
bantime = 600;
maxretry = 5;
};
};
};
})
(lib.mkIf config.host.impermanence.enable { (lib.mkIf config.host.impermanence.enable {
# assertions = [ assertions = [
# { {
# assertion = config.services.home-assistant.configDir == configDir; assertion = config.services.home-assistant.configDir == configDir;
# message = "home assistant config directory does not match persistence"; message = "home assistant config directory does not match persistence";
# } }
# ]; ];
# environment.persistence."/persist/system/root" = { environment.persistence."/persist/system/root" = {
# enable = true; enable = true;
# hideMounts = true; hideMounts = true;
# directories = [ directories = [
# { {
# directory = configDir; directory = configDir;
# user = "hass"; user = "hass";
# group = "hass"; group = "hass";
# } }
# ]; ];
# }; };
}) })
]); ]);
} }

View file

@ -52,10 +52,15 @@ in {
]; ];
networking.firewall.allowedTCPPorts = [jellyfinPort dlanPort]; networking.firewall.allowedTCPPorts = [jellyfinPort dlanPort];
systemd.tmpfiles.rules = [
"d ${config.services.jellyfin.media_directory} 2770 jellyfin jellyfin_media"
"A ${config.services.jellyfin.media_directory} - - - - u:jellyfin:rwX,g:jellyfin_media:rwX,o::-"
];
} }
(lib.mkIf config.services.fail2ban.enable { (lib.mkIf config.services.fail2ban.enable {
environment.etc = { environment.etc = {
"fail2ban/filter.d/jellyfin.local".text = lib.mkIf config.services.jellyfin.enable ( "fail2ban/filter.d/jellyfin.local".text = (
pkgs.lib.mkDefault (pkgs.lib.mkAfter '' pkgs.lib.mkDefault (pkgs.lib.mkAfter ''
[Definition] [Definition]
failregex = "^.*Authentication request for .* has been denied \\\(IP: \"<ADDR>\"\\\)\\\." failregex = "^.*Authentication request for .* has been denied \\\(IP: \"<ADDR>\"\\\)\\\."
@ -65,7 +70,7 @@ in {
services.fail2ban = { services.fail2ban = {
jails = { jails = {
jellyfin-iptables.settings = lib.mkIf config.services.jellyfin.enable { jellyfin-iptables.settings = {
enabled = true; enabled = true;
filter = "jellyfin"; filter = "jellyfin";
action = ''iptables-multiport[name=HTTP, port="http,https"]''; action = ''iptables-multiport[name=HTTP, port="http,https"]'';

View file

@ -0,0 +1,110 @@
{
config,
lib,
pkgs,
...
}: let
dataDir = "/var/lib/paperless";
in {
options.services.paperless = {
subdomain = lib.mkOption {
type = lib.types.str;
description = "subdomain of base domain that paperless will be hosted at";
default = "paperless";
};
database = {
user = lib.mkOption {
type = lib.types.str;
description = "what is the user and database that we are going to use for paperless";
default = "paperless";
};
};
};
config = lib.mkIf config.services.paperless.enable (lib.mkMerge [
{
host = {
reverse_proxy.subdomains.${config.services.paperless.subdomain} = {
target = "http://${config.services.paperless.address}:${toString config.services.paperless.port}";
websockets.enable = true;
forwardHeaders.enable = true;
extraConfig = ''
# allow large file uploads
client_max_body_size 50000M;
'';
};
postgres = {
enable = true;
extraUsers = {
${config.services.paperless.database.user} = {
isClient = true;
createUser = true;
};
};
extraDatabases = {
${config.services.paperless.database.user} = {
name = config.services.paperless.database.user;
};
};
};
};
services.paperless = {
configureTika = true;
settings = {
PAPERLESS_URL = "https://${config.services.paperless.subdomain}.${config.host.reverse_proxy.hostname}";
PAPERLESS_DBENGINE = "postgresql";
PAPERLESS_DBHOST = "/run/postgresql";
PAPERLESS_DBNAME = config.services.paperless.database.user;
PAPERLESS_DBUSER = config.services.paperless.database.user;
};
};
}
(lib.mkIf config.services.fail2ban.enable {
environment.etc = {
"fail2ban/filter.d/paperless.local".text = (
pkgs.lib.mkDefault (pkgs.lib.mkAfter ''
[Definition]
failregex = Login failed for user `.*` from (?:IP|private IP) `<HOST>`\.$
ignoreregex =
'')
);
};
services.fail2ban = {
jails = {
paperless.settings = {
enabled = true;
filter = "paperless";
action = ''iptables-multiport[name=HTTP, port="http,https"]'';
logpath = "${config.services.paperless.dataDir}/log/*.log";
backend = "auto";
findtime = 600;
bantime = 600;
maxretry = 5;
};
};
};
})
(lib.mkIf config.host.impermanence.enable {
assertions = [
{
assertion = config.services.paperless.dataDir == dataDir;
message = "paperless data location does not match persistence";
}
];
environment.persistence."/persist/system/root" = {
directories = [
{
directory = dataDir;
user = "paperless";
group = "paperless";
}
];
};
})
]);
}

View file

@ -4,7 +4,7 @@
... ...
}: { }: {
options.host.podman = { options.host.podman = {
enable = lib.mkEnableOption "should home-assistant be enabled on this computer"; enable = lib.mkEnableOption "should podman be enabled on this computer";
macvlan = { macvlan = {
subnet = lib.mkOption { subnet = lib.mkOption {
type = lib.types.str; type = lib.types.str;

View file

@ -1,115 +1,20 @@
{ {
lib, lib,
pkgs,
config, config,
... ...
}: let }: let
qbittorent_data_directory = "/var/lib/qbittorrent"; qbittorent_profile_directory = "/var/lib/qBittorrent/";
in { in {
options.services.qbittorrent = { options.services.qbittorrent = {
enable = lib.mkEnableOption "should the headless qbittorrent service be enabled";
dataDir = lib.mkOption {
type = lib.types.path;
default = "/var/lib/qbittorrent";
description = lib.mdDoc ''
The directory where qBittorrent stores its data files.
'';
};
mediaDir = lib.mkOption { mediaDir = lib.mkOption {
type = lib.types.path; type = lib.types.path;
description = lib.mdDoc '' description = lib.mdDoc ''
The directory to create to store qbittorrent media. The directory to create to store qbittorrent media.
''; '';
}; };
user = lib.mkOption {
type = lib.types.str;
default = "qbittorrent";
description = lib.mdDoc ''
User account under which qBittorrent runs.
'';
};
group = lib.mkOption {
type = lib.types.str;
default = "qbittorrent";
description = lib.mdDoc ''
Group under which qBittorrent runs.
'';
};
webPort = lib.mkOption {
type = lib.types.port;
default = 8080;
description = lib.mdDoc ''
qBittorrent web UI port.
'';
};
openFirewall = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Open services.qBittorrent.webPort to the outside network.";
};
package = lib.mkOption {
type = lib.types.package;
default = pkgs.qbittorrent-nox;
defaultText = lib.literalExpression "pkgs.qbittorrent-nox";
description = "The qbittorrent package to use.";
};
}; };
config = lib.mkIf config.services.qbittorrent.enable (lib.mkMerge [ config = lib.mkIf config.services.qbittorrent.enable (lib.mkMerge [
{
networking.firewall = lib.mkIf config.services.qbittorrent.openFirewall {
allowedTCPPorts = [config.services.qbittorrent.webPort];
};
systemd.services.qbittorrent = {
# based on the plex.nix service module and
# https://github.com/qbittorrent/qBittorrent/blob/master/dist/unix/systemd/qbittorrent-nox%40.service.in
description = "qBittorrent-nox service";
documentation = ["man:qbittorrent-nox(1)"];
after = ["network.target"];
wantedBy = ["multi-user.target"];
serviceConfig = {
Type = "simple";
User = config.services.qbittorrent.user;
Group = config.services.qbittorrent.group;
# Run the pre-start script with full permissions (the "!" prefix) so it
# can create the data directory if necessary.
ExecStartPre = let
preStartScript = pkgs.writeScript "qbittorrent-run-prestart" ''
#!${pkgs.bash}/bin/bash
# Create data directory if it doesn't exist
if ! test -d "$QBT_PROFILE"; then
echo "Creating initial qBittorrent data directory in: $QBT_PROFILE"
install -d -m 0755 -o "${config.services.qbittorrent.user}" -g "${config.services.qbittorrent.group}" "$QBT_PROFILE"
fi
'';
in "!${preStartScript}";
#ExecStart = "${pkgs.qbittorrent-nox}/bin/qbittorrent-nox";
ExecStart = "${config.services.qbittorrent.package}/bin/qbittorrent-nox";
# To prevent "Quit & shutdown daemon" from working; we want systemd to
# manage it!
#Restart = "on-success";
#UMask = "0002";
#LimitNOFILE = cfg.openFilesLimit;
};
environment = {
QBT_PROFILE = config.services.qbittorrent.dataDir;
QBT_WEBUI_PORT = toString config.services.qbittorrent.webPort;
};
};
}
(lib.mkIf config.host.impermanence.enable { (lib.mkIf config.host.impermanence.enable {
fileSystems."/persist/system/qbittorrent".neededForBoot = true; fileSystems."/persist/system/qbittorrent".neededForBoot = true;
@ -126,7 +31,7 @@ in {
assertions = [ assertions = [
{ {
assertion = config.services.qbittorrent.dataDir == qbittorent_data_directory; assertion = config.services.qbittorrent.profileDir == qbittorent_profile_directory;
message = "qbittorrent data directory does not match persistence"; message = "qbittorrent data directory does not match persistence";
} }
]; ];
@ -135,7 +40,7 @@ in {
"/persist/system/root" = { "/persist/system/root" = {
directories = [ directories = [
{ {
directory = qbittorent_data_directory; directory = qbittorent_profile_directory;
user = "qbittorrent"; user = "qbittorrent";
group = "qbittorrent"; group = "qbittorrent";
} }

View file

@ -1,155 +0,0 @@
{
config,
lib,
pkgs,
...
}: {
options.services.virt-home-assistant = {
enable = lib.mkEnableOption "Wether to enable home assistant virtual machine";
networkBridge = lib.mkOption {
type = lib.types.str;
description = "what network bridge should we attach to the image";
};
hostDevice = lib.mkOption {
type = lib.types.str;
description = "what host devices should be attached to the image";
};
initialVersion = lib.mkOption {
type = lib.types.str;
description = "what home assistant image version should we pull for initial instal";
default = "15.0";
};
imageName = lib.mkOption {
type = lib.types.str;
description = "where should the image be installed to";
default = "home-assistant.qcow2";
};
installLocation = lib.mkOption {
type = lib.types.str;
description = "where should the image be installed to";
default = "/etc/hass";
};
virtualMachineName = lib.mkOption {
type = lib.types.str;
description = "what name should we give the virtual machine";
default = "home-assistant";
};
subdomain = lib.mkOption {
type = lib.types.str;
description = "subdomain of base domain that home-assistant will be hosted at";
default = "home-assistant";
};
};
config = lib.mkIf config.services.virt-home-assistant.enable (lib.mkMerge [
{
# environment.systemPackages = with pkgs; [
# virt-manager
# ];
# TODO: move this to external module and just have an assertion here that its enabled
# enable virtualization on the system
virtualisation = {
libvirtd = {
enable = true;
qemu.ovmf.enable = true;
};
};
# TODO: deactivation script?
# create service to install and start the container
systemd.services.virt-install-home-assistant = let
# TODO: all of these need to be escaped to be used in commands reliably
bridgedNetwork = config.services.virt-home-assistant.networkBridge;
hostDevice = config.services.virt-home-assistant.hostDevice;
virtualMachineName = config.services.virt-home-assistant.virtualMachineName;
imageName = config.services.virt-home-assistant.imageName;
installLocation = config.services.virt-home-assistant.installLocation;
installImage = "${installLocation}/${imageName}";
initialVersion = config.services.virt-home-assistant.initialVersion;
home-assistant-qcow2 = pkgs.fetchurl {
name = "home-assistant.qcow2";
url = "https://github.com/home-assistant/operating-system/releases/download/${initialVersion}/haos_ova-${initialVersion}.qcow2.xz";
hash = "sha256-V1BEjvvLNbMMKJVyMCmipjQ/3owoJteeVxoF9LDHo1U=";
postFetch = ''
cp $out src.xz
rm -r $out
${pkgs.xz}/bin/unxz src.xz --stdout > $out/${imageName}
'';
};
# Write a script to install the Home Assistant OS qcow2 image
virtInstallScript = pkgs.writeShellScriptBin "virt-install-hass" ''
# Copy the initial image out of the package store to the install location if we don't have one yet
if [ ! -f ${installImage} ]; then
cp ${home-assistant-qcow2} ${installLocation}
fi
# Check if VM already exists, and other pre-conditions
if ! ${pkgs.libvirt}/bin/virsh list --all | grep -q ${virtualMachineName}; then
${pkgs.virt-manager}/bin/virt-install --name ${virtualMachineName} \
--description "Home Assistant OS" \
--os-variant=generic \
--boot uefi \
--ram=2048 \
--vcpus=2 \
--import \
--disk ${installImage},bus=sata \
--network bridge=${bridgedNetwork} \
--host-device ${hostDevice} \
--graphics none
${pkgs.libvirt}/bin/virsh autostart ${virtualMachineName}
fi
'';
in {
description = "Install and start Home Assistant";
wantedBy = ["multi-user.target"];
after = ["local-fs.target"];
requires = ["libvirtd.service"];
serviceConfig.Type = "oneshot";
serviceConfig = {
ExecStart = "${virtInstallScript}/bin/virt-install-hass";
};
};
# TODO: figure out what we need to proxy to the virtual image
# host = {
# reverse_proxy.subdomains.${config.services.virt-home-assistant.subdomain} = {
# target = "http://localhost:${toString config.services.home-assistant.config.http.server_port}";
# websockets.enable = true;
# forwardHeaders.enable = true;
# extraConfig = ''
# add_header Upgrade $http_upgrade;
# add_header Connection \"upgrade\";
# proxy_buffering off;
# proxy_read_timeout 90;
# '';
# };
# };
}
(lib.mkIf config.services.fail2ban.enable {
# TODO: figure out how to write a config for this, prob based on nginx proxy logs?
})
(lib.mkIf config.host.impermanence.enable {
# assertions = [
# {
# assertion = config.services.virt-home-assistant.installLocation == configDir;
# message = "home assistant install location does not match persistence";
# }
# ];
environment.persistence."/persist/system/root" = {
enable = true;
hideMounts = true;
directories = [
{
directory = config.services.virt-home-assistant.installLocation;
}
];
};
})
]);
}

View file

@ -0,0 +1,63 @@
{
lib,
config,
...
}: {
options.services.wyoming.enable = lib.mkEnableOption "should wyoming be enabled on this device";
config = lib.mkIf config.services.wyoming.enable (lib.mkMerge [
{
services.wyoming = {
# Text to speech
piper = {
servers = {
"en" = {
enable = true;
# see https://github.com/rhasspy/rhasspy3/blob/master/programs/tts/piper/script/download.py
voice = "en-us-amy-low";
uri = "tcp://0.0.0.0:10200";
speaker = 0;
};
};
};
# Speech to text
faster-whisper = {
servers = {
"en" = {
enable = true;
# see https://github.com/rhasspy/rhasspy3/blob/master/programs/asr/faster-whisper/script/download.py
model = "tiny-int8";
language = "en";
uri = "tcp://0.0.0.0:10300";
device = "cpu";
};
};
};
openwakeword = {
enable = true;
uri = "tcp://0.0.0.0:10400";
preloadModels = [
"ok_nabu"
];
# TODO: custom models
};
};
# needs access to /proc/cpuinfo
systemd.services."wyoming-faster-whisper-en".serviceConfig.ProcSubset = lib.mkForce "all";
}
(lib.mkIf config.host.impermanence.enable {
environment.persistence."/persist/system/root" = {
enable = true;
hideMounts = true;
directories = [
{
directory = "/var/lib/private/wyoming";
mode = "0700";
}
];
};
})
]);
}

View file

@ -11,6 +11,7 @@ in {
{ {
systemd = lib.mkIf config.services.syncthing.enable { systemd = lib.mkIf config.services.syncthing.enable {
tmpfiles.rules = [ tmpfiles.rules = [
"A ${mountDir} - - - - u:syncthing:rwX,g:syncthing:rwX,o::-"
"d ${mountDir} 2755 syncthing syncthing -" "d ${mountDir} 2755 syncthing syncthing -"
"d ${config.services.syncthing.dataDir} 775 syncthing syncthing -" "d ${config.services.syncthing.dataDir} 775 syncthing syncthing -"
"d ${config.services.syncthing.configDir} 755 syncthing syncthing -" "d ${config.services.syncthing.configDir} 755 syncthing syncthing -"

View file

@ -17,13 +17,14 @@
eve = 1002; eve = 1002;
jellyfin = 2000; jellyfin = 2000;
forgejo = 2002; forgejo = 2002;
adguardhome = 2003;
hass = 2004; hass = 2004;
syncthing = 2007; syncthing = 2007;
ollama = 2008; ollama = 2008;
git = 2009; git = 2009;
immich = 2010; immich = 2010;
qbittorrent = 2011; qbittorrent = 2011;
paperless = 2012;
actual = 2013;
}; };
gids = { gids = {
@ -33,13 +34,14 @@
jellyfin_media = 2001; jellyfin_media = 2001;
jellyfin = 2000; jellyfin = 2000;
forgejo = 2002; forgejo = 2002;
adguardhome = 2003;
hass = 2004; hass = 2004;
syncthing = 2007; syncthing = 2007;
ollama = 2008; ollama = 2008;
git = 2009; git = 2009;
immich = 2010; immich = 2010;
qbittorrent = 2011; qbittorrent = 2011;
paperless = 2012;
actual = 2013;
}; };
users = config.users.users; users = config.users.users;
@ -127,12 +129,6 @@ in {
group = config.users.users.forgejo.name; group = config.users.users.forgejo.name;
}; };
adguardhome = {
uid = lib.mkForce uids.adguardhome;
isSystemUser = true;
group = config.users.users.adguardhome.name;
};
hass = { hass = {
uid = lib.mkForce uids.hass; uid = lib.mkForce uids.hass;
isSystemUser = true; isSystemUser = true;
@ -166,9 +162,21 @@ in {
qbittorrent = { qbittorrent = {
uid = lib.mkForce uids.qbittorrent; uid = lib.mkForce uids.qbittorrent;
isNormalUser = true; isSystemUser = true;
group = config.users.users.qbittorrent.name; group = config.users.users.qbittorrent.name;
}; };
paperless = {
uid = lib.mkForce uids.paperless;
isSystemUser = true;
group = config.users.users.paperless.name;
};
actual = {
uid = lib.mkForce uids.actual;
isSystemUser = true;
group = config.users.users.actual.name;
};
}; };
groups = { groups = {
@ -219,14 +227,6 @@ in {
]; ];
}; };
adguardhome = {
gid = lib.mkForce gids.adguardhome;
members = [
users.adguardhome.name
# leyla
];
};
hass = { hass = {
gid = lib.mkForce gids.hass; gid = lib.mkForce gids.hass;
members = [ members = [
@ -273,6 +273,20 @@ in {
leyla leyla
]; ];
}; };
paperless = {
gid = lib.mkForce gids.paperless;
members = [
users.paperless.name
];
};
actual = {
gid = lib.mkForce gids.actual;
members = [
users.actual.name
];
};
}; };
}; };
} }

@ -1 +1 @@
Subproject commit 1c5c059c0c7b6ce691993262fe10a2b63e1c31ba Subproject commit f016767c13aa36dde91503f7a9f01bdd02468045

View file

@ -65,7 +65,7 @@ flake=${flake:-$target}
mode=${mode:-switch} mode=${mode:-switch}
user=${user:-$USER} user=${user:-$USER}
command="nixos-rebuild $mode --use-remote-sudo --ask-sudo-password --flake .#$flake" command="nixos-rebuild $mode --sudo --ask-sudo-password --flake .#$flake"
if [[ $host ]]; if [[ $host ]];
then then

View file

@ -10,7 +10,7 @@
nix-syncthing = inputs.nix-syncthing; nix-syncthing = inputs.nix-syncthing;
disko = inputs.disko; disko = inputs.disko;
impermanence = inputs.impermanence; impermanence = inputs.impermanence;
lix-module = inputs.lix-module; # lix-module = inputs.lix-module;
systems = [ systems = [
"aarch64-darwin" "aarch64-darwin"
@ -83,7 +83,7 @@ in {
impermanence.nixosModules.impermanence impermanence.nixosModules.impermanence
home-manager.nixosModules.home-manager home-manager.nixosModules.home-manager
disko.nixosModules.disko disko.nixosModules.disko
lix-module.nixosModules.default # lix-module.nixosModules.default
../modules/nixos-modules ../modules/nixos-modules
../configurations/nixos/${host} ../configurations/nixos/${host}
]; ];