commit f9a1f35dfe91ffe8aef1849b256a725cb959b185
Author: Chris <chris@echoz.io>
Date: Tue, 7 Oct 2025 04:30:57 +0200
feat: init
Diffstat:
48 files changed, 2841 insertions(+), 0 deletions(-)
diff --git a/.envrc b/.envrc
@@ -0,0 +1 @@
+use flake
diff --git a/.gitignore b/.gitignore
@@ -0,0 +1,3 @@
+.direnv/
+/result
+/tmp
diff --git a/flake.lock b/flake.lock
@@ -0,0 +1,307 @@
+{
+ "nodes": {
+ "disko": {
+ "inputs": {
+ "nixpkgs": [
+ "nixpkgs"
+ ]
+ },
+ "locked": {
+ "lastModified": 1746728054,
+ "narHash": "sha256-eDoSOhxGEm2PykZFa/x9QG5eTH0MJdiJ9aR00VAofXE=",
+ "owner": "nix-community",
+ "repo": "disko",
+ "rev": "ff442f5d1425feb86344c028298548024f21256d",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nix-community",
+ "ref": "v1.12.0",
+ "repo": "disko",
+ "type": "github"
+ }
+ },
+ "elephant": {
+ "inputs": {
+ "nixpkgs": [
+ "nixpkgs"
+ ],
+ "systems": "systems"
+ },
+ "locked": {
+ "lastModified": 1759390862,
+ "narHash": "sha256-j1OY4dXsaoE1Pmeo6tn88JNUJdSLnuTSGuNxmWAkYyo=",
+ "owner": "abenz1267",
+ "repo": "elephant",
+ "rev": "2e5c793d2e14dc44318f19149147134db9e0c332",
+ "type": "github"
+ },
+ "original": {
+ "owner": "abenz1267",
+ "repo": "elephant",
+ "type": "github"
+ }
+ },
+ "flake-parts": {
+ "inputs": {
+ "nixpkgs-lib": [
+ "nixpak",
+ "nixpkgs"
+ ]
+ },
+ "locked": {
+ "lastModified": 1759362264,
+ "narHash": "sha256-wfG0S7pltlYyZTM+qqlhJ7GMw2fTF4mLKCIVhLii/4M=",
+ "owner": "hercules-ci",
+ "repo": "flake-parts",
+ "rev": "758cf7296bee11f1706a574c77d072b8a7baa881",
+ "type": "github"
+ },
+ "original": {
+ "owner": "hercules-ci",
+ "repo": "flake-parts",
+ "type": "github"
+ }
+ },
+ "hercules-ci-effects": {
+ "inputs": {
+ "flake-parts": [
+ "nixpak",
+ "flake-parts"
+ ],
+ "nixpkgs": [
+ "nixpak",
+ "nixpkgs"
+ ]
+ },
+ "locked": {
+ "lastModified": 1758022363,
+ "narHash": "sha256-ENUhCRWgSX4ni751HieNuQoq06dJvApV/Nm89kh+/A0=",
+ "owner": "hercules-ci",
+ "repo": "hercules-ci-effects",
+ "rev": "1a3667d33e247ad35ca250698d63f49a5453d824",
+ "type": "github"
+ },
+ "original": {
+ "owner": "hercules-ci",
+ "repo": "hercules-ci-effects",
+ "type": "github"
+ }
+ },
+ "home-manager": {
+ "inputs": {
+ "nixpkgs": [
+ "nixpkgs"
+ ]
+ },
+ "locked": {
+ "lastModified": 1759337100,
+ "narHash": "sha256-CcT3QvZ74NGfM+lSOILcCEeU+SnqXRvl1XCRHenZ0Us=",
+ "owner": "nix-community",
+ "repo": "home-manager",
+ "rev": "004753ae6b04c4b18aa07192c1106800aaacf6c3",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nix-community",
+ "repo": "home-manager",
+ "type": "github"
+ }
+ },
+ "impermanence": {
+ "locked": {
+ "lastModified": 1737831083,
+ "narHash": "sha256-LJggUHbpyeDvNagTUrdhe/pRVp4pnS6wVKALS782gRI=",
+ "owner": "nix-community",
+ "repo": "impermanence",
+ "rev": "4b3e914cdf97a5b536a889e939fb2fd2b043a170",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nix-community",
+ "repo": "impermanence",
+ "type": "github"
+ }
+ },
+ "imsh-clients": {
+ "inputs": {
+ "nixpkgs": [
+ "nixpkgs"
+ ]
+ },
+ "locked": {
+ "lastModified": 1759497641,
+ "narHash": "sha256-VbzrwiqWhwGQLQiV6TOYiCGMF9+W0RRhHotLZWNiOU4=",
+ "owner": "echozio",
+ "repo": "imsh-clients",
+ "rev": "9fb726805a2f9ae34d80ccf8332a09343dbef709",
+ "type": "github"
+ },
+ "original": {
+ "owner": "echozio",
+ "repo": "imsh-clients",
+ "type": "github"
+ }
+ },
+ "nixpak": {
+ "inputs": {
+ "flake-parts": "flake-parts",
+ "hercules-ci-effects": "hercules-ci-effects",
+ "nixpkgs": [
+ "nixpkgs"
+ ]
+ },
+ "locked": {
+ "lastModified": 1759373157,
+ "narHash": "sha256-AdQmn5AASt6nUYxIAo+/+we312zqA0moB/Cuj7TsIC4=",
+ "owner": "nixpak",
+ "repo": "nixpak",
+ "rev": "7cd8f919d173deeb1f6d01cfda22a84eeedba1ae",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nixpak",
+ "repo": "nixpak",
+ "type": "github"
+ }
+ },
+ "nixpkgs": {
+ "locked": {
+ "lastModified": 1759036355,
+ "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=",
+ "owner": "NixOS",
+ "repo": "nixpkgs",
+ "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127",
+ "type": "github"
+ },
+ "original": {
+ "id": "nixpkgs",
+ "ref": "nixos-unstable",
+ "type": "indirect"
+ }
+ },
+ "nixpkgs_2": {
+ "locked": {
+ "lastModified": 1759036355,
+ "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=",
+ "owner": "NixOS",
+ "repo": "nixpkgs",
+ "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127",
+ "type": "github"
+ },
+ "original": {
+ "id": "nixpkgs",
+ "ref": "nixos-unstable",
+ "type": "indirect"
+ }
+ },
+ "root": {
+ "inputs": {
+ "disko": "disko",
+ "elephant": "elephant",
+ "home-manager": "home-manager",
+ "impermanence": "impermanence",
+ "imsh-clients": "imsh-clients",
+ "nixpak": "nixpak",
+ "nixpkgs": "nixpkgs",
+ "sec": "sec",
+ "sops-nix": "sops-nix",
+ "walker": "walker"
+ }
+ },
+ "sec": {
+ "inputs": {
+ "nixpkgs": "nixpkgs_2"
+ },
+ "locked": {
+ "lastModified": 1759804042,
+ "narHash": "sha256-dvi/FFa9RVS3peH820gXUgl1MIIB73zSYOTg+j/45eo=",
+ "owner": "echozio",
+ "repo": "sec",
+ "rev": "51df35202dc657f66323c0411b18680daa5b7ff1",
+ "type": "github"
+ },
+ "original": {
+ "owner": "echozio",
+ "repo": "sec",
+ "type": "github"
+ }
+ },
+ "sops-nix": {
+ "inputs": {
+ "nixpkgs": [
+ "nixpkgs"
+ ]
+ },
+ "locked": {
+ "lastModified": 1759188042,
+ "narHash": "sha256-f9QC2KKiNReZDG2yyKAtDZh0rSK2Xp1wkPzKbHeQVRU=",
+ "owner": "Mic92",
+ "repo": "sops-nix",
+ "rev": "9fcfabe085281dd793589bdc770a2e577a3caa5d",
+ "type": "github"
+ },
+ "original": {
+ "owner": "Mic92",
+ "repo": "sops-nix",
+ "type": "github"
+ }
+ },
+ "systems": {
+ "locked": {
+ "lastModified": 1689347949,
+ "narHash": "sha256-12tWmuL2zgBgZkdoB6qXZsgJEH9LR3oUgpaQq2RbI80=",
+ "owner": "nix-systems",
+ "repo": "default-linux",
+ "rev": "31732fcf5e8fea42e59c2488ad31a0e651500f68",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nix-systems",
+ "repo": "default-linux",
+ "type": "github"
+ }
+ },
+ "systems_2": {
+ "locked": {
+ "lastModified": 1689347949,
+ "narHash": "sha256-12tWmuL2zgBgZkdoB6qXZsgJEH9LR3oUgpaQq2RbI80=",
+ "owner": "nix-systems",
+ "repo": "default-linux",
+ "rev": "31732fcf5e8fea42e59c2488ad31a0e651500f68",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nix-systems",
+ "repo": "default-linux",
+ "type": "github"
+ }
+ },
+ "walker": {
+ "inputs": {
+ "elephant": [
+ "elephant"
+ ],
+ "nixpkgs": [
+ "nixpkgs"
+ ],
+ "systems": "systems_2"
+ },
+ "locked": {
+ "lastModified": 1759389183,
+ "narHash": "sha256-IltPEM2ljbLjEt3XYR7oWNf52JV9Z3M2WkGnz5ELceY=",
+ "owner": "abenz1267",
+ "repo": "walker",
+ "rev": "0709813b758757eaec6628a2ed4180c2f7885c7a",
+ "type": "github"
+ },
+ "original": {
+ "owner": "abenz1267",
+ "repo": "walker",
+ "type": "github"
+ }
+ }
+ },
+ "root": "root",
+ "version": 7
+}
diff --git a/flake.nix b/flake.nix
@@ -0,0 +1,126 @@
+{
+ inputs = {
+ nixpkgs.url = "nixpkgs/nixos-unstable";
+
+ disko.url = "github:nix-community/disko/v1.12.0";
+ home-manager.url = "github:nix-community/home-manager";
+ impermanence.url = "github:nix-community/impermanence";
+ sops-nix.url = "github:Mic92/sops-nix";
+ walker.url = "github:abenz1267/walker";
+ elephant.url = "github:abenz1267/elephant";
+ nixpak.url = "github:nixpak/nixpak";
+ imsh-clients.url = "github:echozio/imsh-clients";
+
+ disko.inputs.nixpkgs.follows = "nixpkgs";
+ home-manager.inputs.nixpkgs.follows = "nixpkgs";
+ sops-nix.inputs.nixpkgs.follows = "nixpkgs";
+ walker.inputs.nixpkgs.follows = "nixpkgs";
+ walker.inputs.elephant.follows = "elephant";
+ elephant.inputs.nixpkgs.follows = "nixpkgs";
+ nixpak.inputs.nixpkgs.follows = "nixpkgs";
+ imsh-clients.inputs.nixpkgs.follows = "nixpkgs";
+
+ sec.url = "github:echozio/sec";
+ };
+
+ outputs =
+ inputs@{
+ self,
+ disko,
+ nixpkgs,
+ ...
+ }:
+ let
+ lib = nixpkgs.lib;
+ systems = [
+ "x86_64-linux"
+ "aarch64-linux"
+ ];
+ argsFor = system: {
+ pkgs = nixpkgs.legacyPackages.${system};
+ disko = disko.packages.${system}.disko;
+ };
+ forAllSystems = f: lib.genAttrs systems (system: f (argsFor system));
+ in
+ {
+ devShells = forAllSystems (
+ { pkgs, disko, ... }:
+ {
+ default = pkgs.mkShell {
+ packages = with pkgs; [
+ nixos-rebuild
+ nixos-anywhere
+ ssh-to-age
+ disko
+ (writeShellScriptBin "generate-host-keys" ''
+ host="$1"
+ out="tmp/fix"
+ if [ -z "$host" ]; then
+ echo "Usage: $(basename "$0") hostname" >&2
+ exit 1
+ fi
+ umask 0022
+ mkdir -p "$out/etc/ssh"
+ ${openssh}/bin/ssh-keygen -t ed25519 -f "$out/etc/ssh/ssh_host_ed25519_key" -C "root@$host" -q -N ""
+ cat <<EOF
+ age public key:
+ $(${ssh-to-age}/bin/ssh-to-age < "$out/etc/ssh/ssh_host_ed25519_key.pub")
+
+ install with:
+ nixos-anywhere --flake .#$host --extra-files tmp --target-host root@$host
+
+ remember to:
+ rm -rf tmp
+ EOF
+
+ '')
+ ];
+ };
+ }
+ );
+ packages = forAllSystems (
+ { pkgs, ... }:
+ {
+ neovim-minimal = pkgs.wrapNeovimUnstable pkgs.neovim-unwrapped {
+ vimAlias = true;
+ viAlias = true;
+ luaRcContent = builtins.readFile ./neovim.lua;
+ withRuby = false;
+ };
+ }
+ );
+
+ nixosModules.default = ./modules;
+
+ nixosConfigurations =
+ let
+ mkHost =
+ module:
+ lib.nixosSystem {
+ specialArgs = inputs;
+ modules = [
+ self.nixosModules.default
+ module
+ ];
+ };
+ in
+ {
+ vm = mkHost ./hosts/vm;
+ rc = mkHost ./hosts/rc;
+ ws = mkHost ./hosts/ws;
+ };
+
+ formatter = forAllSystems (
+ { pkgs, ... }:
+ pkgs.treefmt.withConfig {
+ settings = {
+ on-unmatched = "info";
+ formatter.nixfmt = {
+ command = lib.getExe pkgs.nixfmt-rfc-style;
+ includes = [ "*.nix" ];
+ };
+ };
+ }
+ );
+ };
+}
diff --git a/hosts/rc/default.nix b/hosts/rc/default.nix
@@ -0,0 +1,31 @@
+{ modulesPath, sec, ... }:
+{
+ imports = [
+ sec.nixosModules.dot
+
+ (modulesPath + "/installer/scan/not-detected.nix")
+ ];
+
+ disko.devices.disk.system.device = "/dev/disk/by-id/wwn-0x5002538e410ec0c1";
+
+ networking = {
+ hostName = "rc";
+ hostId = "9cb48259";
+ };
+
+ boot = {
+ initrd.availableKernelModules = [
+ "xhci_pci"
+ "ehci_pci"
+ "ahci"
+ "sd_mod"
+ "sdhci_pci"
+ ];
+ kernelModules = [ "kvm-intel" ];
+ };
+
+ hardware = {
+ enableAllFirmware = true;
+ cpu.intel.updateMicrocode = true;
+ };
+}
diff --git a/hosts/vm/default.nix b/hosts/vm/default.nix
@@ -0,0 +1,22 @@
+{
+ modulesPath,
+ sec,
+ user,
+ ...
+}:
+{
+ imports = [
+ sec.nixosModules.dot
+
+ (modulesPath + "/profiles/qemu-guest.nix")
+ ];
+
+ disko.devices.disk.system.device = "/dev/vda";
+
+ networking = {
+ hostName = "vm";
+ hostId = "522ef8a2";
+ };
+
+ home-manager.users.${user}.wayland.windowManager.hyprland.settings."$mod" = "ALT";
+}
diff --git a/hosts/ws/default.nix b/hosts/ws/default.nix
@@ -0,0 +1,30 @@
+{ modulesPath, sec, ... }:
+{
+ imports = [
+ sec.nixosModules.dot
+ ./pipewire.nix
+
+ (modulesPath + "/installer/scan/not-detected.nix")
+ ];
+
+ disko.devices.disk.system.device = "/dev/disk/by-id/nvme-eui.00000000000000000026b7282f657265";
+
+ networking = {
+ hostName = "ws";
+ hostId = "e27df163";
+ };
+
+ boot = {
+ initrd.availableKernelModules = [
+ "nvme"
+ "thunderbolt"
+ "usbhid"
+ ];
+ kernelModules = [ "kvm-amd" ];
+ };
+
+ hardware = {
+ enableAllFirmware = true;
+ cpu.amd.updateMicrocode = true;
+ };
+}
diff --git a/hosts/ws/pipewire.nix b/hosts/ws/pipewire.nix
@@ -0,0 +1,115 @@
+{
+ lib,
+ ...
+}:
+{
+ services.pipewire.wireplumber.extraConfig = {
+ devices."monitor.alsa.rules" = [
+ {
+ matches = lib.singleton {
+ "alsa.card_name" = "Xonar STX";
+ "media.class" = "Audio/Sink";
+ };
+ actions.update-props = {
+ "node.description" = "Desktop";
+ "node.nick" = "Desktop";
+ };
+ }
+ {
+ matches = lib.singleton {
+ "alsa.card_name" = "Xonar STX";
+ "media.class" = "Audio/Source";
+ };
+ actions.update-props."node.disabled" = true;
+ }
+ {
+ matches = lib.singleton {
+ "alsa.card_name" = "RODE NT-USB";
+ "media.class" = "Audio/Source";
+ };
+ actions.update-props = {
+ "node.description" = "Voice";
+ "node.nick" = "Voice";
+ };
+ }
+ {
+ matches = lib.singleton {
+ "alsa.card_name" = "RODE NT-USB";
+ "media.class" = "Audio/Sink";
+ };
+ action.update-props."node.disabled" = true;
+ }
+ {
+ matches = lib.singleton {
+ "alsa.card_name" = "HDA ATI HDMI";
+ };
+ actions.update-props."node.disabled" = true;
+ }
+ {
+ matches = lib.singleton {
+ "alsa.card_name" = "HD 720P webcam";
+ };
+ actions.update-props."node.disabled" = true;
+ }
+ {
+ matches = lib.singleton {
+ "alsa.card_name" = "HD-Audio Generic";
+ };
+ actions.update-props."node.disabled" = true;
+ }
+ ];
+ loopback = {
+ "wireplumber.components" = lib.singleton {
+ name = "loopback.lua";
+ type = "script/lua";
+ provides = "custom.loopback";
+ };
+ "wireplumber.profiles".main."custom.loopback" = "required";
+ };
+ };
+ services.pipewire.wireplumber.extraScripts."loopback.lua" = ''
+ masterOutput = LocalModule("libpipewire-module-loopback", [[
+ audio.position = [ FL FR ]
+ capture.props = {
+ media.class = Audio/Sink
+ node.name = "capture.master"
+ node.description = "Master"
+ node.latency = 1024/44100
+ audio.rate = 44100
+ audio.channels = 2
+ audio.position = [ FL FR ]
+ }
+ playback.props = {
+ node.name = "playback.master"
+ node.description = "Master"
+ node.latency = 1024/44100
+ audio.rate = 44100
+ audio.channels = 2
+ audio.position = [ AUX0 AUX1 ]
+ target.object = "alsa_output.usb-DENON_DJ_DENON_DJ_MC7000_201603-00.pro-output-0"
+ }
+ ]])
+
+ headphoneOutput = LocalModule("libpipewire-module-loopback", [[
+ audio.position = [ FL FR ]
+ capture.props = {
+ media.class = Audio/Sink
+ node.name = "capture.headphones"
+ node.description = "Headphones"
+ node.latency = 1024/44100
+ audio.rate = 44100
+ audio.channels = 2
+ audio.position = [ FL FR ]
+ }
+ playback.props = {
+ node.name = "playback.headphones"
+ node.description = "Headphones"
+ node.latency = 1024/44100
+ audio.rate = 44100
+ audio.channels = 2
+ audio.position = [ AUX2 AUX3 ]
+ target.object = "alsa_output.usb-DENON_DJ_DENON_DJ_MC7000_201603-00.pro-output-0"
+ }
+ ]])
+ '';
+}
diff --git a/modules/aerc/binds.nix b/modules/aerc/binds.nix
@@ -0,0 +1,192 @@
+{
+ lib,
+ config,
+ pkgs,
+
+ user,
+ ...
+}:
+{
+ home-manager.users.${user}.programs.aerc.extraBinds = lib.mkMerge [
+ {
+ global = {
+ "<C-p>" = ":prev-tab<Enter>";
+ "<C-n>" = ":next-tab<Enter>";
+ "?" = ":help keys<Enter>";
+ "<C-t>" = ":term<Enter>";
+ };
+
+ # Actions
+ messages = {
+ d = ":read<Enter>,:move Trash<Enter>";
+ a = ":archive flat<Enter>";
+ f = ":move Important<Enter>";
+
+ m = ":compose<Enter>";
+ b = ":bounce<space>";
+ rr = ":reply -a<Enter>";
+ rq = ":reply -aq<Enter>";
+ Rr = ":reply<Enter>";
+ Rq = ":reply -q<Enter>";
+ F = ":forward<Enter>";
+
+ v = ":mark -t<Enter>";
+ V = ":mark -v<Enter>";
+ };
+
+ # Navigation
+ messages = {
+ "<Enter>" = ":view<Enter>";
+
+ j = ":next<Enter>";
+ k = ":prev<Enter>";
+
+ "<C-d>" = ":next 50%<Enter>";
+ "<C-u>" = ":prev 50%<Enter>";
+
+ "<C-f>" = ":next 100%<Enter>";
+ "<C-b>" = ":prev 100%<Enter>";
+
+ G = ":select -1<Enter>";
+ gg = ":select 0<Enter>";
+
+ J = ":next-folder<Enter>";
+ K = ":prev-folder<Enter>";
+ H = ":collapse-folder<Enter>";
+ L = ":expand-folder<Enter>";
+
+ zz = ":align center<Enter>";
+ zt = ":align top<Enter>";
+ zb = ":align bottom<Enter>";
+ };
+
+ # Search
+ messages = {
+ "/" = ":search<space>";
+ "\\" = ":filter<space>";
+ n = ":next-result<Enter>";
+ N = ":prev-result<Enter>";
+
+ "<Esc>" = ":clear<Enter>:unmark -a<Enter>";
+ };
+
+ messages = {
+ T = ":toggle-threads<Enter>";
+ zc = ":fold<Enter>";
+ zo = ":unfold<Enter>";
+ za = ":fold -t<Enter>";
+ zM = ":fold -a<Enter>";
+ zR = ":unfold -a<Enter>";
+
+ c = ":cf<space>";
+
+ s = ":split<Enter>";
+ S = ":vsplit<Enter>";
+
+ "|" = ":pipe<space>";
+ pl = ":patch list<Enter>";
+ pa = ":patch apply <Tab>";
+ pd = ":patch drop <Tab>";
+ pb = ":patch rebase <Tab>";
+ pt = ":patch term<Enter>";
+ ps = ":patch switch <Tab>";
+ };
+
+ "messages:folder=Drafts" = {
+ "<Enter>" = ":recall<Enter>";
+ };
+
+ view = {
+ "/" = ":toggle-key-passthrough<Enter>/";
+ q = ":close<Enter>";
+ O = ":open<Enter>";
+ o = ":open<Enter>";
+ S = ":save<space>";
+ "|" = ":pipe<space>";
+ D = ":delete<Enter>";
+ A = ":archive flat<Enter>";
+
+ "<C-y>" = ":copy-link <space>";
+ "<C-l>" = ":open-link <space>";
+
+ r = ":reply -a<Enter>";
+ rq = ":reply -aq<Enter>";
+ Rr = ":reply<Enter>";
+ Rq = ":reply -q<Enter>";
+ F = ":forward<Enter>";
+
+ H = ":toggle-headers<Enter>";
+ "<C-k>" = ":prev-part<Enter>";
+ "<C-j>" = ":next-part<Enter>";
+ J = ":next<Enter>";
+ K = ":prev<Enter>";
+ };
+
+ "view::passthrough" = {
+ "$noinherit" = true;
+ "$ex" = "<C-x>";
+ "<Esc>" = ":toggle-key-passthrough<Enter>";
+ };
+
+ compose = {
+ "$noinherit" = true;
+ "$ex" = "<C-x>";
+ "$complete" = "<C-o>";
+
+ "<C-k>" = ":prev-field<Enter>";
+ "<C-j>" = ":next-field<Enter>";
+ "<A-p>" = ":switch-account -p<Enter>";
+ "<A-n>" = ":switch-account -n<Enter>";
+ "<C-p>" = ":prev-tab<Enter>";
+ "<C-n>" = ":next-tab<Enter>";
+ };
+
+ "compose::editor" = {
+ "$noinherit" = true;
+ "$ex" = "<C-x>";
+ "<C-k>" = ":prev-field<Enter>";
+ "<C-j>" = ":next-field<Enter>";
+ "<C-p>" = ":prev-tab<Enter>";
+ "<C-n>" = ":next-tab<Enter>";
+ };
+
+ "compose::review" = {
+ y = ":send<Enter>";
+ s = ":sign<Enter>";
+ x = ":encrypt<Enter>";
+ v = ":preview<Enter>";
+ p = ":postpone<Enter>";
+ q = ":choose -o d discard abort -o p postpone postpone<Enter>";
+ Q = ":abort<Enter>";
+ e = ":edit<Enter>";
+ a = ":attach<space>";
+ d = ":detach<space>";
+ };
+
+ terminal = {
+ "$noinherit" = true;
+ "$ex" = "<C-x>";
+
+ "<C-p>" = ":prev-tab<Enter>";
+ "<C-n>" = ":next-tab<Enter>";
+ };
+ }
+
+ (lib.mapAttrs' (
+ name: account:
+ let
+ passwordCmd = lib.concatStringsSep " " account.passwordCommand;
+ in
+ lib.nameValuePair "messages:account=${name}" {
+ "gs" = ":term ${pkgs.writeShellScript "sieve-edit" ''
+ printf "\033]0;%s\a" "Sieve: ${name}"
+ "${lib.getExe pkgs.sieve-connect}" \
+ --user "${account.userName}" \
+ --server "${account.imap.host}" \
+ --passwordfd 5 \
+ 5<<<"$(${passwordCmd})"
+ ''}<Enter>";
+ }
+ ) config.home-manager.users.${user}.accounts.email.accounts)
+ ];
+}
diff --git a/modules/aerc/default.nix b/modules/aerc/default.nix
@@ -0,0 +1,53 @@
+{
+ lib,
+ config,
+ pkgs,
+
+ user,
+ ...
+}:
+{
+ imports = [
+ ./filters.nix
+ ./binds.nix
+ ];
+
+ config = {
+ environment.persistence."/fix".users.${user}.directories = [
+ {
+ directory = ".cache/aerc";
+ mode = "0700";
+ }
+ ];
+
+ home-manager.users.${user} = {
+ options.accounts.email.accounts = lib.mkOption {
+ type = lib.types.attrsOf (
+ lib.types.submodule {
+ config.aerc.enable = true;
+ }
+ );
+ };
+
+ config = {
+ programs.aerc = {
+ enable = true;
+ extraConfig = {
+ general.unsafe-accounts-conf = true;
+
+ ui = {
+ mouse-enabled = true;
+ dirlist-tree = true;
+ fuzzy-complete = true;
+ };
+
+ openers = {
+ "text/html" = "firefox";
+ "application/pdf" = "firefox";
+ };
+ };
+ };
+ };
+ };
+ };
+}
diff --git a/modules/aerc/filters.nix b/modules/aerc/filters.nix
@@ -0,0 +1,56 @@
+{
+ lib,
+ pkgs,
+
+ user,
+
+ mkNixPak,
+ ...
+}:
+{
+ home-manager.users.${user}.programs.aerc.extraConfig.filters = {
+ "text/plain" = "colorize";
+ "text/calendar" = "calendar";
+ "message/rfc822" = "colorize";
+ "message/delivery-status" = "colorize";
+ ".headers" = "colorize";
+
+ "text/html" = builtins.toString (
+ lib.getExe
+ (mkNixPak {
+ config.app.package = pkgs.writeShellScriptBin "w3m-html-filter" ''
+ export LC_ALL=C.UTF-8
+ exec ${lib.getExe pkgs.w3m} -dump -T text/html -no-cookie -config /dev/null \
+ | ${lib.getExe pkgs.perl} -CS -ne 's/[\p{M}\p{Cf}]//g; s/[ \t\p{Zs}]+$//; print unless /^[ \t\p{Zs}]*$/ && $p; $p=/^[ \t\p{Zs}]*$/;'
+ '';
+ config.dbus.enable = false;
+ config.bubblewrap.network = false;
+ config.bubblewrap.bindEntireStore = false;
+ }).config.script
+ );
+
+ "application/pdf" = builtins.toString (
+ lib.getExe
+ (mkNixPak {
+ config.app.package = pkgs.writeShellScriptBin "w3m-pdf-filter" ''
+ exec ${pkgs.poppler-utils}/bin/pdftotext -layout - -
+ '';
+ config.dbus.enable = false;
+ config.bubblewrap.network = false;
+ config.bubblewrap.bindEntireStore = false;
+ }).config.script
+ );
+
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document" = builtins.toString (
+ lib.getExe
+ (mkNixPak {
+ config.app.package = pkgs.writeShellScriptBin "w3m-pdf-filter" ''
+ exec ${lib.getExe pkgs.pandoc} -f docx -t plain
+ '';
+ config.dbus.enable = false;
+ config.bubblewrap.network = false;
+ config.bubblewrap.bindEntireStore = false;
+ }).config.script
+ );
+ };
+}
diff --git a/modules/boot/default.nix b/modules/boot/default.nix
@@ -0,0 +1,15 @@
+{ config, ... }:
+{
+ config = {
+ boot = {
+ initrd.systemd.enable = true;
+
+ zfs.devNodes = builtins.dirOf config.disko.devices.disk.system.device;
+
+ loader = {
+ systemd-boot.enable = true;
+ efi.canTouchEfiVariables = true;
+ };
+ };
+ };
+}
diff --git a/modules/dark-mode/default.nix b/modules/dark-mode/default.nix
@@ -0,0 +1,32 @@
+{
+ pkgs,
+
+ user,
+ ...
+}:
+{
+ programs.dconf.enable = true;
+ home-manager.users.${user} = {
+ dconf.settings = {
+ "org/gnome/desktop/interface".color-scheme = "prefer-dark";
+ };
+
+ gtk = {
+ enable = true;
+
+ gtk3.extraConfig.gtk-application-prefer-dark-theme = true;
+
+ theme = {
+ name = "Adwaita:Dark";
+ package = pkgs.gnome-themes-extra;
+ };
+ };
+
+ qt = {
+ enable = true;
+
+ platformTheme.name = "adwaita";
+ style.name = "adwaita-dark";
+ };
+ };
+}
diff --git a/modules/default.nix b/modules/default.nix
@@ -0,0 +1,31 @@
+{
+ lib,
+ config,
+ ...
+}:
+{
+ imports = lib.mapAttrsToList (path: _: ./. + "/${path}") (
+ lib.filterAttrs (path: type: type == "directory") (builtins.readDir ./.)
+ );
+
+ system.stateVersion = lib.mkDefault "25.05";
+
+ nixpkgs = {
+ hostPlatform = lib.mkDefault "x86_64-linux";
+ config.allowUnfree = true;
+ };
+
+ nix.settings = {
+ experimental-features = [
+ "nix-command"
+ "flakes"
+ ];
+ trusted-users = [ "@wheel" ];
+ };
+
+ users.mutableUsers = false;
+
+ security.sudo.extraConfig = ''
+ Defaults lecture = never
+ '';
+}
diff --git a/modules/disko/default.nix b/modules/disko/default.nix
@@ -0,0 +1,73 @@
+{ config, disko, ... }:
+{
+ imports = [ disko.nixosModules.disko ];
+
+ disko.devices = {
+ disk.system = {
+ type = "disk";
+
+ content = {
+ type = "gpt";
+ partitions = {
+ boot = {
+ size = "1G";
+ type = "EF00";
+ content = {
+ type = "filesystem";
+ format = "vfat";
+ mountpoint = "/boot";
+ mountOptions = [ "nofail" ];
+ };
+ };
+
+ zfs = {
+ size = "100%";
+ content = {
+ type = "zfs";
+ pool = "system";
+ };
+ };
+ };
+ };
+ };
+
+ zpool.system = {
+ type = "zpool";
+ rootFsOptions = {
+ mountpoint = "none";
+ compression = "zstd";
+ acltype = "posixacl";
+ xattr = "sa";
+ encryption = "aes-256-gcm";
+ keyformat = "passphrase";
+ keylocation = "prompt";
+ };
+ options.ashift = "12";
+
+ datasets = {
+ root = {
+ type = "zfs_fs";
+ options.mountpoint = "none";
+ };
+
+ "root/current" = {
+ type = "zfs_fs";
+ options.mountpoint = "legacy";
+ mountpoint = "/";
+ };
+
+ nix = {
+ type = "zfs_fs";
+ options.mountpoint = "legacy";
+ mountpoint = "/nix";
+ };
+
+ fix = {
+ type = "zfs_fs";
+ options.mountpoint = "legacy";
+ mountpoint = "/fix";
+ };
+ };
+ };
+ };
+}
diff --git a/modules/ephemeral-root/default.nix b/modules/ephemeral-root/default.nix
@@ -0,0 +1,30 @@
+{
+ lib,
+ config,
+ pkgs,
+ ...
+}:
+{
+ config = {
+ boot.initrd.systemd.services.zfs-rotate-root = {
+ wantedBy = [ "initrd.target" ];
+ after = [ "zfs-import-system.service" ];
+ before = [ "sysroot.mount" ];
+ path = with pkgs; [
+ zfs
+ coreutils
+ ];
+ description = "rotate root dataset";
+ unitConfig.DefaultDependencies = "no";
+ serviceConfig.Type = "oneshot";
+ script = ''
+ zfs rename "system/root/current" "system/root/$(date -ud "@$(zfs get -Hpo value creation system/root/current)" +%Y-%m-%dT%H-%M-%SZ)"
+ zfs create -o mountpoint=legacy "system/root/current"
+ '';
+ };
+
+ environment.systemPackages = [
+ (pkgs.writeShellScriptBin "prune-roots" (builtins.readFile ./prune-roots.sh))
+ ];
+ };
+}
diff --git a/modules/ephemeral-root/prune-roots.sh b/modules/ephemeral-root/prune-roots.sh
@@ -0,0 +1,174 @@
+argv0="prune-roots"
+
+help() {
+cat <<EOF
+$argv0 - Prune non-current root filesystems.
+
+Usage: $argv0 [options...]
+
+Options:
+ -k, --keep=n
+ Keep the latest n roots.
+
+ -b, --before=date
+ Prune roots from before date.
+
+ -y, --yes
+ Delete filesystems without confirmation.
+
+ -n, --dry-run
+ List filesystems that would be deleted.
+
+ -l, --list
+ List all non-current filesystems along with their creation times.
+
+ -h, --help
+ Display this message.
+
+If both --keep and --before are specified, a filesystem will need to
+match both filters to be selected for deletion.
+EOF
+}
+
+list() {
+ local filesystems=("$@")
+ (
+ printf "AGE NAME CREATION\n"
+ for i in "${!filesystems[@]}"; do
+ age="$((${#filesystems[@]}-$i))"
+ fs="${filesystems[$i]}"
+ creation="$(date -d "@$(zfs get -Hpo value creation "$fs")")"
+ printf "%d %s %s\n" "$age" "$fs" "$creation"
+ done
+ ) | column -tl3
+}
+
+keep=""
+before=""
+confirmation=""
+dry=""
+
+readarray -t filesystems < <(\
+ zfs list -t filesystem -Ho name -s creation \
+ | awk '/^system\/root\// && !/^system\/root\/current$/' \
+)
+
+while [ $# -gt 0 ]; do
+ case "$1" in
+ -k*|--keep|--keep=*)
+ keep="${1/#-k/}"
+ if [ -z "$keep" ] || [ "$keep" = "$1" ]; then
+ keep="${1#*=}"
+ fi
+ if [ -z "$keep" ] || [ "$keep" = "$1" ]; then
+ if [ $# -lt 2 ]; then
+ printf "Error: Flag needs an argument: %s\n" "$1" >&2
+ exit 1
+ fi
+ keep="$2"
+ shift
+ fi
+ shift
+ if [ ! "$keep" -ge 0 ]; then
+ printf "Error: Invalid value for --keep: Must be an unsigned integer.\n" >&2
+ exit 1
+ fi
+ ;;
+ -b*|--before*)
+ before="${1/#-b/}"
+ if [ -z "$before" ] || [ "$before" = "$1" ]; then
+ before="${1#*=}"
+ fi
+ if [ -z "$before" ] || [ "$before" = "$1" ]; then
+ if [ $# -lt 2 ]; then
+ printf "Error: Flag needs an argument: %s\n" "$1" >&2
+ exit 1
+ fi
+ before="$2"
+ shift
+ fi
+ shift
+ if date -d "$before" 2>/dev/null >/dev/null; then
+ before="$(date -d "$before" +%s)"
+ else
+ error="$(date -d "$before" 2>&1)"
+ error="${error#date: }"
+ printf "Error: Invalid value for --before: %s\n" "${error^}" >&2
+ exit 1
+ fi
+ ;;
+ -h*|--help*)
+ help
+ exit 0
+ ;;
+ -y|--yes)
+ confirmation="Yes"
+ shift
+ ;;
+ -n|--dry-run)
+ dry=1
+ shift
+ ;;
+ -l|--list)
+ list "${filesystems[@]}"
+ exit 0
+ ;;
+ *)
+ printf "Error: Unknown argument: %s\n" "$1" >&2
+ exit 1
+ esac
+done
+
+delete=()
+for i in "${!filesystems[@]}"; do
+ fs="${filesystems[$i]}"
+ creation="$(zfs get -Hpo value creation "$fs")"
+ if [ ! -z "$before" ] && [ "$creation" -ge "$before" ]; then
+ continue
+ fi
+ if [ ! -z "$keep" ] && [ $((${#filesystems[@]}-$i)) -le "$keep" ]; then
+ continue
+ fi
+ delete+=($fs)
+done
+
+if [ ${#delete[@]} -le 0 ]; then
+ printf "Nothing to do: No matching filesystems.\n" >&2
+ exit 0
+fi
+
+list "${delete[@]}"
+
+if [ ! -z "$dry" ]; then
+ exit 0
+fi
+
+if [ "$confirmation" != "Yes" ]; then
+ printf "\n"
+ read -r -p "Type 'Yes' to confirm deletion of ${#delete[@]} volumes: " confirmation >&2
+ if [ "$confirmation" != "Yes" ]; then
+ printf "Abort.\n" >&2
+ exit 1
+ fi
+fi
+
+for i in "${!delete[@]}"; do
+ fs="${filesystems[$i]}"
+ elevate=""
+ if [ $UID -ne 0 ]; then
+ elevate="sudo"
+ error=$($elevate true 2>&1)
+ if [ $? -ne 0 ]; then
+ error="${error#sudo: }"
+ printf "Error: Could not elevate privileges: %s\n" "${error^}" >&2
+ exit 1
+ fi
+ fi
+ printf "Deleting filesystem '%s'...\n" "$fs" >&2
+ error="$($elevate zfs destroy "$fs" 2>&1)"
+ if [ $? -ne 0 ]; then
+ printf "Error: Could not delete filesystem: %s\n" "${error^}" >&2
+ exit 1
+ fi
+done
+
diff --git a/modules/firefox/default.nix b/modules/firefox/default.nix
@@ -0,0 +1,47 @@
+{
+ lib,
+
+ user,
+ ...
+}:
+{
+ imports = [
+ ./policies.nix
+ ];
+
+ environment.persistence."/fix".users.${user}.directories = [
+ {
+ directory = ".mozilla";
+ mode = "0700";
+ }
+ ];
+
+ home-manager.users.${user} = {
+ wayland.windowManager.hyprland.settings.bind = [ "$mod, W, exec, uwsm app -- firefox" ];
+
+ programs.firefox = {
+ enable = true;
+
+ profiles.default = {
+ id = 0;
+ isDefault = true;
+ containersForce = true;
+ containers = {
+ personal = {
+ id = 0;
+ color = "green";
+ icon = "fingerprint";
+ };
+ work = {
+ id = 1;
+ color = "blue";
+ icon = "briefcase";
+ };
+ };
+
+ extraConfig = builtins.readFile ./user.js;
+ userChrome = builtins.readFile ./userChrome.css;
+ };
+ };
+ };
+}
diff --git a/modules/firefox/policies.nix b/modules/firefox/policies.nix
@@ -0,0 +1,273 @@
+{
+ name,
+ lib,
+
+ user,
+ ...
+}:
+let
+ installExtension = id: attrs: {
+ ${id} = {
+ installation_mode = "normal_installed";
+ install_url = "https://addons.mozilla.org/firefox/downloads/latest/${id}/latest.xpi";
+ }
+ // attrs;
+ };
+in
+{
+ # Read: https://mozilla.github.io/policy-templates/
+
+ home-manager.users.${user}.programs.firefox.policies = {
+ ExtensionUpdate = true;
+ ExtensionSettings = lib.mkMerge [
+ (installExtension "{446900e4-71c2-419f-a6a7-df9c091e268b}" {
+ # Bitwarden
+ private_browsing = true;
+ default_area = "navbar";
+ })
+ (installExtension "uBlock0@raymondhill.net" {
+ private_browsing = true;
+ default_area = "navbar";
+ })
+ (installExtension "addon@darkreader.org" {
+ private_browsing = true;
+ default_area = "navbar";
+ })
+ (installExtension "firefox@tampermonkey.net" {
+ private_browsing = true;
+ default_area = "navbar";
+ })
+ (installExtension "soundfixer@unrelenting.technology" {
+ private_browsing = true;
+ default_area = "navbar";
+ })
+ (installExtension "{d7742d87-e61d-4b78-b8a1-b469842139fa}" {
+ # Vimium
+ private_browsing = true;
+ default_area = "menupanel";
+ })
+ (installExtension "{0c3ab5c8-57ac-4ad8-9dd1-ee331517884d}" {
+ # Proxy Toggle
+ private_browsing = true;
+ default_area = "menupanel";
+ })
+
+ # YouTube repairs
+ (installExtension "sponsorBlocker@ajay.app" {
+ private_browsing = true;
+ default_area = "menupanel";
+ })
+ (installExtension "{0d7cafdd-501c-49ca-8ebb-e3341caaa55e}" {
+ # YouTube NonStop
+ private_browsing = true;
+ default_area = "menupanel";
+ })
+ (installExtension "{7b1bf0b6-a1b9-42b0-b75d-252036438bdc}" {
+ # YouTube High Definition
+ private_browsing = true;
+ default_area = "menupanel";
+ })
+ (installExtension "{762f9885-5a13-4abd-9c77-433dcd38b8fd}" {
+ # Return YouTube Dislike
+ private_browsing = true;
+ default_area = "menupanel";
+ })
+ ];
+
+ AutofillAddressEnabled = false;
+ AutofillCreditCardEnabled = false;
+ Cookies = {
+ Behavior = "reject-tracker-and-partition-foreign";
+ BehaviorPrivateBrowsing = "reject-tracker-and-partition-foreign";
+ };
+ DefaultDownloadDirectory = "\${home}/dls";
+ DownloadDirectory = "\${home}/dls";
+ DisableFeedbackCommands = true;
+ DisableFirefoxAccounts = true;
+ DisableFirefoxScreenshots = true;
+ DisableFirefoxStudies = true;
+ DisableFormHistory = true;
+ DisableMasterPasswordCreation = true;
+ DisablePasswordReveal = true;
+ DisablePocket = true;
+ DisableProfileImport = true;
+ DisableProfileRefresh = true;
+ DisableSetDesktopBackground = true;
+ DisableSystemAddonUpdate = true;
+ DisableTelemetry = true;
+ DisplayBookmarksToolbar = "never";
+ DisplayMenuBar = "never";
+ DNSOverHTTPS.Enabled = false;
+ DontCheckDefaultBrowser = true;
+ EnableTrackingProtection = {
+ Value = true;
+ Cryptomining = true;
+ Fingerprinting = true;
+ EmailTracking = true;
+ SuspectedFingerprinting = true;
+ };
+ EncryptedMediaExtensions = {
+ Enabled = false;
+ Locked = true;
+ };
+ ExemptDomainFileTypePairsFromFileTypeDownloadWarnings = [
+ {
+ file_extension = "*";
+ domains = [ "*" ];
+ }
+ ];
+ FirefoxHome = {
+ Search = false;
+ TopSites = false;
+ SponsoredTopSites = false;
+ Highlights = false;
+ Pocket = false;
+ Stories = false;
+ SponsoredPocket = false;
+ SponsoredStories = false;
+ Snippets = false;
+ Locked = true;
+ };
+ FirefoxSuggest = {
+ WebSuggestions = false;
+ SponsoredSuggestions = false;
+ ImproveSuggest = false;
+ Locked = true;
+ };
+ GoToIntranetSiteForSingleWordEntryInAddressBar = false;
+ HardwareAcceleration = true;
+ HttpsOnlyMode = "disabled";
+ LegacyProfiles = false;
+ LegacySameSiteCookieBehaviorEnabled = false;
+ ManualAppUpdateOnly = true;
+ NetworkPrediction = false;
+ NewTabPage = false;
+ NoDefaultBookmarks = true;
+ OfferToSaveLogins = false;
+ OverrideFirstRunPage = "";
+ OverridePostUpdatePage = "";
+ PasswordManagerEnabled = false;
+ Preferences = {
+ "accessibility.browsewithcaret" = {
+ Value = false;
+ Status = "locked";
+ };
+ "accessibility.browsewithcaret_shortcut.enabled" = {
+ Value = false;
+ Status = "locked";
+ };
+ "browser.aboutConfig.showWarning" = {
+ Value = false;
+ Status = "locked";
+ };
+ "browser.compactmode.show" = {
+ Value = true;
+ Status = "locked";
+ };
+ "browser.link.open_newwindow.restriction" = {
+ Value = 0;
+ Status = "locked";
+ Type = "number";
+ };
+ "browser.ml.enable" = {
+ Value = false;
+ Status = "locked";
+ };
+ "browser.search.region" = {
+ Value = "US";
+ Status = "locked";
+ };
+ "browser.uidensity" = {
+ Value = 1;
+ Status = "locked";
+ Type = "number";
+ };
+ "dom.block_download_insecure" = {
+ Value = false;
+ Status = "locked";
+ };
+ "extensions.ui.dictionary.hidden" = {
+ Value = true;
+ Status = "locked";
+ };
+ "network.IDN_show_punycode" = {
+ Value = true;
+ Status = "locked";
+ };
+ "print.more-settings.open" = {
+ Value = true;
+ Status = "locked";
+ };
+ "print.print_bgcolor" = {
+ Value = false;
+ Status = "locked";
+ };
+ "print.print_bgimages" = {
+ Value = false;
+ Status = "locked";
+ };
+ "ui.context_menus.after_mouseup" = {
+ Value = true;
+ Status = "locked";
+ };
+ "ui.key.menuAccessKeyFocuses" = {
+ Value = false;
+ Status = "locked";
+ };
+ "browser.theme.content-theme" = {
+ Value = 2;
+ Status = "locked";
+ };
+ "browser.theme.dark-private-windows" = {
+ Value = true;
+ Status = "locked";
+ };
+ "browser.theme.dark-toolbar-theme" = {
+ Value = true;
+ Status = "locked";
+ };
+ "browser.theme.native-theme" = {
+ Value = true;
+ Status = "locked";
+ };
+ "browser.theme.toolbar-theme" = {
+ Value = 0;
+ Status = "locked";
+ Type = "number";
+ };
+ "browser.newtabpage.activity-stream.system.showWeather" = {
+ Value = false;
+ Status = "locked";
+ };
+ "browser.newtabpage.activity-stream.telemetry" = {
+ Value = false;
+ Status = "locked";
+ };
+ };
+ PrimaryPassword = false;
+ PopupBlocking.Default = false;
+ RequestedLocales = [ "en-US" ];
+ SanitizeOnShutdown = {
+ Cache = true;
+ FormData = true;
+ };
+ SearchBar = "unified";
+ SearchEngines = {
+ PreventInstalls = true;
+ };
+ SearchSuggestEnabled = false;
+ ShowHomeButton = false;
+ SkipTermsOfUse = true;
+ StartDownloadsInTempDirectory = true;
+ UserMessaging = {
+ WhatsNew = false;
+ ExtensionRecommendations = false;
+ FeatureRecommendations = false;
+ UrlbarInterventions = false;
+ SkipOnboarding = true;
+ MoreFromMozilla = false;
+ FirefoxLabs = false;
+ Locked = true;
+ };
+ };
+}
diff --git a/modules/firefox/user.js b/modules/firefox/user.js
@@ -0,0 +1,24 @@
+// Restore previous session
+user_pref("browser.startup.page", 3);
+
+// Middle mouse scroll
+user_pref("general.autoScroll", true);
+
+// Don't navigate history with alt+scroll
+user_pref("mousewheel.with_alt.action", 1);
+
+// Enable browser toolbox
+user_pref("devtools.chrome.enabled", true);
+user_pref("devtools.debugger.remote-enabled", true);
+
+user_pref("browser.sessionstore.resume_from_crash", true);
+user_pref("general.aboutConfig.enable", true);
+user_pref("middlemouse.paste", false);
+user_pref("sidebar.animation.enabled", false);
+user_pref("image.jxl.enabled", true);
+user_pref("devtools.cache.disabled", true);
+user_pref("devtools.netmonitor.persistlog", true);
+user_pref("devtools.webconsole.persistlog", true);
+user_pref("devtools.webconsole.timestampMessages", true);
+
+user_pref("toolkit.legacyUserProfileCustomizations.stylesheets", true);
diff --git a/modules/firefox/userChrome.css b/modules/firefox/userChrome.css
@@ -0,0 +1,64 @@
+@-moz-document url(chrome://browser/content/browser.xul), url(chrome://browser/content/browser.xhtml) {
+ #browser {
+ background-color: rgba(0,0,0,0.2) !important;
+ }
+
+ #navigator-toolbox {
+ background-color: rgba(0,0,0,0.2) !important;
+ border-bottom: none !important;
+ }
+
+ #firefox-view-button,
+ toolbarbutton.titlebar-close,
+ toolbarspring,
+ toolbarbutton#alltabs-button,
+ #urlbar-searchmode-switcher,
+ /* #unified-extensions-button, */
+ image.tab-close-button.close-icon,
+ toolbarbutton#tabs-newtab-button,
+ splitter#sidebar-tools-and-extensions-splitter,
+ .titlebar-spacer {
+ display: none !important;
+ }
+
+ #urlbar hbox.urlbar-background,
+ toolbar#nav-bar {
+ background: transparent !important;
+ }
+
+ #urlbar[breakout-extend] hbox.urlbar-background {
+ background: #222 !important;
+ }
+
+ tabs#tabbrowser-tabs {
+ border-inline-start: none !important;
+ }
+
+
+ .tabbrowser-tab:hover > .tab-stack > &:not([selected], [multiselected]) {
+ background-color: rgba(0,0,0,0.2) !important;
+ }
+
+ .tab-background {
+ &:is([selected], [multiselected]) {
+ background-color: rgba(255,255,255,0.1) !important;
+ }
+ }
+
+ toolbaritem#ublock0_raymondhill_net-browser-action image.toolbarbutton-icon {
+ filter: grayscale(100%) invert(100%);
+ }
+
+ toolbaritem#_446900e4-71c2-419f-a6a7-df9c091e268b_-browser-action image.toolbarbutton-icon,
+ button[title="Bitwarden"] img {
+ filter: grayscale(100%) invert(100%) contrast(200%);
+ }
+
+ toolbaritem#addon_darkreader_org-browser-action image.toolbarbutton-icon {
+ filter: grayscale(100%) invert(100%) contrast(250%);
+ }
+
+ stack.tab-icon-stack {
+ display: none;
+ }
+}
diff --git a/modules/flatpak/default.nix b/modules/flatpak/default.nix
@@ -0,0 +1,12 @@
+{ user, ... }:
+{
+ services.flatpak.enable = true;
+
+ environment.persistence."/fix" = {
+ directories = [ "/var/lib/flatpak" ];
+ users.${user}.directories = [
+ ".local/share/flatpak"
+ ".var/app"
+ ];
+ };
+}
diff --git a/modules/git/default.nix b/modules/git/default.nix
@@ -0,0 +1,14 @@
+{
+ lib,
+
+ user,
+ email,
+ ...
+}:
+{
+ home-manager.users.${user}.programs.git = {
+ enable = true;
+ userName = lib.toSentenceCase user;
+ userEmail = email;
+ };
+}
diff --git a/modules/greetd/default.nix b/modules/greetd/default.nix
@@ -0,0 +1,58 @@
+{
+ lib,
+ config,
+ pkgs,
+
+ user,
+
+ home-manager,
+ ...
+}:
+{
+ config = {
+ services.greetd = {
+ enable = true;
+ settings = {
+ default_session.command = "Hyprland --config ${
+ pkgs.writeText "greetd-hyprland.conf" (
+ home-manager.lib.hm.generators.toHyprconf {
+ attrs = {
+ inherit (config.home-manager.users.${user}.wayland.windowManager.hyprland.settings)
+ input
+ general
+ misc
+ decoration
+ animations
+ ;
+ windowrule = "float, class:gtkgreet";
+ exec-once = [
+ "${lib.getExe pkgs.gtkgreet} -s ${pkgs.writeText "greetd-style.css" ''
+ window, button, entry {
+ background: rgba(0,0,0,0.2);
+ border: none;
+ box-shadow: none;
+ text-shadow: none;
+ }
+ label, window, button, entry {
+ color: #eee;
+ }
+ #command-selector arrow {
+ opacity: 0;
+ }
+ ''} -c 'uwsm start hyprland-uwsm.desktop'; hyprctl dispatch exit"
+ "${lib.getExe pkgs.hyprpaper} -c ${
+ pkgs.writeText "greetd-hyprpaper.conf" (
+ home-manager.lib.hm.generators.toHyprconf {
+ attrs = config.home-manager.users.${user}.services.hyprpaper.settings;
+ }
+ )
+ }"
+ ];
+ };
+ }
+ )
+ }";
+ };
+ };
+ };
+}
diff --git a/modules/hypr/default.nix b/modules/hypr/default.nix
@@ -0,0 +1,6 @@
+{
+ imports = [
+ ./hyprland.nix
+ ./hyprpaper.nix
+ ];
+}
diff --git a/modules/hypr/hyprland.nix b/modules/hypr/hyprland.nix
@@ -0,0 +1,145 @@
+{
+ lib,
+ pkgs,
+
+ user,
+ ...
+}:
+{
+ programs.hyprland = {
+ enable = true;
+ xwayland.enable = true;
+ withUWSM = true;
+ };
+
+ home-manager.users.${user} =
+ { config, ... }:
+ {
+ wayland.windowManager.hyprland = {
+ enable = true;
+ package = null;
+ portalPackage = null;
+ settings = {
+ input = {
+ kb_layout = "us";
+ kb_options = "compose:ralt,caps:escape";
+ follow_mouse = 1;
+ touchpad.natural_scroll = false;
+ sensitivity = 0;
+ accel_profile = "flat";
+ };
+
+ general = {
+ gaps_in = 5;
+ gaps_out = 20;
+ border_size = 2;
+ "col.active_border" = "rgba(00000000) rgba(ffffffff) rgba(00000000) 45deg";
+ "col.inactive_border" = "rgba(00000000)";
+ layout = "dwindle";
+ };
+
+ decoration = {
+ rounding = 10;
+ shadow = {
+ enabled = false;
+ range = 4;
+ render_power = 3;
+ color = "rgba(1a1a1aee)";
+ };
+ blur = {
+ enabled = true;
+ size = 9;
+ passes = 3;
+ new_optimizations = true;
+ };
+ };
+
+ animations = {
+ enabled = lib.mkDefault true;
+ bezier = [
+ "linear, 0, 0, 1, 1"
+ "easeOut, 0.42, 0, 0.58, 1"
+ "easeInOut, 0, 0, 0.58, 1"
+ "easeIn, 0.42, 0, 1, 1"
+ ];
+ animation = [
+ "windows, 1, 2, linear"
+ "windowsOut, 1, 2, linear"
+ "border, 1, 2, linear"
+ "borderangle, 1, 100, linear, loop"
+ "fade, 1, 2, linear"
+ "workspaces, 1, 2, linear"
+ ];
+ };
+
+ misc = {
+ disable_hyprland_logo = true;
+ disable_splash_rendering = true;
+ disable_hyprland_qtutils_check = true;
+ };
+
+ ecosystem = {
+ no_donation_nag = true;
+ no_update_news = true;
+ };
+
+ "$mod" = lib.mkDefault "SUPER";
+
+ bind = [
+ "$mod, Q, killactive,"
+ "$mod SHIFT, Delete, exit,"
+ "$mod, F, fullscreen, 0"
+ "$mod SHIFT, F, togglefloating,"
+ "$mod, G, togglegroup,"
+ "$mod SHIFT, G, lockgroups,"
+ "$mod, Tab, changegroupactive,"
+ "$mod, P, pseudo,"
+ "$mod, O, togglesplit,"
+
+ "$mod, 1, workspace, 1"
+ "$mod, 2, workspace, 2"
+ "$mod, 3, workspace, 3"
+ "$mod, 4, workspace, 4"
+ "$mod, 5, workspace, 5"
+ "$mod, 6, workspace, 6"
+ "$mod, 7, workspace, 7"
+ "$mod, 8, workspace, 8"
+ "$mod, 9, workspace, 9"
+ "$mod, 0, workspace, 10"
+
+ "$mod SHIFT, 1, movetoworkspace, 1"
+ "$mod SHIFT, 2, movetoworkspace, 2"
+ "$mod SHIFT, 3, movetoworkspace, 3"
+ "$mod SHIFT, 4, movetoworkspace, 4"
+ "$mod SHIFT, 5, movetoworkspace, 5"
+ "$mod SHIFT, 6, movetoworkspace, 6"
+ "$mod SHIFT, 7, movetoworkspace, 7"
+ "$mod SHIFT, 8, movetoworkspace, 8"
+ "$mod SHIFT, 9, movetoworkspace, 9"
+ "$mod SHIFT, 0, movetoworkspace, 10"
+ ];
+
+ bindm = [
+ "$mod, mouse:272, movewindow"
+ "$mod, mouse:273, resizewindow"
+ ];
+
+ bindni = [ ", Alt_L, sendkeystate, , Alt_L, down, class:discord" ];
+ bindnir = [ ", Alt_L, sendkeystate, , Alt_L, up, class:discord" ];
+
+ windowrule = [ "noblur, xwayland:1" ];
+ };
+ };
+
+ xdg.configFile."uwsm/env".source =
+ "${config.home.sessionVariablesPackage}/etc/profile.d/hm-session-vars.sh";
+
+ home.pointerCursor = {
+ enable = true;
+ gtk.enable = true;
+ name = "Adwaita";
+ package = pkgs.gnome-themes-extra;
+ size = 24;
+ };
+ };
+}
diff --git a/modules/hypr/hyprpaper.nix b/modules/hypr/hyprpaper.nix
@@ -0,0 +1,19 @@
+{
+ lib,
+ config,
+
+ user,
+ ...
+}:
+{
+ config = {
+
+ home-manager.users.${user}.services.hyprpaper = {
+ enable = true;
+ settings = {
+ preload = [ "${./wallpaper.jpg}" ];
+ wallpaper = [ ",${./wallpaper.jpg}" ];
+ };
+ };
+ };
+}
diff --git a/modules/hypr/wallpaper.jpg b/modules/hypr/wallpaper.jpg
Binary files differ.
diff --git a/modules/imsh-clients/default.nix b/modules/imsh-clients/default.nix
@@ -0,0 +1,69 @@
+{
+ user,
+
+ imsh-clients,
+ ...
+}:
+{
+ home-manager.users.${user} = {
+ imports = [
+ imsh-clients.homeManagerModules.imsh-clients
+ ];
+
+ programs.imsh-clients = {
+ enable = true;
+ imsh-cast-monitor.waybar.enable = true;
+ };
+
+ wayland.windowManager.hyprland.settings.bind =
+ let
+ cat = builtins.concatStringsSep " ";
+ in
+ [
+ (cat [
+ "$mod, A, exec, uwsm app --"
+ "imsh-shot screen --cursor --utc --copy"
+ "--output ~/pic/scr/%Y-%m-%dT%H-%M-%S.%3NZ.png"
+ ])
+ (cat [
+ "$mod SHIFT, A, exec, uwsm app --"
+ "imsh-shot screen --cursor --utc --copy"
+ "--output ~/pic/scr/%Y-%m-%dT%H-%M-%S.%3NZ.png"
+ "--api-key '%rbw get scrn.is/api-key' --upload"
+ ])
+ (cat [
+ "$mod, S, exec, uwsm app --"
+ "imsh-shot area --freeze --utc --copy"
+ "--output ~/pic/scr/%Y-%m-%dT%H-%M-%S.%3NZ.png"
+ ])
+ (cat [
+ "$mod SHIFT, S, exec, uwsm app --"
+ "imsh-shot area --freeze --utc --copy"
+ "--output ~/pic/scr/%Y-%m-%dT%H-%M-%S.%3NZ.png"
+ "--api-key '%rbw get scrn.is/api-key' --upload"
+ ])
+ (cat [
+ "$mod, D, exec, uwsm app --"
+ "imsh-shot active --cursor --utc --copy"
+ "--output ~/pic/scr/%Y-%m-%dT%H-%M-%S.%3NZ.png"
+ ])
+ (cat [
+ "$mod SHIFT, D, exec, uwsm app --"
+ "imsh-shot active --cursor --utc --copy"
+ "--output ~/pic/scr/%Y-%m-%dT%H-%M-%S.%3NZ.png"
+ "--api-key '%rbw get scrn.is/api-key' --upload"
+ ])
+ (cat [
+ "$mod, R, exec, uwsm app --"
+ "imsh-cast --utc --copy"
+ "--output ~/vid/rec/%Y-%m-%dT%H-%M-%S.%3NZ.mp4"
+ ])
+ (cat [
+ "$mod SHIFT, R, exec, uwsm app --"
+ "imsh-cast --utc --copy"
+ "--output ~/vic/rec/%Y-%m-%dT%H-%M-%S.%3NZ.mp4"
+ "--api-key '%rbw get scrn.is/api-key' --upload"
+ ])
+ ];
+ };
+}
diff --git a/modules/kitty/default.nix b/modules/kitty/default.nix
@@ -0,0 +1,29 @@
+{
+ lib,
+ config,
+ pkgs,
+
+ user,
+ ...
+}:
+{
+ config.home-manager.users.${user} = {
+ wayland.windowManager.hyprland.settings.bind = [ "$mod, Return, exec, uwsm app -- kitty" ];
+
+ programs.kitty = {
+ enable = true;
+ font = {
+ name = "JetBrains Mono Nerd Font Mono";
+ size = 12;
+ package = pkgs.nerd-fonts.jetbrains-mono;
+ };
+ settings = {
+ background = "#000000";
+ background_opacity = 0.2;
+ window_padding_width = 10;
+ disable_ligatures = false;
+ confirm_os_window_close = 0;
+ };
+ };
+ };
+}
diff --git a/modules/mutable-files/default.nix b/modules/mutable-files/default.nix
@@ -0,0 +1,85 @@
+{
+ home-manager.sharedModules = [
+ (
+ { lib, config, ... }:
+ let
+ fileOptionAttrPaths = [
+ [
+ "home"
+ "file"
+ ]
+ [
+ "xdg"
+ "configFile"
+ ]
+ [
+ "xdg"
+ "dataFile"
+ ]
+ ];
+
+ mergeAttrsList = builtins.foldl' (lib.mergeAttrs) { };
+
+ fileAttrsType = lib.types.attrsOf (
+ lib.types.submodule ({
+ options.mutable = lib.mkOption {
+ type = lib.types.bool;
+ default = false;
+ description = ''
+ Whether to copy the file without the read-only attribute instead of
+ symlinking. If you set this to `true`, you must also set `force` to
+ `true`. Mutable files are not removed when you remove them from your
+ configuration.
+
+ This option is useful for programs that don't have a very good
+ support for read-only configurations.
+ '';
+ };
+ })
+ );
+ in
+ {
+ options = mergeAttrsList (
+ map (
+ attrPath: lib.setAttrByPath attrPath (lib.mkOption { type = fileAttrsType; })
+ ) fileOptionAttrPaths
+ );
+
+ config.home.activation.mutableFileGeneration =
+ let
+ allFiles = (
+ builtins.concatLists (
+ map (attrPath: builtins.attrValues (lib.getAttrFromPath attrPath config)) fileOptionAttrPaths
+ )
+ );
+
+ filterMutableFiles = builtins.filter (
+ file:
+ (file.mutable or false)
+ && lib.assertMsg file.force "if you set `mutable` to `true` on a file you must also set `force` to `true`"
+ );
+
+ mutableFiles = filterMutableFiles allFiles;
+
+ toCommand = (
+ file:
+ let
+ source = lib.escapeShellArg file.source;
+ target = lib.escapeShellArg file.target;
+ in
+ ''
+ $VERBOSE_ECHO "${source} -> ${target}"
+ $DRY_RUN_CMD cp --remove-destination --no-preserve=mode ${source} ${target}
+ ''
+ );
+
+ command = ''
+ echo "Copying mutable home files for $HOME"
+ ''
+ + lib.concatLines (map toCommand mutableFiles);
+ in
+ (lib.hm.dag.entryAfter [ "linkGeneration" ] command);
+ }
+ )
+ ];
+}
diff --git a/modules/neovim/default.nix b/modules/neovim/default.nix
@@ -0,0 +1,13 @@
+{ lib, config, ... }:
+{
+ programs.neovim = {
+ enable = true;
+ defaultEditor = true;
+ viAlias = true;
+ vimAlias = true;
+ configure.customLuaRC = builtins.readFile ./neovim.lua;
+ withRuby = lib.mkDefault false;
+ withPython3 = lib.mkDefault false;
+ withNodeJs = lib.mkDefault false;
+ };
+}
diff --git a/modules/neovim/neovim.lua b/modules/neovim/neovim.lua
@@ -0,0 +1,116 @@
+vim.opt.tabstop = 2
+vim.opt.shiftwidth = 0
+vim.opt.softtabstop = 0
+vim.opt.expandtab = true
+vim.opt.number = true
+vim.opt.relativenumber = true
+vim.opt.scrolloff = 5
+vim.opt.path = vim.o.path .. '**'
+vim.opt.wildmenu = true
+vim.opt.cursorline = true
+vim.opt.undofile = true
+vim.opt.undodir = vim.fn.stdpath('data') .. '/undodir'
+vim.opt.colorcolumn = '+1'
+vim.opt.textwidth = 72
+vim.opt.formatoptions = 'cqj'
+vim.keymap.set({'n','v'}, '<C-c>', '"+y')
+vim.keymap.set({'n','v'}, '<C-v>', '"+p')
+vim.keymap.set({'n','v'}, '<C-x>', '"+d')
+vim.keymap.set({'n','v'}, '<C-q>', '<C-v>', { noremap = true})
+vim.api.nvim_create_autocmd('BufReadPost', {
+ group = vim.api.nvim_create_augroup('ReturnToLastLine', { clear = true }),
+ callback = function()
+ local last = vim.fn.line([['"]])
+ if last > 1 and last < vim.fn.line("$") then
+ vim.cmd([[normal! g'"]])
+ end
+ end
+})
+vim.opt.list = true
+vim.opt.listchars = {
+ trail = '~',
+ tab = '| ',
+ leadmultispace = ':' .. string.rep(' ', vim.opt.tabstop:get() - 1),
+}
+vim.api.nvim_create_autocmd('OptionSet', {
+ group = vim.api.nvim_create_augroup('ListcharsLeadmultispaceWidth', { clear = true }),
+ pattern = 'tabstop',
+ callback = function()
+ local listchars = vim.opt.listchars:get()
+ listchars.leadmultispace = ':' .. string.rep(' ', vim.opt.tabstop:get() - 1)
+ vim.opt.listchars = listchars
+ end
+})
+vim.opt.cmdheight = 0
+vim.api.nvim_create_autocmd("RecordingEnter", {
+ callback = function()
+ vim.opt.cmdheight = 1
+ end,
+})
+vim.api.nvim_create_autocmd("RecordingLeave", {
+ callback = function()
+ vim.opt.cmdheight = 0
+ end,
+})
+vim.api.nvim_set_hl(0, 'Normal', { bg='none' })
+vim.api.nvim_set_hl(0, 'StatusLine', { bg='none' })
+vim.api.nvim_set_hl(0, 'WinBar', { bg='none' })
+vim.api.nvim_set_hl(0, 'WinBarNC', { bg='none', fg='gray' })
+function _G.WinBar()
+ local buffers = {}
+ local current = vim.api.nvim_win_get_buf(0)
+ for _, bufnr in ipairs(vim.api.nvim_list_bufs()) do
+ if vim.bo[bufnr].buflisted then
+ local name = vim.fn.fnamemodify(vim.fn.bufname(bufnr), ":t")
+ if name == "" then name = "[No name]" end
+ local highlight = bufnr == current and "WinBar" or "WinBarNC"
+ local modified = vim.bo[bufnr].modified and "*" or ""
+ table.insert(buffers, string.format(
+ "%%#%s# %d:%s%s %%*",
+ highlight, bufnr, name, modified
+ ))
+ end
+ end
+ return table.concat(buffers)
+end
+vim.api.nvim_create_autocmd({ 'BufEnter', 'BufAdd', 'BufDelete'}, {
+ group = vim.api.nvim_create_augroup("WinBarVisibility", { clear = true }),
+ pattern = "*",
+ callback = function()
+ local buffers = 0
+ for _, bufnr in ipairs(vim.api.nvim_list_bufs()) do
+ if vim.bo[bufnr].buflisted then
+ buffers = buffers + 1
+ end
+ end
+ if buffers > 1 then
+ vim.opt.winbar = "%!v:lua.WinBar()"
+ else
+ vim.opt.winbar = ""
+ end
+ end
+})
+vim.diagnostic.config({ virtual_text = true })
+vim.opt.signcolumn = "yes"
+vim.opt.completeopt = { "menuone", "noselect", "popup" }
+vim.api.nvim_create_autocmd('InsertCharPre', {
+ group = vim.api.nvim_create_augroup("TriggerAutocomplete", { clear = true }),
+ buffer = vim.api.nvim_get_current_buf(),
+ callback = function()
+ vim.lsp.completion.get()
+ end
+})
+vim.lsp.config('*', {
+ root_markers = { '.git' },
+ on_attach = function(client, bufnr)
+ vim.lsp.completion.enable(true, client.id, bufnr, {})
+ end,
+})
+vim.lsp.config('nixd', {
+ cmd = { 'nixd' },
+ filetypes = { 'nix' },
+ root_markers = { 'flake.nix', '.git' },
+})
+vim.lsp.enable({
+ "nixd",
+})
diff --git a/modules/nixpak/default.nix b/modules/nixpak/default.nix
@@ -0,0 +1,11 @@
+{
+ lib,
+ pkgs,
+ nixpak,
+ ...
+}:
+{
+ _module.args.mkNixPak = nixpak.lib.nixpak {
+ inherit lib pkgs;
+ };
+}
diff --git a/modules/persistence/default.nix b/modules/persistence/default.nix
@@ -0,0 +1,26 @@
+{
+ lib,
+ config,
+
+ impermanence,
+ ...
+}:
+{
+ imports = [ impermanence.nixosModules.impermanence ];
+
+ config = {
+ fileSystems."/fix".neededForBoot = true;
+
+ environment.persistence."/fix" = {
+ hideMounts = true;
+
+ files = [
+ "/etc/machine-id"
+ "/etc/ssh/ssh_host_ed25519_key"
+ "/etc/ssh/ssh_host_ed25519_key.pub"
+ "/etc/ssh/ssh_host_rsa_key"
+ "/etc/ssh/ssh_host_rsa_key.pub"
+ ];
+ };
+ };
+}
diff --git a/modules/pipewire/default.nix b/modules/pipewire/default.nix
@@ -0,0 +1,11 @@
+{
+ security.rtkit.enable = true;
+ services.pipewire = {
+ enable = true;
+ alsa.enable = true;
+ alsa.support32Bit = true;
+ pulse.enable = true;
+ jack.enable = true;
+ wireplumber.enable = true;
+ };
+}
diff --git a/modules/plymouth/default.nix b/modules/plymouth/default.nix
@@ -0,0 +1,10 @@
+{
+ pkgs,
+ ...
+}:
+{
+ config.boot.plymouth = {
+ enable = true;
+ font = "${pkgs.nerd-fonts.jetbrains-mono}/share/fonts/truetype/NerdFonts/JetBrainsMono/JetBrainsMonoNerdFontPropo-Regular.ttf";
+ };
+}
diff --git a/modules/rbw/default.nix b/modules/rbw/default.nix
@@ -0,0 +1,37 @@
+{
+ user,
+ email,
+ ...
+}:
+{
+ environment.persistence."/fix".users.${user} = {
+ files = [
+ {
+ file = ".local/share/rbw/device_id";
+ parentDirectory.mode = "0700";
+ }
+ ];
+ directories = [
+ {
+ directory = ".cache/rbw";
+ mode = "0700";
+ }
+ ];
+ };
+
+ home-manager.users.${user} =
+ { config, ... }:
+ {
+ home.sessionVariables = {
+ SSH_AUTH_SOCK = "$XDG_RUNTIME_DIR/rbw/ssh-agent-socket";
+ };
+
+ programs.rbw = {
+ enable = true;
+ settings = {
+ email = email;
+ pinentry = config.programs.wayprompt.package;
+ };
+ };
+ };
+}
diff --git a/modules/secrets/default.nix b/modules/secrets/default.nix
@@ -0,0 +1,18 @@
+{
+ lib,
+ config,
+ sops-nix,
+ ...
+}:
+{
+ imports = [ sops-nix.nixosModules.sops ];
+
+ config = {
+ home-manager.sharedModules = [ sops-nix.homeManagerModules.sops ];
+
+ sops = {
+ age.sshKeyPaths = [ "/fix/etc/ssh/ssh_host_ed25519_key" ];
+ gnupg.sshKeyPaths = [ ];
+ };
+ };
+}
diff --git a/modules/ssh/default.nix b/modules/ssh/default.nix
@@ -0,0 +1,5 @@
+{
+ services.openssh = {
+ enable = true;
+ };
+}
diff --git a/modules/user/default.nix b/modules/user/default.nix
@@ -0,0 +1,40 @@
+{
+ lib,
+ config,
+ pkgs,
+
+ user,
+
+ home-manager,
+ ...
+}:
+{
+ imports = [
+ home-manager.nixosModules.home-manager
+ ];
+
+ config = {
+ users = {
+ users.${user} = {
+ isNormalUser = true;
+ hashedPasswordFile = config.sops.secrets.password.path;
+ group = user;
+ uid = 1000;
+ useDefaultShell = true;
+ extraGroups = [
+ "wheel"
+ "video"
+ "audio"
+ ];
+ };
+
+ groups.${user}.gid = 1000;
+ };
+
+ sops.secrets.password.neededForUsers = true;
+
+ home-manager.users.${user} = {
+ home.stateVersion = lib.mkDefault config.system.stateVersion;
+ };
+ };
+}
diff --git a/modules/walker/default.nix b/modules/walker/default.nix
@@ -0,0 +1,64 @@
+{
+ lib,
+ config,
+
+ user,
+
+ walker,
+ ...
+}:
+{
+ config.home-manager.users.${user} = {
+ imports = [ walker.homeManagerModules.walker ];
+
+ wayland.windowManager.hyprland.settings = {
+ bind = [ "$mod, Escape, exec, uwsm app -- walker" ];
+ layerrule = [
+ "blur,walker"
+ "ignorezero,walker"
+ ];
+ };
+
+ programs.walker = {
+ enable = true;
+ runAsService = true;
+
+ config = {
+ placeholders.default = {
+ input = "Search...";
+ list = "";
+ };
+ providers = {
+ empty = [ ];
+ };
+ };
+
+ theme.style = ''
+ * {
+ background: none;
+ border: none;
+ box-shadow: none;
+ color: #eee;
+ font-family: JetBrains Mono Nerd Font Propo;
+ font-size: 12pt;
+ outline: none;
+ }
+
+ .box-wrapper {
+ padding: 20px;
+ border-radius: 20px;
+ background: rgba(0,0,0,0.2);
+ }
+ '';
+
+ elephant = {
+ installService = true;
+ config = {
+ providers.desktopapplications = {
+ launch_prefix = "uwsm app --";
+ };
+ };
+ };
+ };
+ };
+}
diff --git a/modules/waybar/default.nix b/modules/waybar/default.nix
@@ -0,0 +1,95 @@
+{
+ lib,
+ config,
+
+ user,
+ ...
+}:
+{
+ home-manager.users.${user} = {
+ wayland.windowManager.hyprland.settings.layerrule = [ "blur,waybar" ];
+
+ programs.waybar = {
+ enable = true;
+ systemd.enable = true;
+
+ style = builtins.readFile ./style.css;
+
+ settings = {
+ mainBar = {
+ layer = "top";
+ position = "top";
+ height = 36;
+ modules-left = [
+ "hyprland/workspaces"
+ "hyprland/window"
+ ];
+ modules-center = [
+ "clock#date"
+ "clock"
+ "clock#utc"
+ ];
+ modules-right = [
+ "battery"
+ "cpu"
+ "memory"
+ "disk"
+ "tray"
+ ];
+ "hyprland/workspaces" = {
+ format = "{name}";
+ on-click = "activate";
+ sort-by = "number";
+ };
+ "clock#date" = {
+ interval = 1;
+ format = " {:%a, %b %d}";
+ };
+ clock = {
+ interval = 1;
+ format = " {:%I:%M:%S%p}";
+ };
+ "clock#utc" = {
+ interval = 1;
+ format = " {:%I:%M:%S%p}";
+ timezone = "UTC";
+ };
+ cpu = {
+ interval = 1;
+ format = " {usage}%";
+ };
+ memory = {
+ interval = 1;
+ format = " {percentage}%";
+ };
+ disk = {
+ interval = 1;
+ format = " {percentage_used}%";
+ path = "/";
+ };
+ battery = {
+ bat = "BAT0";
+ interval = 1;
+ states = {
+ warning = 20;
+ critical = 10;
+ };
+ format = "{icon} {capacity}%";
+ format-icons = [
+ ""
+ ""
+ ""
+ ""
+ ""
+ ];
+ tooltip-format = "{timeTo}";
+ };
+ tray = {
+ icon-size = 18;
+ spacing = 10;
+ };
+ };
+ };
+ };
+ };
+}
diff --git a/modules/waybar/style.css b/modules/waybar/style.css
@@ -0,0 +1,40 @@
+* {
+ border: none;
+ border-radius: 0;
+ font-family: JetBrains Mono Nerd Font Propo;
+ font-size: 12pt;
+}
+
+window#waybar {
+ border: none;
+ color: #fff;
+ background: rgba(0,0,0,0.2);
+}
+
+.modules-right > widget > *, #window {
+ padding: 0 10px;
+}
+
+.modules-center > widget > * {
+ padding: 0 10px;
+}
+
+#tray {
+ padding: 0;
+ padding-right: 10px;
+}
+
+#workspaces button {
+ color: #fff;
+ transition: box-shadow 0.1s linear;
+}
+
+#workspaces button:hover {
+ background: inherit;
+ box-shadow: inset 0px 2px #777;
+ text-shadow: inherit;
+}
+
+#workspaces button.active {
+ box-shadow: inset 0px 2px #fff;
+}
diff --git a/modules/wayprompt/default.nix b/modules/wayprompt/default.nix
@@ -0,0 +1,51 @@
+{
+ lib,
+
+ user,
+ ...
+}:
+{
+ home-manager.users.${user} = {
+ wayland.windowManager.hyprland.settings.layerrule = [
+ "blur,wayprompt"
+ "ignorezero,wayprompt"
+ ];
+
+ programs.wayprompt = {
+ enable = true;
+
+ settings = {
+ general = {
+ font-regular = "JetBrains Mono Nerd Font Propo:size=12";
+ font-large = "JetBrains Mono Nerd Font Propo:size=24";
+ corner-radius = 10;
+ border = 0;
+ pin-square-amount = 32;
+ };
+
+ colours = {
+ background = "00000033";
+ border = "00000033";
+ text = "ffffffff";
+ error-text = "ffffffff";
+
+ pin-background = "00000033";
+ pin-border = "eeeeeeff";
+ pin-square = "eeeeeeff";
+
+ ok-button = "00000033";
+ ok-button-border = "eeeeeeff";
+ ok-button-text = "eeeeeeff";
+
+ not-ok-button = "00000033";
+ not-ok-button-border = "eeeeeeff";
+ not-ok-button-text = "eeeeeeff";
+
+ cancel-button = "00000033";
+ cancel-button-border = "eeeeeeff";
+ cancel-button-text = "eeeeeeff";
+ };
+ };
+ };
+ };
+}
diff --git a/modules/xdg-user-dirs/default.nix b/modules/xdg-user-dirs/default.nix
@@ -0,0 +1,43 @@
+{
+ lib,
+ config,
+
+ user,
+ ...
+}:
+{
+ config = {
+ home-manager.users.${user}.xdg.userDirs =
+ let
+ tilde = config.home-manager.users.${user}.home.homeDirectory;
+ in
+ {
+ enable = true;
+ createDirectories = true;
+
+ # cli friendly names
+ documents = "${tilde}/doc";
+ download = "${tilde}/dls";
+ music = "${tilde}/mus";
+ pictures = "${tilde}/pic";
+ videos = "${tilde}/vid";
+
+ # some non-standard ones that should be there
+ extraConfig = {
+ XDG_GIT_DIR = "${tilde}/git";
+ XDG_TMP_DIR = "${tilde}/tmp";
+ XDG_MNT_DIR = "${tilde}/mnt";
+
+ # important git repos
+ XDG_DOT_DIR = "${tilde}/dot";
+ XDG_SEC_DIR = "${tilde}/sec";
+ XDG_SRV_DIR = "${tilde}/srv";
+ };
+
+ # map useless dirs to tmp
+ desktop = "${tilde}/tmp";
+ publicShare = "${tilde}/tmp";
+ templates = "${tilde}/tmp";
+ };
+ };
+}
diff --git a/modules/zsh/default.nix b/modules/zsh/default.nix
@@ -0,0 +1,55 @@
+{
+ lib,
+ pkgs,
+ ...
+}:
+{
+ config = {
+ users = {
+ defaultUserShell = pkgs.zsh;
+ users.root.shell = pkgs.zsh;
+ };
+
+ environment.systemPackages = [ pkgs.fzf ];
+
+ programs = {
+ # Use this and remove other fzf-related config if this is made to work together with
+ # zsh-vi-mode (currently breaks ^r binding):
+ # fzf.keybindings = true;
+ # fzf.fuzzyCompletion = true;
+ git.enable = true;
+ direnv.enable = true;
+ };
+
+ programs.zsh = {
+ enable = true;
+
+ shellInit = ''
+ # Silence new user configuration prompt
+ zsh-newuser-install() { :; }
+ '';
+
+ interactiveShellInit = ''
+ zvm_after_init() {
+ source <(fzf --zsh)
+ }
+ source ${pkgs.zsh-vi-mode}/share/zsh-vi-mode/zsh-vi-mode.plugin.zsh
+ ZSH_AUTOSUGGEST_STRATEGY=(history completion)
+ ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE=200
+ ZSH_AUTOSUGGEST_HISTORY_IGNORE="?(#c200,)"
+ source ${pkgs.zsh-autosuggestions}/share/zsh-autosuggestions/zsh-autosuggestions.zsh
+ source ${pkgs.zsh-syntax-highlighting}/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh
+ '';
+
+ promptInit = ''
+ source ${pkgs.liquidprompt}/bin/liquidprompt
+ '';
+ };
+
+ environment.etc."liquidpromptrc".source = ./liquidpromptrc;
+
+ home-manager.sharedModules = lib.singleton {
+ programs.zsh.enable = true;
+ };
+ };
+}
diff --git a/modules/zsh/liquidpromptrc b/modules/zsh/liquidpromptrc
@@ -0,0 +1,70 @@
+LP_ENV_VARS=(
+ "DIRENV_DIR +d"
+)
+LP_ENABLE_GIT=1
+LP_ENABLE_SSH_COLORS=1
+LP_ENABLE_KUBECONTEXT=1
+LP_ENABLE_KUBE_NAMESPACE=1
+LP_USER_ALWAYS=0
+LP_ENABLE_DIRSTACK=1
+LP_ENABLE_DISK=1
+LP_DISK_THRESHOLD=$((1024 * 1024))
+LP_DISK_THRESHOLD_PERC=1
+LP_ENABLE_RAM=1
+LP_RAM_THRESHOLD=0
+LP_RAM_THRESHOLD_PERC=10
+LP_ENABLE_RUNTIME=1
+LP_RUNTIME_THRESHOLD=2
+LP_ENABLE_TITLE=1
+LP_ENABLE_SCREEN_TITLE=1
+
+LP_ENABLE_BATT=0
+LP_ENABLE_BZR=0
+LP_ENABLE_CHROOT=0
+LP_ENABLE_DETACHED_SESSIONS=0
+LP_ENABLE_DISPLAY=0
+LP_ENABLE_FOSSIL=0
+LP_ENABLE_HG=0
+LP_ENABLE_LOAD=0
+LP_ENABLE_MODULES=0
+LP_ENABLE_MODULES_VERSIONS=0
+LP_ENABLE_PERL_VENV=0
+LP_ENABLE_RUBY_VENV=0
+LP_ENABLE_SCLS=0
+LP_ENABLE_SVN=0
+LP_ENABLE_TEMP=0
+LP_ENABLE_VIRTUALENV=0
+
+LP_MARK_BRACKET_CLOSE=""
+LP_MARK_BRACKET_OPEN=""
+LP_MARK_DEV_CLOSE=""
+LP_MARK_DEV_MID=" "
+LP_MARK_DEV_OPEN=""
+LP_MARK_ENV_VARS_OPEN=" "
+LP_MARK_ENV_VARS_SEP=" "
+LP_MARK_ENV_VARS_CLOSE=""
+LP_MARK_KUBECONTEXT="${BLUE}k="
+LP_MARK_DISK="df="
+LP_MARK_RAM="free="
+LP_MARK_JOBS_SEPARATOR="/"
+LP_MARK_PERM=""
+LP_COLOR_WRITE=""
+LP_COLOR_NOWRITE="${BOLD_RED}ro="
+LP_MARK_PROXY=" +proxy"
+LP_MARK_SHLVL=" l="
+LP_MARK_SHORTEN_PATH="..."
+LP_MARK_UNTRACKED="*"
+LP_MARK_GIT="%%"
+LP_MARK_DIRSTACK="d="
+
+LP_COLOR_CHANGES="$BOLD_RED"
+LP_COLOR_KUBECONTEXT="$BLUE"
+LP_COLOR_PATH="$CYAN"
+LP_COLOR_PATH_ROOT="$CYAN"
+LP_COLOR_PATH_LAST_DIR="$BOLD_CYAN"
+LP_COLOR_PATH_SEPARATOR="$WHITE"
+LP_COLOR_PATH_SHORTENED="$WHITE"
+LP_COLOR_PATH_VCS_ROOT="$YELLOW"
+LP_COLOR_ENV_VARS_SET="$BLUE"
+LP_COLOR_ENV_VARS_UNSET="$RED"
+