This commit is contained in:
root 2026-03-16 12:19:11 -03:00
commit 73ff9ee8ee
No known key found for this signature in database
31 changed files with 4906 additions and 0 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
result
.cache

9
.sops.yaml Normal file
View file

@ -0,0 +1,9 @@
keys:
- &root age1y0tj3kt67pfnj38t9c8g2ghry3a0mhcq8rrqv5xr4jekwepxaelqzu3dkf
- &user age16v8w7q4wmn22hhakq2uzaus2508rhldm7lcwh0kukshzjzyhuqesqz44ze
creation_rules:
- path_regex: secrets/[^/]+\.yaml$
key_groups:
- age:
- *root
- *user

143
configuration.nix Normal file
View file

@ -0,0 +1,143 @@
# Edit this configuration file to define what should be installed on
# your system. Help is available in the configuration.nix(5) man page, on
# https://search.nixos.org/options and in the NixOS manual (`nixos-help`).
{
config,
lib,
pkgs,
...
}:
{
sops.defaultSopsFile = ./secrets/home.yaml;
sops.age.keyFile = "/.persist/root/.config/sops/age/keys.txt";
sops.secrets."root/ssh/desktop" = {
path = "/root/.ssh/desktop";
mode = "0600";
};
nix.settings.experimental-features = [
"nix-command"
"flakes"
];
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
boot.initrd.systemd.enable = true;
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
# Set your time zone.
time.timeZone = "America/Sao_Paulo";
# Select internationalisation properties.
i18n.defaultLocale = "en_US.UTF-8";
#console = {
# # font = "Lat2-Terminus16";
# keyMap = "us";
# useXkbConfig = true; # use xkb.options in tty.
#};
# services.xserver.xkb.layout = "us";
# services.xserver.xkb.options = "eurosign:e,caps:escape";
services.printing.enable = true;
services = {
xserver.xkb = {
layout = "us";
variant = "altgr-intl";
};
pipewire = {
enable = true;
alsa.enable = true;
alsa.support32Bit = true;
pulse.enable = true;
# jack.enable = true;
};
logind.settings.Login = {
HandlePowerKey = "ignore";
HandlePowerKeyLongPress = "ignore";
HandleRebootKey = "ignore";
HandleRebootKeyLongPress = "ignore";
HandleHibernateKey = "ignore";
HandleHibernateKeyLongPress = "ignore";
};
getty = {
autologinUser = "user";
autologinOnce = true;
};
# greetd = {
# enable = true;
# settings = rec {
# initial_session = {
# command = "${pkgs.niri}/bin/niri-session";
# user = "user";
# };
# default_session = initial_session;
# };
# };
tailscale.enable = true;
};
hardware = {
graphics = {
enable = true;
enable32Bit = true;
};
bluetooth = {
enable = true;
powerOnBoot = true;
settings = {
General = {
Enable = "Source,Sink,Media,Socket";
};
};
};
};
xdg.portal = {
enable = true;
xdgOpenUsePortal = true;
# config.common.default = [ "*" ];
extraPortals = [
pkgs.xdg-desktop-portal-gnome
pkgs.gnome-keyring
pkgs.xdg-desktop-portal-gtk
];
};
qt = {
enable = true;
};
fonts.packages = with pkgs; [
fira-code-symbols
nerd-fonts.monaspace
];
environment.sessionVariables = {
NIXOS_OZONE_WL = "1";
EDITOR = "nvim";
GTK_IM_MODULE = "simple";
};
security.pki.certificateFiles = [ ./templates/certs/hydra_root_ca.crt ];
system.stateVersion = "25.11";
systemd = {
user.services.polkit-gnome-authentication-agent-1 = {
description = "polkit-gnome-authentication-agent-1";
wantedBy = [ "graphical-session.target" ];
wants = [ "graphical-session.target" ];
after = [ "graphical-session.target" ];
serviceConfig = {
Type = "simple";
ExecStart = "${pkgs.polkit_gnome}/libexec/polkit-gnome-authentication-agent-1";
Restart = "on-failure";
RestartSec = 1;
TimeoutStopSec = 10;
};
};
};
services.openssh.enable = true;
}

533
flake.lock generated Normal file
View file

@ -0,0 +1,533 @@
{
"nodes": {
"dgop": {
"inputs": {
"nixpkgs": [
"dms",
"nixpkgs"
]
},
"locked": {
"lastModified": 1765838956,
"narHash": "sha256-A3a2ZfvjirX8VIdIPI+nAyukWs6vx4vet3fU0mpr7lU=",
"owner": "AvengeMedia",
"repo": "dgop",
"rev": "0ff697a4e3418966caa714c838fc73f1ef6ba59b",
"type": "github"
},
"original": {
"owner": "AvengeMedia",
"repo": "dgop",
"type": "github"
}
},
"disko": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1768920986,
"narHash": "sha256-CNzzBsRhq7gg4BMBuTDObiWDH/rFYHEuDRVOwCcwXw4=",
"owner": "nix-community",
"repo": "disko",
"rev": "de5708739256238fb912c62f03988815db89ec9a",
"type": "github"
},
"original": {
"owner": "nix-community",
"ref": "latest",
"repo": "disko",
"type": "github"
}
},
"dms": {
"inputs": {
"dgop": "dgop",
"nixpkgs": "nixpkgs",
"quickshell": "quickshell"
},
"locked": {
"lastModified": 1766776522,
"narHash": "sha256-wS2fSepxdtOr4RErdEY91hkxOjsrs2nA2nm72eZMEEU=",
"owner": "AvengeMedia",
"repo": "DankMaterialShell",
"rev": "987856a1de35c62dc0930b007b561545d6a832a8",
"type": "github"
},
"original": {
"owner": "AvengeMedia",
"repo": "DankMaterialShell",
"rev": "987856a1de35c62dc0930b007b561545d6a832a8",
"type": "github"
}
},
"flake-parts": {
"inputs": {
"nixpkgs-lib": [
"neovim-nightly-overlay",
"nixpkgs"
]
},
"locked": {
"lastModified": 1769996383,
"narHash": "sha256-AnYjnFWgS49RlqX7LrC4uA+sCCDBj0Ry/WOJ5XWAsa0=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "57928607ea566b5db3ad13af0e57e921e6b12381",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"flake-parts_2": {
"inputs": {
"nixpkgs-lib": [
"nur",
"nixpkgs"
]
},
"locked": {
"lastModified": 1733312601,
"narHash": "sha256-4pDvzqnegAfRkPwO3wmwBhVi/Sye1mzps0zHWYnP88c=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "205b12d8b7cd4802fbcb8e8ef6a0f1408781a4f9",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"home-manager": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1770260404,
"narHash": "sha256-3iVX1+7YUIt23hBx1WZsUllhbmP2EnXrV8tCRbLxHc8=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "0d782ee42c86b196acff08acfbf41bb7d13eed5b",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "home-manager",
"rev": "0d782ee42c86b196acff08acfbf41bb7d13eed5b",
"type": "github"
}
},
"home-manager_2": {
"inputs": {
"nixpkgs": [
"impermanence",
"nixpkgs"
]
},
"locked": {
"lastModified": 1768598210,
"narHash": "sha256-kkgA32s/f4jaa4UG+2f8C225Qvclxnqs76mf8zvTVPg=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "c47b2cc64a629f8e075de52e4742de688f930dc6",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "home-manager",
"type": "github"
}
},
"impermanence": {
"inputs": {
"home-manager": "home-manager_2",
"nixpkgs": "nixpkgs_2"
},
"locked": {
"lastModified": 1769548169,
"narHash": "sha256-03+JxvzmfwRu+5JafM0DLbxgHttOQZkUtDWBmeUkN8Y=",
"owner": "nix-community",
"repo": "impermanence",
"rev": "7b1d382faf603b6d264f58627330f9faa5cba149",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "impermanence",
"type": "github"
}
},
"microvm": {
"inputs": {
"nixpkgs": [
"nixpkgs"
],
"spectrum": "spectrum"
},
"locked": {
"lastModified": 1770310890,
"narHash": "sha256-lyWAs4XKg3kLYaf4gm5qc5WJrDkYy3/qeV5G733fJww=",
"owner": "microvm-nix",
"repo": "microvm.nix",
"rev": "68c9f9c6ca91841f04f726a298c385411b7bfcd5",
"type": "github"
},
"original": {
"owner": "microvm-nix",
"repo": "microvm.nix",
"type": "github"
}
},
"neovim-nightly-overlay": {
"inputs": {
"flake-parts": "flake-parts",
"neovim-src": "neovim-src",
"nixpkgs": "nixpkgs_3"
},
"locked": {
"lastModified": 1771632300,
"narHash": "sha256-uP5SbbbN86+LZ8VubL01UKD6bez5DK9prqIqQOMy3Jw=",
"owner": "nix-community",
"repo": "neovim-nightly-overlay",
"rev": "0f601090d4d54b3da0d03e270cb6a5c68bf84dd3",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "neovim-nightly-overlay",
"type": "github"
}
},
"neovim-src": {
"flake": false,
"locked": {
"lastModified": 1771630915,
"narHash": "sha256-7RPG+RG/e0O79HjNT/ztC7K7j/xXazltq3TPk1mauqY=",
"owner": "neovim",
"repo": "neovim",
"rev": "d79a9dcd422133bc1e4b4ef94444962560d7a6d7",
"type": "github"
},
"original": {
"owner": "neovim",
"repo": "neovim",
"type": "github"
}
},
"niri-branch": {
"inputs": {
"nixpkgs": [
"nixpkgs"
],
"rust-overlay": "rust-overlay"
},
"locked": {
"lastModified": 1769284707,
"narHash": "sha256-X60XGpLjNTgYyaC/gChHGpqQqLWGI+0n5BbWaybXKiE=",
"owner": "argosnothing",
"repo": "niri",
"rev": "6dcaa349acf3b04ed1593022388b4f1cbef8893b",
"type": "github"
},
"original": {
"owner": "argosnothing",
"ref": "hidden-workspaces",
"repo": "niri",
"type": "github"
}
},
"niri-scratchpad": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": [
"nixpkgs"
],
"rust-overlay": "rust-overlay_2"
},
"locked": {
"lastModified": 1765743947,
"narHash": "sha256-kx8XFbzG59eLNImygoN9jRjgaxR7kvmjg64equccmK0=",
"owner": "argosnothing",
"repo": "niri-scratchpad-rs",
"rev": "163420c14c9199d311627501eedee2a3b2507db2",
"type": "github"
},
"original": {
"owner": "argosnothing",
"ref": "hidden-workspaces",
"repo": "niri-scratchpad-rs",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1766651565,
"narHash": "sha256-QEhk0eXgyIqTpJ/ehZKg9IKS7EtlWxF3N7DXy42zPfU=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "3e2499d5539c16d0d173ba53552a4ff8547f4539",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1768564909,
"narHash": "sha256-Kell/SpJYVkHWMvnhqJz/8DqQg2b6PguxVWOuadbHCc=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "e4bae1bd10c9c57b2cf517953ab70060a828ee6f",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_3": {
"locked": {
"lastModified": 1771207753,
"narHash": "sha256-b9uG8yN50DRQ6A7JdZBfzq718ryYrlmGgqkRm9OOwCE=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "d1c15b7d5806069da59e819999d70e1cec0760bf",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_4": {
"locked": {
"lastModified": 1744536153,
"narHash": "sha256-awS2zRgF4uTwrOKwwiJcByDzDOdo3Q1rPZbiHQg/N38=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "18dd725c29603f582cf1900e0d25f9f1063dbf11",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_5": {
"locked": {
"lastModified": 1771342064,
"narHash": "sha256-Aros+b3kQpzJAyxjDyhLUmnEfzQfyor2tiIoUTSgki0=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "3f03a5f1bede585f58c878c22cb12988bb0d1ed2",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_6": {
"locked": {
"lastModified": 1770562336,
"narHash": "sha256-ub1gpAONMFsT/GU2hV6ZWJjur8rJ6kKxdm9IlCT0j84=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "d6c71932130818840fc8fe9509cf50be8c64634f",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nur": {
"inputs": {
"flake-parts": "flake-parts_2",
"nixpkgs": "nixpkgs_6"
},
"locked": {
"lastModified": 1770758031,
"narHash": "sha256-YEq6M9OOEOl7l2zr/YjOi2UnuQZZ02HvXebpWGpkEHM=",
"owner": "nix-community",
"repo": "NUR",
"rev": "6701aa01b90606ab75078c1910bb991b8e7a389b",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "NUR",
"type": "github"
}
},
"quickshell": {
"inputs": {
"nixpkgs": [
"dms",
"nixpkgs"
]
},
"locked": {
"lastModified": 1766386896,
"narHash": "sha256-1uql4y229Rh+/2da99OVNe6DfsjObukXkf60TYRCvhI=",
"ref": "refs/heads/master",
"rev": "3918290c1bcd93ed81291844d9f1ed146672dbfc",
"revCount": 714,
"type": "git",
"url": "https://git.outfoxxed.me/quickshell/quickshell"
},
"original": {
"rev": "3918290c1bcd93ed81291844d9f1ed146672dbfc",
"type": "git",
"url": "https://git.outfoxxed.me/quickshell/quickshell"
}
},
"root": {
"inputs": {
"disko": "disko",
"dms": "dms",
"home-manager": "home-manager",
"impermanence": "impermanence",
"microvm": "microvm",
"neovim-nightly-overlay": "neovim-nightly-overlay",
"niri-branch": "niri-branch",
"niri-scratchpad": "niri-scratchpad",
"nixpkgs": "nixpkgs_5",
"nur": "nur",
"sops-nix": "sops-nix"
}
},
"rust-overlay": {
"inputs": {
"nixpkgs": [
"niri-branch",
"nixpkgs"
]
},
"locked": {
"lastModified": 1767322002,
"narHash": "sha256-yHKXXw2OWfIFsyTjduB4EyFwR0SYYF0hK8xI9z4NIn0=",
"owner": "oxalica",
"repo": "rust-overlay",
"rev": "03c6e38661c02a27ca006a284813afdc461e9f7e",
"type": "github"
},
"original": {
"owner": "oxalica",
"repo": "rust-overlay",
"type": "github"
}
},
"rust-overlay_2": {
"inputs": {
"nixpkgs": "nixpkgs_4"
},
"locked": {
"lastModified": 1763952169,
"narHash": "sha256-+PeDBD8P+NKauH+w7eO/QWCIp8Cx4mCfWnh9sJmy9CM=",
"owner": "oxalica",
"repo": "rust-overlay",
"rev": "ab726555a9a72e6dc80649809147823a813fa95b",
"type": "github"
},
"original": {
"owner": "oxalica",
"repo": "rust-overlay",
"type": "github"
}
},
"sops-nix": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1770683991,
"narHash": "sha256-xVfPvXDf9QN3Eh9dV+Lw6IkWG42KSuQ1u2260HKvpnc=",
"owner": "Mic92",
"repo": "sops-nix",
"rev": "8b89f44c2cc4581e402111d928869fe7ba9f7033",
"type": "github"
},
"original": {
"owner": "Mic92",
"repo": "sops-nix",
"type": "github"
}
},
"spectrum": {
"flake": false,
"locked": {
"lastModified": 1759482047,
"narHash": "sha256-H1wiXRQHxxPyMMlP39ce3ROKCwI5/tUn36P8x6dFiiQ=",
"ref": "refs/heads/main",
"rev": "c5d5786d3dc938af0b279c542d1e43bce381b4b9",
"revCount": 996,
"type": "git",
"url": "https://spectrum-os.org/git/spectrum"
},
"original": {
"type": "git",
"url": "https://spectrum-os.org/git/spectrum"
}
},
"systems": {
"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",
"version": 7
}

121
flake.nix Normal file
View file

@ -0,0 +1,121 @@
{
description = "NixOS flake";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs";
sops-nix = {
url = "github:Mic92/sops-nix";
inputs.nixpkgs.follows = "nixpkgs";
};
microvm = {
url = "github:microvm-nix/microvm.nix";
inputs.nixpkgs.follows = "nixpkgs";
};
disko = {
url = "github:nix-community/disko/latest";
inputs.nixpkgs.follows = "nixpkgs";
};
impermanence.url = "github:nix-community/impermanence";
neovim-nightly-overlay.url = "github:nix-community/neovim-nightly-overlay";
home-manager = {
url = "github:nix-community/home-manager/0d782ee42c86b196acff08acfbf41bb7d13eed5b";
inputs.nixpkgs.follows = "nixpkgs";
};
nur.url = "github:nix-community/NUR";
niri-branch = {
url = "github:argosnothing/niri/hidden-workspaces";
inputs.nixpkgs.follows = "nixpkgs";
};
niri-scratchpad = {
url = "github:argosnothing/niri-scratchpad-rs/hidden-workspaces";
inputs.nixpkgs.follows = "nixpkgs";
};
dms.url = "github:AvengeMedia/DankMaterialShell/987856a1de35c62dc0930b007b561545d6a832a8";
};
outputs =
inputs@{
nixpkgs,
sops-nix,
impermanence,
home-manager,
...
}:
let
hostname = "amelia";
system = "x86_64-linux";
pkgs = import nixpkgs {
inherit system;
config.allowUnfree = true;
# overlays = [ inputs.neovim-nightly-overlay.overlays.default ];
};
microvm = inputs.microvm.nixosModules.host;
in
{
nixosConfigurations."${hostname}" = nixpkgs.lib.nixosSystem {
inherit system pkgs;
specialArgs = {
inherit
nixpkgs
impermanence
home-manager
microvm
sops-nix
;
hostname = hostname;
};
modules = [
./users.nix
./storage.nix
./configuration.nix
./impermanence.nix
./networking.nix
./packages.nix
./kernel
./home
inputs.sops-nix.nixosModules.sops
inputs.microvm.nixosModules.host
(import ./vms)
inputs.disko.nixosModules.disko
inputs.impermanence.nixosModules.impermanence
inputs.home-manager.nixosModules.home-manager
{
home-manager.useGlobalPkgs = true;
home-manager.useUserPackages = true;
home-manager.sharedModules = [
inputs.sops-nix.homeManagerModules.sops
inputs.dms.homeModules.dank-material-shell
];
nixpkgs.overlays = [
(_: prev: {
niri-scratchpad = inputs.niri-scratchpad.packages.${prev.system}.default;
vimPlugins = prev.vimPlugins.extend (
f: p: {
neotest = p.neotest.overrideAttrs {
src = prev.fetchzip {
url = "https://github.com/archie-judd/neotest/archive/c8dd7597bb4182c0547d188e1dd5f684a4f01852.zip";
sha256 = "sha256-E/Heh+mAxvN5RaWqv1UJuHSA90c0evMKFkDD1BrpV7g=";
};
};
neotest-pest = p.neotest-pest.overrideAttrs (_: {
src = prev.fetchFromGitHub {
owner = "jradtilbrook";
repo = "neotest-pest";
rev = "e92131bde9c24e632c4ad76124f545d098127e60";
hash = "sha256-wy4nA7hxrX8JiDXkNzVHf6LUOVb8WankB600r7I8uSg=";
};
});
}
);
})
inputs.nur.overlays.default
(_: prev: {
niri = inputs.niri-branch.packages.${prev.system}.niri;
})
];
}
# ./networking/wireguard
];
};
};
}

32
home/bin/tmux-sessionizer Executable file
View file

@ -0,0 +1,32 @@
#!/usr/bin/env bash
if [[ $# -eq 1 ]]; then
selected=$1
else
selected=$(
(
find ~/work -mindepth 1 -maxdepth 1 -type d
find ~/dev -mindepth 2 -maxdepth 2 -type d
find ~/dev/icefox/php -mindepth 1 -maxdepth 1 -type d
) | fzf
)
fi
if [[ -z $selected ]]; then
exit 0
fi
selected_name=$(basename "$selected" | tr . _)
tmux_running=$(pgrep tmux)
if [[ -z $TMUX ]] && [[ -z $tmux_running ]]; then
tmux new-session -s "$selected_name" -c "$selected"
exit 0
fi
if ! tmux has-session -t="$selected_name" 2> /dev/null; then
tmux new-session -ds "$selected_name" -c "$selected"
fi
tmux switch-client -t "$selected_name"

3
home/default.nix Normal file
View file

@ -0,0 +1,3 @@
{ ... }:
{
}

0
home/files/lf/lfrc Normal file
View file

416
home/nvim/default.nix Normal file
View file

@ -0,0 +1,416 @@
{
config,
pkgs,
lib,
...
}:
with lib;
let
cfg = config.custom.neovim;
in
{
options.custom.neovim = {
enable = mkEnableOption "Custom Neovim";
colorscheme = mkOption {
type = types.str;
default = "unokai";
};
hostname = mkOption {
type = types.str;
};
};
config = mkIf cfg.enable {
programs.neovim = {
enable = true;
defaultEditor = true;
viAlias = true;
vimAlias = false;
vimdiffAlias = true;
plugins = with pkgs.vimPlugins; [
{
plugin = auto-session;
type = "lua";
config = ''
require("auto-session").setup({
cwd_change_handling = true,
suppressed_dirs = { "$HOME", "/etc/nixos", "$HOME/tmp" },
})
'';
}
{
plugin = blink-cmp;
type = "lua";
config = ''
require("blink.cmp").setup({
completion = {
documentation = {
auto_show = true
},
},
keymap = {
preset = "default",
["<C-y>"] = { "snippet_forward", "fallback" },
["<C-s>"] = { "select_and_accept", "snippet_forward", "fallback" },
["<C-S-y>"] = { "snippet_backward", "fallback" },
},
})
'';
}
{
plugin = comment-nvim;
type = "lua";
config = ''
require('Comment').setup()
'';
}
{
plugin = conform-nvim;
type = "lua";
config = ''
require("conform").setup({
formatters_by_ft = {
c = { "clang-format" },
cpp = { "clang-format" },
lua = { "stylua" },
javascript = { "prettierd" },
nix = { "nixfmt" },
python = { "black" },
php = { "php_cs_fixer" },
zig = { "zigfmt" },
css = { "prettierd" },
scss = { "prettierd" },
less = { "prettierd" },
blade = { "blade-formatter" },
go = { "gofmt" },
wgsl = { "wgsl_fmt" },
},
})
vim.api.nvim_create_autocmd("BufWritePre", {
pattern = "*",
callback = function(args)
require("conform").format({ bufnr = args.buf })
end,
})
vim.keymap.set("n", "<leader>rf", function()
require("conform").format({
lsp_fallback = true,
async = true,
timeout_ms = 500,
})
end, { desc = "conform format" })
'';
}
leap-nvim
{
plugin = lsp_lines-nvim;
type = "lua";
config = ''
require("lsp_lines").setup()
vim.keymap.set("n", "<localleader>i", require("lsp_lines").toggle, { desc = "Toggle LSP lines" })
'';
}
{
plugin = mini-icons;
type = "lua";
config = ''require("mini.icons").setup()'';
}
{
plugin = neotest;
type = "lua";
config = ''
require('neotest').setup({
output = {
open_on_run = true
},
adapters = {
require('neotest-pest'),
}
})
vim.keymap.set('n', '<localleader>pn', function() require('neotest').run.run() end, { desc = "test nearest" })
vim.keymap.set('n', '<localleader>pe', function() require('neotest').run.run(vim.fn.expand('%')) end, { desc = "test file" })
'';
}
{
plugin = neotest-pest;
type = "lua";
}
# {
# plugin = nvim-autopairs;
# type = "lua";
# config = ''
# require('nvim-autopairs').setup()
# '';
# }
{
plugin = nvim-dap;
type = "lua";
config = ''
local dap = require("dap")
dap.adapters.php = {
type = 'executable',
command = '${pkgs.nodejs}/bin/node',
args = { '${pkgs.vscode-extensions.xdebug.php-debug}/share/vscode/extensions/xdebug.php-debug/out/phpDebug.js' },
}
dap.configurations.php = {
{
type = 'php',
request = 'launch',
name = 'listen for xdebug',
port = 9003,
}
}
'';
}
{
plugin = nvim-dap-ui;
type = "lua";
config = ''
local dapui = require("dapui")
dapui.setup()
dap.listeners.before.attach.dapui_config = function()
dapui.open()
end
dap.listeners.before.launch.dapui_config = function()
dapui.open()
end
dap.listeners.before.event_terminated.dapui_config = function()
dapui.close()
end
dap.listeners.before.event_exited.dapui_config = function()
dapui.close()
end
'';
}
nvim-nio
{
plugin = nvim-treesitter.withAllGrammars;
type = "lua";
config = ''
local treesitter = require("nvim-treesitter")
treesitter.setup()
vim.api.nvim_create_autocmd('FileType', {
pattern = {
'c', 'lua', 'vim', 'vimdoc', 'query', 'elixir', 'heex', 'javascript', 'typescript',
'html', 'yaml', 'blade', 'php', 'scss', 'comment', 'cmake' , 'dockerfile', 'fish',
'fsharp', 'git_config', 'git_rebase', 'gitignore', 'glsl', 'go', 'gomod', 'graphql',
'haskell', 'hlsl', 'http', 'ini', 'javadoc', 'jq', 'jsdoc', 'json', 'json5', 'kitty',
'latex', 'markdown', 'nginx', 'nix', 'php', 'php_only', 'phpdoc', 'regex', 'rust', 'sql',
'ssh_config', 'tmux', 'vim', 'wgsl', 'yaml', 'zig', 'ols',
},
callback = function()
vim.treesitter.start()
end,
})
'';
}
{
plugin = nvim-treesitter-context;
type = "lua";
config = ''require("treesitter-context").setup()'';
}
# {
# plugin = nvim-treesitter-textobjectse
# type = "lua";
# config = ''
# vim.g.no_plugin_maps = true
# require("treesitter-textobjects").setup()
# '';
# }
nvim-lspconfig
nvim-nio
{
plugin = nvim-surround;
type = "lua";
config = ''require("nvim-surround").setup()'';
}
{
plugin = nvim-web-devicons;
type = "lua";
config = ''require("nvim-web-devicons").setup()'';
}
{
plugin = oil-nvim;
type = "lua";
config = ''
require("oil").setup()
vim.keymap.set("n", "<leader>o", "<cmd>Oil<cr>", { desc = "Oil" })
'';
}
{
plugin = opencode-nvim;
type = "lua";
config = ''
vim.o.autoread = true
-- Recommended/example keymaps.
vim.keymap.set({ "n", "x" }, "<C-a>", function() require("opencode").ask("@this: ", { submit = true }) end, { desc = "Ask opencode" })
vim.keymap.set({ "n", "x" }, "<C-x>", function() require("opencode").select() end, { desc = "Execute opencode action" })
vim.keymap.set({ "n", "t" }, "<C-.>", function() require("opencode").toggle() end, { desc = "Toggle opencode" })
vim.keymap.set({ "n", "x" }, "go", function() return require("opencode").operator("@this ") end, { desc = "Add range to opencode", expr = true })
vim.keymap.set("n", "goo", function() return require("opencode").operator("@this ") .. "_" end, { desc = "Add line to opencode", expr = true })
vim.keymap.set("n", "<S-C-u>", function() require("opencode").command("session.half.page.up") end, { desc = "Scroll opencode up" })
vim.keymap.set("n", "<S-C-d>", function() require("opencode").command("session.half.page.down") end, { desc = "Scroll opencode down" })
-- You may want these if you stick with the opinionated "<C-a>" and "<C-x>" above otherwise consider "<leader>o".
vim.keymap.set("n", "+", "<C-a>", { desc = "Increment under cursor", noremap = true })
vim.keymap.set("n", "-", "<C-x>", { desc = "Decrement under cursor", noremap = true })
'';
}
phpactor
plenary-nvim
{
plugin = render-markdown-nvim;
type = "lua";
config = ''require("render-markdown").setup()'';
}
{
plugin = snacks-nvim;
type = "lua";
config = ''
local snacks = require("snacks")
snacks.setup({
bigfile = {},
dim = {},
image = {},
indent = {},
lazygit = {},
picker = {
win = {
input = {
keys = {
["<Esc>"] = { "close", mode = { "n", "i" } },
},
},
},
},
quickfile = {},
notifier = {},
})
vim.keymap.set({ "n" }, "<localleader>t", snacks.picker.grep, { desc = "grep picker" })
vim.keymap.set({ "n" }, "<localleader>r", snacks.picker.buffers, { desc = "buffer picker" })
vim.keymap.set({ "n" }, "<localleader>s", snacks.picker.files, { desc = "file picker" })
vim.keymap.set({ "n" }, "<localleader>ln", snacks.picker.lsp_references, { desc = "lsp references" })
vim.keymap.set("n", "<localleader>le", snacks.picker.lsp_implementations, { desc = "lsp implementations" })
-- vim.keymap.set("n", "<leader>lg", function() snacks.lazygit() end , { desc = "lazygit" })
'';
}
rose-pine
{
plugin = rustaceanvim;
type = "lua";
config = ''
vim.keymap.set("n", "<leader>da", function() vim.cmd.RustLsp('codeAction') end, { silent = true, buffer = bufnr })
vim.keymap.set("n", "K", function() vim.cmd.RustLsp({'hover', 'actions'}) end, { silent = true, buffer = bufnr })
'';
}
{
plugin = tabby-nvim;
type = "lua";
config = ''
require("tabby").setup({
preset = "active_wins_at_tail",
options = {
theme = {
fill = "TabLineFill",
head = "TabLine",
current_tab = "TabLineSel",
tab = "TabLine",
win = "TabLine",
tail = "TabLine",
},
nerdfont = true,
lualine_theme = nil,
tab_name = {
name_fallback = function(tabid)
return tabid
end,
},
buf_name = { mode = "unique", },
},
})
vim.keymap.set("n", "<leader>to", "<cmd>Tabby jump_to_tab<cr>", { desc = "Jump to tab" })
vim.keymap.set("n", "<leader>tf", "<cmd>Tabby pick_window<cr>", { desc = "Search tab" })
'';
}
{
plugin = ts-autotag-nvim;
type = "lua";
config = ''
require("ts-autotag").setup()
'';
}
# {
# plugin = toggleterm-nvim;
# type = "lua";
# config = ''
# require("toggleterm").setup()
# vim.keymap.set("n", "<leader>nt", "<cmd>ToggleTerm size=120 direction=tab name=ttermh<cr>", { desc = "Toggle tterm tab" })
# vim.keymap.set("n", "<leader>ns", "<cmd>ToggleTerm direction=vertical name=ttermv<cr>", { desc = "Toggle tterm vertical" })
# vim.keymap.set("n", "<C-n>", "<cmd>ToggleTerm direction=float name=ttermf<cr>", { desc = "Toggle tterm float" })
# '';
# }
{
plugin = trouble-nvim;
type = "lua";
config = ''
require("trouble").setup({})
vim.keymap.set("n", "<localleader>id", "<cmd>Trouble diagnostics toggle<cr>", { desc = "Trouble project" })
-- vim.keymap.set("n", "<localleader>io", "<cmd>Trouble diagnostics toggle filter.buf=0<cr>", { desc = "Trouble buffer" })
'';
}
{
plugin = undotree;
type = "lua";
config = ''vim.keymap.set("n", "<leader>u", vim.cmd.UndotreeToggle, { desc = "undotree" })'';
}
vim-repeat
{
plugin = vimtex;
type = "lua";
config = ''
vim.g.vimtex_view_method = "zathura"
vim.g.vimtex_compiler_method = "latexmk"
'';
}
{
plugin = which-key-nvim;
type = "lua";
config = ''
local wk = require("which-key")
wk.setup({
preset = "helix",
win = { row = 0, col = 0.5 },
triggers = {
{ "<leader>", mode = { "n", "v" } },
{ "<localleader>", mode = { "n", "v" } },
}
})
wk.add({
{ "<leader>s", group = "Search..." },
{ "<leader>l", group = "Launch..." },
{ "<leader>t", group = "Tab..." },
{ "<leader>g", group = "Go..." },
{ "<leader>r", group = "Run..." },
{ "<leader>n", group = "term..." },
})
-- vim.keymap.set({"t"}, "<C-/>", wk.show, { desc = "Show which key in terminal mode" })
'';
}
vim-fugitive
];
extraConfig = ''
colorscheme ${cfg.colorscheme}
'';
extraLuaConfig = ''
${builtins.readFile ./settings.lua}
${builtins.replaceStrings [ "@HOSTNAME@" ] [ cfg.hostname ] (builtins.readFile ./plugins.lua)}
require("custom")
'';
};
};
}

161
home/nvim/plugins.lua Normal file
View file

@ -0,0 +1,161 @@
local hostname = "@HOSTNAME@"
local servers = {
clangd = {},
textlab = {},
lua_ls = {
settings = {
Lua = {
runtime = {
version = "LuaJIT",
},
diagnostics = {
globals = { "vim" },
},
workspace = {
library = vim.api.nvim_get_runtime_file("", true),
checkThirdParty = false,
},
telemetry = {
enable = false,
},
},
},
},
nixd = {
nixpkgs = {
expr = 'import (builtins.getFlake "/etc/nixos").inputs.nixpkgs {}',
},
formatting = { command = { "nixfmt" } },
options = {
nixos = {
expr = '(builtins.getFlake "/etc/nixos").nixosConfigurations.' .. hostname .. ".options",
},
home_manager = {
expr = '(builtins.getFlake "/etc/nixos").homeConfigurations.' .. hostname .. ".options",
},
},
},
phpactor = {},
zls = {
settings = {
zls = {
enable_build_on_save = true,
semantic_tokens = "partial",
},
},
},
css = {},
scss = {},
less = {},
blade = {},
html = { filetypes = { "html", "blade" } },
htmx = { filetypes = { "html", "blade" } },
gopls = {},
ols = {},
wgsl_analyzer = {},
}
for server, config in pairs(servers) do
vim.lsp.config(server, config)
vim.lsp.enable(server)
end
-- vim.api.nvim_create_autocmd("LspAttach", {
-- group = vim.api.nvim_create_augroup("UserConfigLsp", {}),
-- callback = function(ev)
vim.keymap.set("n", "gn", vim.lsp.buf.declaration, { desc = "go to declaration" })
vim.keymap.set("n", "ge", vim.lsp.buf.definition, { desc = "go to definition" })
-- vim.keymap.set("n", "gi", vim.lsp.buf.implementaion, { desc = "go to implementation" })
vim.keymap.set("n", "<leader>rr", vim.lsp.buf.rename, { desc = "lsp rename" })
vim.keymap.set("n", "<leader>ra", vim.lsp.buf.code_action, { desc = "code action" })
vim.keymap.set({ "n", "i" }, "<C-k>", vim.lsp.buf.signature_help, { desc = "signature help" })
vim.keymap.set("n", "gr", vim.lsp.buf.references, { desc = "references" })
-- end,
-- })
local leap = require("leap")
leap.opts.preview = function(ch0, ch1, ch2)
return not (ch1:match("%s") or (ch0:match("%a") and ch1:match("%a") and ch2:match("%a")))
end
leap.opts.equivalence_classes = {
" \t\r\n",
"([{",
")]}",
"'\"`",
}
vim.api.nvim_set_hl(0, "LeapBackdrop", { link = "Comment" })
do
-- Return an argument table for `leap()`, tailored for f/t-motions.
local function as_ft(key_specific_args)
local common_args = {
inputlen = 1,
inclusive = true,
-- To limit search scope to the current line:
-- pattern = function (pat) return '\\%.l'..pat end,
opts = {
labels = "", -- force autojump
safe_labels = vim.fn.mode(1):match("[no]") and "" or nil, -- [1]
},
}
return vim.tbl_deep_extend("keep", common_args, key_specific_args)
end
local clever = require("leap.user").with_traversal_keys -- [2]
local clever_f = clever("f", "F")
local clever_t = clever("t", "T")
for key, key_specific_args in pairs({
f = { opts = clever_f },
F = { backward = true, opts = clever_f },
t = { offset = -1, opts = clever_t },
T = { backward = true, offset = 1, opts = clever_t },
}) do
vim.keymap.set({ "n", "x", "o" }, key, function()
require("leap").leap(as_ft(key_specific_args))
end)
end
end
vim.api.nvim_create_autocmd("CmdlineLeave", {
group = vim.api.nvim_create_augroup("LeapOnSearch", {}),
callback = function()
local ev = vim.v.event
local is_search_cmd = (ev.cmdtype == "/") or (ev.cmdtype == "?")
local cnt = vim.fn.searchcount().total
if is_search_cmd and not ev.abort and (cnt > 1) then
-- Allow CmdLineLeave-related chores to be completed before
-- invoking Leap.
vim.schedule(function()
-- We want "safe" labels, but no auto-jump (as the search
-- command already does that), so just use `safe_labels`
-- as `labels`, with n/N removed.
local labels = require("leap").opts.safe_labels:gsub("[nN]", "")
-- For `pattern` search, we never need to adjust conceallevel
-- (no user input). We cannot merge `nil` from a table, but
-- using the option's current value has the same effect.
local vim_opts = { ["wo.conceallevel"] = vim.wo.conceallevel }
require("leap").leap({
pattern = vim.fn.getreg("/"), -- last search pattern
windows = { vim.fn.win_getid() },
opts = { safe_labels = "", labels = labels, vim_opts = vim_opts },
})
end)
end
end,
})
vim.keymap.set({ "n", "x", "o" }, "s", "<Plug>(leap)", { desc = "leap" })
vim.keymap.set({ "n", "x", "o" }, "S", "<Plug>(leap-from-window)", { desc = "leap across window" })
require("neotest").setup({
adapters = {
require("neotest-pest"),
},
})
vim.keymap.set("n", "<localleader>pn", function()
require("neotest").run.run()
end, { desc = "test nearest" })
vim.keymap.set("n", "<localleader>pe", function()
require("neotest").run.run(vim.fn.expand("%"))
end, { desc = "test file" })

147
home/nvim/settings.lua Normal file
View file

@ -0,0 +1,147 @@
vim.opt.number = true
vim.opt.relativenumber = true
vim.opt.cursorline = true
vim.opt.wrap = false
vim.opt.scrolloff = 10
vim.opt.sidescrolloff = 8
vim.opt.tabstop = 4
vim.opt.shiftwidth = 4
vim.opt.softtabstop = 4
vim.opt.expandtab = true
vim.opt.smartindent = true
vim.opt.autoindent = true
vim.opt.ignorecase = true
vim.opt.smartcase = true
vim.opt.hlsearch = false
vim.opt.incsearch = true
vim.opt.termguicolors = true
vim.opt.signcolumn = "yes"
vim.opt.colorcolumn = "120"
vim.opt.showmatch = false
--- vim.opt.matchtime = 2
vim.opt.conceallevel = 0
vim.opt.concealcursor = ""
vim.opt.synmaxcol = 1000
vim.opt.isfname:append("@-@")
vim.opt.backup = false
vim.opt.writebackup = false
vim.opt.swapfile = false
vim.opt.undofile = true
vim.opt.undodir = vim.fn.expand("~/.cache/vim/undodir")
vim.opt.updatetime = 100
vim.opt.timeoutlen = 500
vim.opt.ttimeoutlen = 0
vim.opt.autoread = true
vim.opt.autowrite = false
vim.opt.hidden = true
--- vim.opt.errorbells = false
vim.opt.backspace = "indent,eol,start"
vim.opt.autochdir = false
vim.opt.path:append("**")
vim.opt.selection = "exclusive"
vim.opt.mouse = "a"
vim.opt.modifiable = true
vim.opt.encoding = "UTF-8"
-- Split behavior
vim.opt.splitbelow = true
vim.opt.splitright = true
vim.g.mapleader = " "
vim.g.maplocalleader = vim.api.nvim_replace_termcodes("<BS>", false, false, true)
vim.o.exrc = true
vim.o.showtabline = 2
vim.cmd("set title")
vim.cmd("set ic")
-- Delete without yanking
vim.keymap.set({ "n", "v" }, "<localleader>d", '"_d', { desc = "Delete without yanking" })
vim.keymap.set({ "n", "v" }, "<C-c>", '"+y', { desc = "Copy to clipboard" })
vim.keymap.set("x", "<localleader>p", '"_dP', { desc = "Replace without yanking" })
-- Wayland clipboard mappings
vim.keymap.set({ "n", "v" }, "<leader>y", '"+y', { desc = "Yank to system clipboard" })
vim.keymap.set("n", "<leader>Y", '"+Y', { desc = "Yank line to system clipboard" })
vim.keymap.set({ "n", "v" }, "<leader>p", '"+p', { desc = "Paste from system clipboard" })
vim.keymap.set({ "n", "v" }, "<leader>P", '"+P', { desc = "Paste from system clipboard before cursor" })
-- Better J behavior
vim.keymap.set("n", "J", "mzJ`z", { desc = "Join lines and keep cursor position" })
--- center when jumping
vim.keymap.set("n", "n", "nzzzv", { desc = "Next search result (centered)" })
vim.keymap.set("n", "N", "Nzzzv", { desc = "Previous search result (centered)" })
vim.keymap.set("n", "<C-d>", "<C-d>zz", { desc = "Half page down (centered)" })
vim.keymap.set("n", "<C-u>", "<C-u>zz", { desc = "Half page up (centered)" })
--- window navigation
vim.keymap.set("n", "<C-w>Left", "<C-w>h", { desc = "Move to left window" })
vim.keymap.set("n", "<C-w>Right", "<C-w>l", { desc = "Move to right window" })
vim.keymap.set("n", "<C-w>Top", "<C-w>k", { desc = "Move to top window" })
vim.keymap.set("n", "<C-w>Down", "<C-w>j", { desc = "Move to bottom window" })
vim.keymap.set("t", "<S-Esc>", "<C-\\><C-n>", { desc = "Exit terminal mode" })
vim.keymap.set("n", "<leader>tn", "<cmd>$tabnew<cr>", { desc = "create tab" })
vim.keymap.set("n", "<leader>n", function()
vim.cmd("tabnext 1")
end, { desc = "Go to tab 1" })
vim.keymap.set("n", "<leader>e", function()
vim.cmd("tabnext 2")
end, { desc = "Go to tab 2" })
vim.keymap.set("n", "<leader>i", function()
vim.cmd("tabnext 3")
end, { desc = "Go to tab 3" })
vim.keymap.set("n", "<leader>a", function()
vim.cmd("tabnext 4")
end, { desc = "Go to tab 4" })
vim.keymap.set({ "n", "t" }, "<C-H>", function()
vim.cmd("tabnext #")
end, { desc = "Go to previous tab" })
vim.keymap.set({ "n", "t" }, "<C-Space>", "<C-w>p", { desc = "Go to previous pane" })
vim.keymap.set("n", "<localleader>v", "<cmd>vsplit<cr>", { desc = "split (vertical line)" })
vim.keymap.set("n", "<leader>h", "<cmd>split<cr>", { desc = "split (horizontal line)" })
vim.keymap.set("n", "<localleader><localleader>", "<cmd>w<cr>", { desc = "save buffer" })
vim.diagnostic.config({
virtual_text = false,
virtual_lines = true,
signs = true,
underline = true,
update_in_insert = false,
severity_sort = true,
})
-- Highlight yanked text
vim.api.nvim_create_autocmd("TextYankPost", {
group = augroup,
callback = function()
vim.highlight.on_yank()
end,
})
--- return to last edit position when opening files
vim.api.nvim_create_autocmd("BufReadPost", {
group = augroup,
callback = function()
local mark = vim.api.nvim_buf_get_mark(0, '"')
local lcount = vim.api.nvim_buf_line_count(0)
if mark[1] > 0 and mark[1] <= lcount then
pcall(vim.api.nvim_win_set_cursor, 0, mark)
end
end,
})
--- command line completion
vim.opt.wildmenu = true
vim.opt.wildmode = "longest:full,full"
vim.opt.wildignore:append({ "*.o", "*.obj", "*.pyc", "*.class", "*.jar", "*.lock" })

91
home/root.nix Normal file
View file

@ -0,0 +1,91 @@
{ hostname, ... }:
{
home-manager.users.root =
{ config, ... }:
{
imports = [ ./nvim ];
home.username = "root";
home.homeDirectory = "/root";
home.stateVersion = "25.11";
home.enableNixpkgsReleaseCheck = false;
home.file."/.ssh/desktop.pub".text =
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILquARrJ3Vyh5z6aeVoiYrkLpgiMts+V/JzFEvs3Cnth root@icefox.sh";
xdg.userDirs = {
enable = false;
extraConfig = {
XDG_CACHE_HOME = "${config.home.homeDirectory}/.cache";
};
};
programs = {
ssh = {
enable = true;
enableDefaultConfig = false;
matchBlocks = {
"icefox.sh" = {
user = "git";
identityFile = "/root/.ssh/desktop";
};
};
};
delta = {
enable = true;
options = {
navigate = true;
line-numbers = true;
side-by-side = true;
};
enableGitIntegration = true;
};
git = {
enable = true;
lfs.enable = true;
settings = {
user = {
email = "root@icefox.sh";
name = "root";
};
gpg.format = "ssh";
user.signingkey = "${config.home.homeDirectory}/.ssh/desktop.pub";
commit.gpgsign = true;
tag.gpgsign = true;
core = {
editor = "nvim";
whitespace = "fix,only-indent-error,trailing-space,space-before-tab";
quotepath = false;
};
diff = {
algorithm = "histogram";
renames = "copies";
};
merge = {
conflictstyle = "zdiff3";
};
init = {
defaultBranch = "master";
};
push = {
autoSetupRemote = true;
default = "current";
};
pull = {
rebase = true;
};
fetch = {
prune = true;
};
help = {
autocorrect = "prompt";
};
};
};
};
custom.neovim = {
enable = true;
hostname = hostname;
colorscheme = "unokai";
};
};
}

200
home/tmux.nix Normal file
View file

@ -0,0 +1,200 @@
{
config,
pkgs,
lib,
...
}:
with lib;
let
cfg = config.custom.tmux;
whichKeyConfig = pkgs.writeText "tmux-which-key-config.yaml" (
lib.generators.toYAML { } {
command_alias_start_index = 200;
keybindings = {
prefix_table = "Space";
};
title = {
style = "align=centre,bold";
prefix = "tmux";
prefix_style = "fg=green,align=centre,bold";
};
custom_variables = { };
macros = [ ];
position = {
x = "R";
y = "P";
};
items = [
{
name = "Window 1";
key = "n";
command = "select-window -t 1";
}
{
name = "Window 2";
key = "e";
command = "select-window -t 2";
}
{
name = "Window 3";
key = "i";
command = "select-window -t 3";
}
{
name = "Window 4";
key = "a";
command = "select-window -t 4";
}
{
name = "Next window";
key = "Enter";
command = "next-window";
transient = true;
}
{
name = "Last window";
key = "Space";
command = "last-window";
}
{
separator = true;
}
{
name = "Copy mode";
key = "BSpace";
command = "copy-mode";
}
{
separator = true;
}
{
name = "New window";
key = "l";
command = "new-window";
}
{
name = "New pane (vertical line)";
key = ",";
command = "splitw -h -c #{pane_current_path}";
}
{
name = "Split (horizontal line)";
key = "h";
command = "splitw -v -c #{pane_current_path}";
}
{
separator = true;
}
{
name = "Sessions";
key = "m";
menu = [
{
name = "sessionizer";
key = "Space";
command = "run-shell \"tmux neww tmux-sessionizer\"";
}
{
name = "last-session";
key = "Enter";
command = "run-shell \"tmux switchc -l\"";
}
];
}
{
separator = true;
}
{
name = "Kill";
key = "k";
menu = [
{
name = "Kill window";
key = "w";
command = "kill-window";
}
{
name = "Kill pane";
key = "p";
command = "kill-pane";
}
];
}
];
}
);
in
{
options.custom.tmux = {
enable = mkEnableOption "Custom Tmux";
};
config = mkIf cfg.enable {
programs.tmux = {
enable = true;
baseIndex = 1;
keyMode = "vi";
mouse = true;
plugins = with pkgs.tmuxPlugins; [
sensible
yank
pain-control
# tmux-powerline
{
plugin = rose-pine;
extraConfig = ''
set -g @rose_pine_variant 'main' # Options are 'main', 'moon' or 'dawn'
set -g @rose_pine_host 'on'
set -g @rose_pine_directory 'on'
set -g @rose_pine_bar_bg_disable 'on'
'';
}
{
plugin = pkgs.tmuxPlugins.tmux-which-key.overrideAttrs (old: {
postInstall = (old.postInstall or "") + ''
echo "[tmux-which-key] Pre-generating configuration at build time..."
${lib.getExe (pkgs.python3.withPackages (ps: with ps; [ pyyaml ]))} \
$target/plugin/build.py \
${whichKeyConfig} \
$target/init.tmux
# Append a line to source the pre-generated config
echo 'tmux source-file "$root_dir/init.tmux"' >> $target/plugin.sh.tmux
'';
});
extraConfig = ''
set -g @tmux-which-key-xdg-enable 1;
set -g @tmux-which-key-disable-autobuild 1;
'';
}
{
plugin = resurrect;
extraConfig = "set -g @ressurect-strategy-nvim 'session'";
}
{
plugin = continuum;
extraConfig = ''
set -g @continuum-restore 'on'
set -g @continuum-save-interval '60'
'';
}
];
extraConfig = ''
set -g status-position top
set -g focus-events on
set -g allow-passthrough on
set -s extended-keys on
set -g default-terminal "xterm-ghostty"
set -as terminal-features ",xterm-ghostty:extkeys"
bind -n M-, run-shell "tmux neww tmux-sessionizer"
bind -n M-/ run-shell "tmux switch-client -t default"
bind -n M-. run-shell "tmux switchc -l"
bind-key -n Home send Escape "OH"
bind-key -n End send Escape "OF"
'';
};
};
}

855
home/user.nix Normal file
View file

@ -0,0 +1,855 @@
{ hostname, ... }:
{
home-manager.users.user =
{
config,
pkgs,
lib,
...
}:
{
home.username = "user";
home.homeDirectory = "/home/user";
home.stateVersion = "25.11";
home.sessionVariables = {
HOME = "/home/user";
};
imports = [
./nvim
./tmux.nix
];
sops.defaultSopsFile = ../secrets/home.yaml;
sops.age.keyFile = "/.persist/${config.home.homeDirectory}/.config/sops/age/keys.txt";
sops.secrets."user/ssh/desktop" = {
path = "${config.home.homeDirectory}/.ssh/desktop";
mode = "0600";
};
home.file."/.ssh/desktop.pub".text =
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILABd/iSJ4gn/ystDqNxLJTG0n0z5VIC9YXlmdUfOhHf desktop@icefox.sh";
sops.secrets."user/ssh/legacy_ed25519" = {
path = "${config.home.homeDirectory}/.ssh/legacy_ed25519";
mode = "0600";
};
home.file."/.ssh/legacy_ed25519.pub".text =
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILkchxtY21PzSLHJ5SoYPrl03+NRzRqznbdCqNyGuOX/ master@michizure.net";
dconf.settings = {
"org/gnome/desktop/interface" = {
text-scaling-factor = 1.0;
};
};
# xresources.properties = {
# "Xcursor.size" = 14;
# # "Xft.dpi" = 144;
# "Xft.autohint" = 0;
# "Xft.lcdfilter" = "lcddefault";
# "Xft.hintstyle" = "hintfull";
# "Xft.hinting" = 1;
# "Xft.antialias" = 1;
# "Xft.rgba" = "rgb";
# };
# systemd.user.services.xrdb-configure = {
# Unit = {
# Description = "Load Xresources";
# };
# Intall = {
# WantedBy = [ "graphical-session.target" ];
# };
# Service = {
# ExecStart = "${pkgs.xrdb}/bin/xrdb -merge ${config.home.homeDirectory}/.Xresources";
# Type = "oneshot";
# };
# };
sops.secrets."user/gpg/legacy_fnzr" = { };
home.activation.importGpgKey = config.lib.dag.entryAfter [ "writeBoundary" ] ''
if [[ -f "${config.sops.secrets."user/gpg/legacy_fnzr".path}" ]]; then
${pkgs.gnupg}/bin/gpg --batch --import "${
config.sops.secrets."user/gpg/legacy_fnzr".path
}" || true
echo "YOUR_KEY_FINGERPRINT:6:" | ${pkgs.gnupg}/bin/gpg --import-ownertrust || true
fi
'';
xdg.configFile."mimeapps.list".force = true;
xdg.configFile."containers/containers.conf".text = ''
[engine]
compose_warning_logs=false
events_logger="file"
[containers]
log_driver="k8s-file"
'';
xdg.configFile."lazygit/config.yml".text = lib.generators.toYAML { } {
gui = {
theme = {
selectedLineBgColor = [ "reverse" ];
};
};
};
# xdg.configFile."opencode/opencode.json".text = builtins.toJSON {
# "$schema" = "https://opencode.ai/config.json";
# plugin = [ "opencode-antigravity-auth@latest" ];
# provider = {
# google = {
# models = {
# antigravity-gemini-3-pro = {
# name = "Gemini 3 Pro (Antigravity)";
# limit = {
# context = 1048576;
# output = 65535;
# };
# modalities = {
# input = [
# "text"
# "image"
# "pdf"
# ];
# output = [ "text" ];
# };
# variants = {
# low = {
# thinkingLevel = "low";
# };
# high = {
# thinkingLevel = "high";
# };
# };
# };
# antigravity-gemini-3-flash = {
# name = "Gemini 3 Flash (Antigravity)";
# limit = {
# context = 1048576;
# output = 65536;
# };
# modalities = {
# input = [
# "text"
# "image"
# "pdf"
# ];
# output = [ "text" ];
# };
# variants = {
# minimal = {
# thinkingLevel = "minimal";
# };
# low = {
# thinkingLevel = "low";
# };
# medium = {
# thinkingLevel = "medium";
# };
# high = {
# thinkingLevel = "high";
# };
# };
# };
# antigravity-claude-sonnet-4-5 = {
# name = "Claude Sonnet 4.5 (Antigravity)";
# limit = {
# context = 200000;
# output = 64000;
# };
# modalities = {
# input = [
# "text"
# "image"
# "pdf"
# ];
# output = [ "text" ];
# };
# };
# antigravity-claude-sonnet-4-5-thinking = {
# name = "Claude Sonnet 4.5 Thinking (Antigravity)";
# limit = {
# context = 200000;
# output = 64000;
# };
# modalities = {
# input = [
# "text"
# "image"
# "pdf"
# ];
# output = [ "text" ];
# };
# variants = {
# low = {
# thinkingConfig = {
# thinkingBudget = 8192;
# };
# };
# max = {
# thinkingConfig = {
# thinkingBudget = 32768;
# };
# };
# };
# };
# antigravity-claude-opus-4-5-thinking = {
# name = "Claude Opus 4.5 Thinking (Antigravity)";
# limit = {
# context = 200000;
# output = 64000;
# };
# modalities = {
# input = [
# "text"
# "image"
# "pdf"
# ];
# output = [ "text" ];
# };
# variants = {
# low = {
# thinkingConfig = {
# thinkingBudget = 8192;
# };
# };
# max = {
# thinkingConfig = {
# thinkingBudget = 32768;
# };
# };
# };
# };
# antigravity-claude-opus-4-6-thinking = {
# name = "Claude Opus 4.6 Thinking (Antigravity)";
# limit = {
# context = 200000;
# output = 64000;
# };
# modalities = {
# input = [
# "text"
# "image"
# "pdf"
# ];
# output = [ "text" ];
# };
# variants = {
# low = {
# thinkingConfig = {
# thinkingBudget = 8192;
# };
# };
# max = {
# thinkingConfig = {
# thinkingBudget = 32768;
# };
# };
# };
# };
# "gemini-2.5-flash" = {
# name = "Gemini 2.5 Flash (Gemini CLI)";
# limit = {
# context = 1048576;
# output = 65536;
# };
# modalities = {
# input = [
# "text"
# "image"
# "pdf"
# ];
# output = [ "text" ];
# };
# };
# "gemini-2.5-pro" = {
# name = "Gemini 2.5 Pro (Gemini CLI)";
# limit = {
# context = 1048576;
# output = 65536;
# };
# modalities = {
# input = [
# "text"
# "image"
# "pdf"
# ];
# output = [ "text" ];
# };
# };
# gemini-3-flash-preview = {
# name = "Gemini 3 Flash Preview (Gemini CLI)";
# limit = {
# context = 1048576;
# output = 65536;
# };
# modalities = {
# input = [
# "text"
# "image"
# "pdf"
# ];
# output = [ "text" ];
# };
# };
# gemini-3-pro-preview = {
# name = "Gemini 3 Pro Preview (Gemini CLI)";
# limit = {
# context = 1048576;
# output = 65535;
# };
# modalities = {
# input = [
# "text"
# "image"
# "pdf"
# ];
# output = [ "text" ];
# };
# };
# };
# };
# };
# };
xdg.desktopEntries = {
google-chrome = {
name = "Google Chrome";
genericName = "Web Browser";
exec = "/run/current-system/sw/bin/google-chrome-stable";
terminal = false;
icon = "google-chrome";
type = "Application";
categories = [
"Network"
"WebBrowser"
];
};
chromium-browser = {
name = "Chromium";
genericName = "Web Browser";
exec = "/run/current-system/sw/bin/chromium";
terminal = false;
icon = "chromium";
type = "Application";
categories = [
"Network"
"WebBrowser"
];
};
chromium-browser-sandbox = {
name = "Chromium (Sandbox)";
genericName = "Web Browser";
exec = "/run/current-system/sw/bin/chromium-sandbox --data-dir=/data/sandbox/chromium/data-dir";
terminal = false;
icon = "chromium";
type = "Application";
categories = [
"Network"
"WebBrowser"
];
};
};
xdg.mimeApps = {
enable = true;
defaultApplications = {
# text
"text/plain" = "nvim.desktop";
"text/markdown" = "nvim.desktop";
"text/x-python" = "nvim.desktop";
"text/x-shellscript" = "nvim.desktop";
"text/x-csrc" = "nvim.desktop";
"text/x-c++src" = "nvim.desktop";
"text/x-java" = "nvim.desktop";
"text/x-makefile" = "nvim.desktop";
"application/json" = "nvim.desktop";
"application/javascript" = "nvim.desktop";
"application/x-yaml" = "nvim.desktop";
"application/xml" = "nvim.desktop";
"text/*" = "nvim.desktop";
# browser
"text/html" = "firefox.desktop";
"x-scheme-handler/http" = "firefox.desktop";
"x-scheme-handler/https" = "firefox.desktop";
"x-scheme-handler/about" = "firefox.desktop";
"x-scheme-handler/unknown" = "firefox.desktop";
# swayimg
"image/jpeg" = "swayimg.desktop";
"image/jpg" = "swayimg.desktop";
"image/png" = "swayimg.desktop";
"image/gif" = "swayimg.desktop";
"image/webp" = "swayimg.desktop";
"image/bmp" = "swayimg.desktop";
"image/tiff" = "swayimg.desktop";
"image/svg+xml" = "swayimg.desktop";
"image/x-icon" = "swayimg.desktop";
"image/*" = "swayimg.desktop";
# pdf & readers
"application/pdf" = "org.pwmt.zathura.desktop";
"application/postscript" = "org.pwmt.zathura.desktop";
"image/vnd.djvu" = "org.pwmt.zathura.desktop";
"application/x-cbr" = "org.pwmt.zathura.desktop";
"application/x-cbz" = "org.pwmt.zathura.desktop";
"application/x-cb7" = "org.pwmt.zathura.desktop";
"application/x-cbt" = "org.pwmt.zathura.desktop";
"application/epub+zip" = "org.pwmt.zathura.desktop";
# video
"video/mp4" = "mpv.desktop";
"video/x-matroska" = "mpv.desktop";
"video/webm" = "mpv.desktop";
"video/mpeg" = "mpv.desktop";
"video/x-msvideo" = "mpv.desktop";
"video/quicktime" = "mpv.desktop";
"video/x-flv" = "mpv.desktop";
"video/3gpp" = "mpv.desktop";
"video/ogg" = "mpv.desktop";
"video/*" = "mpv.desktop";
# audio
"audio/mpeg" = "mpv.desktop";
"audio/mp4" = "mpv.desktop";
"audio/x-wav" = "mpv.desktop";
"audio/flac" = "mpv.desktop";
"audio/ogg" = "mpv.desktop";
"audio/x-vorbis+ogg" = "mpv.desktop";
"audio/x-opus+ogg" = "mpv.desktop";
"audio/aac" = "mpv.desktop";
"audio/x-m4a" = "mpv.desktop";
"audio/webm" = "mpv.desktop";
"audio/*" = "mpv.desktop";
};
};
xdg.userDirs = {
enable = true;
createDirectories = true;
download = "${config.home.homeDirectory}/downloads";
documents = "${config.home.homeDirectory}/documents";
desktop = "${config.home.homeDirectory}/desktop";
pictures = "${config.home.homeDirectory}/pictures";
music = "${config.home.homeDirectory}/music";
videos = "${config.home.homeDirectory}/videos";
templates = "${config.home.homeDirectory}";
publicShare = "${config.home.homeDirectory}";
extraConfig = {
SCREENSHOTS = "${config.home.homeDirectory}/pictures/screenshots";
XDG_CACHE_HOME = "${config.home.homeDirectory}/.cache";
};
};
programs = {
dank-material-shell.enable = true;
ssh = {
enable = true;
enableDefaultConfig = false;
matchBlocks = {
"*" = {
serverAliveInterval = 60;
serverAliveCountMax = 3;
};
"github.com" = {
identityFile = config.sops.secrets."user/ssh/legacy_ed25519".path;
};
"icefox.sh" = {
user = "git";
identityFile = config.sops.secrets."user/ssh/desktop".path;
};
};
};
delta = {
enable = true;
options = {
navigate = true;
line-numbers = true;
side-by-side = true;
};
enableGitIntegration = true;
};
git = {
enable = true;
lfs.enable = true;
signing = {
key = "${config.home.homeDirectory}/.ssh/desktop.pub";
signByDefault = true;
};
includes = [
{
condition = "gitdir:~/work/";
contents = {
user = {
name = "felipematos";
email = "5471818+fnzr@users.noreply.github.com";
};
};
}
];
settings = {
user = {
email = "felipe@icefox.sh";
name = "icefox";
signingkey = "${config.home.homeDirectory}/.ssh/desktop.pub";
};
gpg.format = "ssh";
commit.gpgsign = true;
tag.gpgsign = true;
core = {
editor = "nvim";
whitespace = "fix,only-indent-error,trailing-space,space-before-tab";
quotepath = false;
};
diff = {
algorithm = "histogram";
renames = "copies";
tool = "nvim";
};
difftool = {
prompt = false;
nvim.cmd = "nvim -d $LOCAL $REMOTE";
};
merge = {
conflictstyle = "zdiff3";
tool = "nvim";
};
mergetool = {
prompt = false;
keepBackup = false;
nvim.cmd = "nvim -d $LOCAL $REMOTE $MERGED -c 'wincmd w' -c 'wincmd J'";
};
init = {
defaultBranch = "master";
};
push = {
autoSetupRemote = true;
default = "current";
};
pull = {
rebase = true;
};
fetch = {
prune = true;
};
help = {
autocorrect = "prompt";
};
};
};
};
home.packages = with pkgs; [
xrdb
(writeShellApplication {
name = "tmux-sessionizer";
runtimeInputs = [
tmux
fzf
];
text = builtins.readFile ./bin/tmux-sessionizer;
})
(writeShellScriptBin "opencode" ''
ssh -t user@192.168.77.2 "
cd $(pwd) 2>/dev/null || cd \$(mktemp -d)
opencode $*
"
'')
(writeShellScriptBin "claude" ''
ssh -t user@192.168.77.2 "
cd $(pwd) 2>/dev/null || cd \$(mktemp -d)
claude $*
"
'')
];
custom.tmux.enable = true;
custom.neovim = {
enable = true;
colorscheme = "rose-pine-moon";
hostname = hostname;
};
programs.fish = {
enable = true;
plugins = [
{
name = "puffer";
src = pkgs.fetchFromGitHub {
owner = "nickeb96";
repo = "puffer-fish";
rev = "83174b0";
sha256 = "sha256-Dhx5+XRxJvlhdnFyimNxFyFiASrGU4ZwyefsDwtKnSg=";
};
}
];
interactiveShellInit = ''
set fish_greeting
bind ctrl-space ""
'';
};
programs.starship = {
enable = true;
};
programs.zoxide = {
enable = true;
enableFishIntegration = true;
};
programs.ghostty = {
enable = true;
settings = {
theme = "Rose Pine Moon";
font-family = [
"MonaspiceNe Nerd Font Mono"
"Fire Code Symbol"
];
font-size = "10";
font-feature = "+calt, +liga, +dlig, +ss01, +ss02, +ss03, +ss04, +ss05, +ss06, +ss07, +ss08, +ss09, +ss10";
keybind = [
"shift+escape=unbind"
# "ctrl+c=copy_to_clipboard"
"ctrl+v=paste_from_clipboard"
];
};
enableFishIntegration = true;
systemd.enable = true;
};
programs.firefox = {
enable = true;
package = pkgs.firefox;
nativeMessagingHosts = [
pkgs.browserpass
pkgs.tridactyl-native
];
profiles.default = {
id = 0;
name = "default";
isDefault = true;
containersForce = true;
userChrome = ''
#TabsToolbar {
visibility: collapse;
}
'';
settings = {
"toolkit.legacyUserProfileCustomizations.stylesheets" = true;
};
# settings = {
# "tabopencontaineraware" = true;
# };
containers = {
personal = {
id = 1;
color = "blue";
icon = "fingerprint";
};
google = {
id = 2;
color = "pink";
icon = "fence";
};
london = {
id = 3;
color = "orange";
icon = "tree";
};
research = {
id = 4;
color = "green";
icon = "tree";
};
chill = {
id = 5;
color = "turquoise";
icon = "chill";
};
work = {
id = 6;
color = "red";
icon = "briefcase";
};
};
search = {
force = true;
default = "ddg";
order = [
"ddg"
"Kagi"
"MyNixOS"
"PHP"
];
engines = {
"PHP" = {
urls = [
{
template = "https://www.php.net/search.php";
params = [
{
name = "pattern";
value = "{searchTerms}";
}
];
}
];
definedAliases = [ "php" ];
};
"MyNixOS" = {
urls = [
{
template = "https://mynixos.com/search";
params = [
{
name = "q";
value = "{searchTerms}";
}
];
}
];
definedAliases = [ "nix" ];
};
"Kagi" = {
urls = [ { template = "https://kagi.com/search?q={searchTerms}"; } ];
icon = "https://kagi.com/favicon.ico";
updateInterval = 24 * 60 * 60 * 1000;
definedAliases = [ "kg" ];
};
"ddg" = {
metaData.alias = "dd";
};
"google".metaData.hidden = true;
"bing".metaData.hidden = true;
"amazondotcom-us".metaData.hidden = true;
"ebay".metaData.hidden = true;
};
};
# settings = {
# "layout.css.prefers-color-scheme.content-override" = 2;
# "privacy.resistFingerprinting" = true;
# "privacy.resistFingerprinting.exemptions" = "prefers-color-scheme";
# "browser.theme.dark-private-windows" = true;
# };
extensions = {
packages = with pkgs.nur.repos.rycee.firefox-addons; [
ublock-origin
tridactyl
gopass-bridge
multi-account-containers
foxyproxy-standard
];
};
};
};
programs.librewolf = {
enable = true;
package = pkgs.librewolf;
nativeMessagingHosts = [
pkgs.browserpass
pkgs.tridactyl-native
];
profiles.default = {
id = 0;
name = "default";
isDefault = true;
containersForce = true;
settings = {
"tabopencontaineraware" = true;
};
containers = {
personal = {
id = 1;
color = "blue";
icon = "fingerprint";
};
google = {
id = 2;
color = "pink";
icon = "fence";
};
london = {
id = 3;
color = "orange";
icon = "briefcase";
};
research = {
id = 4;
color = "green";
icon = "tree";
};
chill = {
id = 5;
color = "turquoise";
icon = "chill";
};
};
search = {
force = true;
default = "ddg";
order = [
"ddg"
"Kagi"
"NixOS"
];
engines = {
"Kagi" = {
urls = [ { template = "https://kagi.com/search?q={searchTerms}"; } ];
icon = "https://kagi.com/favicon.ico";
updateInterval = 24 * 60 * 60 * 1000;
definedAliases = [ "kg" ];
};
"nx" = {
urls = [ { template = "https://mynixos.com/search?q={searchTerms}"; } ];
icon = "https://mynixos.com/favicon.ico";
updateInterval = 24 * 60 * 60 * 1000;
definedAliases = [ "nx" ];
};
"ddg" = {
metaData.alias = "dd";
};
"google".metaData.hidden = true;
"bing".metaData.hidden = true;
"amazondotcom-us".metaData.hidden = true;
"ebay".metaData.hidden = true;
};
};
settings = {
"layout.css.prefers-color-scheme.content-override" = 2;
"privacy.resistFingerprinting" = false;
"privacy.resistFingerprinting.exemptions" = "prefers-color-scheme";
"browser.theme.dark-private-windows" = true;
};
extensions = {
packages = with pkgs.nur.repos.rycee.firefox-addons; [
ublock-origin
tridactyl
gopass-bridge
multi-account-containers
foxyproxy-standard
];
# force = true;
# settings."uBlock0@raymondhill.net".settings = {
# selectedFilterLists = [
# "ublock-filters"
# "ublock-badware"
# "ublock-privacy"
# "ublock-unbreak"
# "ublock-quick-fixes"
# ];
# };
};
};
};
};
}

53
impermanence.nix Normal file
View file

@ -0,0 +1,53 @@
{
environment.persistence."/.persist" = {
enable = true;
hideMounts = true;
directories = [
"/etc/nixos"
"/var/lib/nixos"
];
users.root = {
files = [
".config/sops/age/keys.txt"
];
};
files = [
#
];
users.user = {
files = [
".config/sops/age/keys.txt"
];
directories = [
".config/nvim"
".config/niri"
".config/ghostty"
".config/tmux"
".config/tmux-powerline"
".ssh"
".local/share/wallpapers"
];
};
};
environment.persistence."/.nobackup" = {
enable = true;
hideMounts = true;
directories = [
"/var/log"
"/var/cache"
"/var/lib/docker"
];
users.root = {
directories = [
".cache"
];
};
users.user = {
directories = [
".cache"
];
};
};
}

63
kernel/default.nix Normal file
View file

@ -0,0 +1,63 @@
{
lib,
pkgs,
...
}:
{
imports = [
./hardened.nix
./vfio.nix
./standard.nix
# ./apparmor.nix
];
custom.kernel.hardened.enable = true;
custom.kernel.vfio.enable = false;
custom.kernel.standard.enable = true;
# security.apparmor.enable = false;
specialisation.unhardened.configuration = {
custom.kernel.hardened.enable = lib.mkForce false;
# security.apparmor.enable = lib.mkForce false;
};
specialisation.vfio.configuration = {
custom.kernel.vfio.enable = lib.mkForce true;
custom.kernel.standard.enable = lib.mkForce false;
};
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
hardware.enableRedistributableFirmware = true;
hardware.cpu.amd.updateMicrocode = true;
security.rtkit.enable = true;
security.sudo.enable = false;
security.doas = {
enable = true;
extraRules = [
{
users = [ "user" ];
keepEnv = true;
persist = true;
}
{
users = [ "user" ];
runAs = "agent";
noPass = true;
keepEnv = false;
}
];
};
boot = {
loader = {
systemd-boot.enable = true;
efi.canTouchEfiVariables = true;
};
kernelPackages = pkgs.linuxPackages_zen;
kernelParams = [
"amd_iommu=on"
];
};
}

207
kernel/hardened.nix Normal file
View file

@ -0,0 +1,207 @@
{
lib,
config,
pkgs,
...
}:
let
cfg = config.custom.kernel.hardened;
in
{
options.custom.kernel.hardened = {
enable = lib.mkEnableOption "hardened kernel options";
};
config = lib.mkIf cfg.enable {
environment.systemPackages = [
pkgs.kernel-hardening-checker
pkgs.lynis
];
security = {
auditd.enable = false;
audit.enable = false;
audit.rules = [ "-a always,exit -F arch=b64 -S execve" ];
protectKernelImage = true;
lockKernelModules = false; # this breaks iptables, wireguard, and virtd
# force-enable the Page Table Isolation (PTI) Linux kernel feature
forcePageTableIsolation = true;
# User namespaces are required for sandboxing.
# this means you cannot set `"user.max_user_namespaces" = 0;` in sysctl
allowUserNamespaces = true;
# Disable unprivileged user namespaces, unless containers are enabled
unprivilegedUsernsClone = config.virtualisation.containers.enable;
allowSimultaneousMultithreading = true;
};
boot.kernel.sysctl = {
"fs.suid_dumpable" = 0;
# prevent pointer leaks
"kernel.kptr_restrict" = 2;
# restrict kernel log to CAP_SYSLOG capability
"kernel.dmesg_restrict" = 1;
# Note: certian container runtimes or browser sandboxes might rely on the following
# restrict eBPF to the CAP_BPF capability
"kernel.unprivileged_bpf_disabled" = 1;
# should be enabled along with bpf above
# "net.core.bpf_jit_harden" = 2;
# restrict loading TTY line disciplines to the CAP_SYS_MODULE
"dev.tty.ldisk_autoload" = 0;
# prevent exploit of use-after-free flaws
"vm.unprivileged_userfaultfd" = 0;
# kexec is used to boot another kernel during runtime and can be abused
"kernel.kexec_load_disabled" = 1;
# Kernel self-protection
# SysRq exposes a lot of potentially dangerous debugging functionality to unprivileged users
# 4 makes it so a user can only use the secure attention key. A value of 0 would disable completely
"kernel.sysrq" = 4;
# disable unprivileged user namespaces, Note: Docker, NH, and other apps may need this
"kernel.unprivileged_userns_clone" = 1;
# restrict all usage of performance events to the CAP_PERFMON capability
"kernel.perf_event_paranoid" = 3;
# Network
# protect against SYN flood attacks (denial of service attack)
"net.ipv4.tcp_syncookies" = 1;
# protection against TIME-WAIT assassination
"net.ipv4.tcp_rfc1337" = 1;
# enable source validation of packets received (prevents IP spoofing)
"net.ipv4.conf.default.rp_filter" = 1;
"net.ipv4.conf.all.rp_filter" = 1;
"net.ipv4.conf.all.accept_redirects" = 0;
"net.ipv4.conf.default.accept_redirects" = 0;
"net.ipv4.conf.all.secure_redirects" = 0;
"net.ipv4.conf.default.secure_redirects" = 0;
# Protect against IP spoofing
"net.ipv6.conf.all.accept_redirects" = 0;
"net.ipv6.conf.default.accept_redirects" = 0;
"net.ipv4.conf.all.send_redirects" = 0;
"net.ipv4.conf.default.send_redirects" = 0;
# prevent man-in-the-middle attacks
"net.ipv4.icmp_echo_ignore_all" = 1;
# ignore ICMP request, helps avoid Smurf attacks
"net.ipv4.conf.all.forwarding" = 0;
"net.ipv4.conf.default.accept_source_route" = 0;
"net.ipv4.conf.all.accept_source_route" = 0;
"net.ipv6.conf.all.accept_source_route" = 0;
"net.ipv6.conf.default.accept_source_route" = 0;
# Reverse path filtering causes the kernel to do source validation of
"net.ipv6.conf.all.forwarding" = 0;
"net.ipv6.conf.all.accept_ra" = 0;
"net.ipv6.conf.default.accept_ra" = 0;
## TCP hardening
# Prevent bogus ICMP errors from filling up logs.
"net.ipv4.icmp_ignore_bogus_error_responses" = 1;
# Disable TCP SACK
"net.ipv4.tcp_sack" = 0;
"net.ipv4.tcp_dsack" = 0;
"net.ipv4.tcp_fack" = 0;
# Userspace
# restrict usage of ptrace
"kernel.yama.ptrace_scope" = 2;
# ASLR memory protection (64-bit systems)
"vm.mmap_rnd_bits" = 32;
"vm.mmap_rnd_compat_bits" = 16;
# only permit symlinks to be followed when outside of a world-writable sticky directory
"fs.protected_symlinks" = 1;
"fs.protected_hardlinks" = 1;
# Prevent creating files in potentially attacker-controlled environments
"fs.protected_fifos" = 2;
"fs.protected_regular" = 2;
# Randomize memory
"kernel.randomize_va_space" = 2;
# Exec Shield (Stack protection)
"kernel.exec-shield" = 1;
## TCP optimization
# TCP Fast Open is a TCP extension that reduces network latency by packing
# data in the senders initial TCP SYN. Setting 3 = enable TCP Fast Open for
# both incoming and outgoing connections:
"net.ipv4.tcp_fastopen" = 3;
# Bufferbloat mitigations + slight improvement in throughput & latency
"net.ipv4.tcp_congestion_control" = "bbr";
"net.core.default_qdisc" = "cake";
};
boot.kernelParams = [
"audit=0"
# make it harder to influence slab cache layout
"slab_nomerge"
# enables zeroing of memory during allocation and free time
# helps mitigate use-after-free vulnerabilaties
"init_on_alloc=1"
"init_on_free=1"
# randomizes page allocator freelist, improving security by
# making page allocations less predictable
"page_alloc.shuffel=1"
# enables Kernel Page Table Isolation, which mitigates Meltdown and
# prevents some KASLR bypasses
"pti=on"
# randomizes the kernel stack offset on each syscall
# making attacks that rely on a deterministic stack layout difficult
"randomize_kstack_offset=on"
# disables vsyscalls, they've been replaced with vDSO
"vsyscall=none"
# disables debugfs, which exposes sensitive info about the kernel
"debugfs=off"
# certain exploits cause an "oops", this makes the kernel panic if an "oops" occurs
"oops=panic"
# only alows kernel modules that have been signed with a valid key to be loaded
# making it harder to load malicious kernel modules
# can make VirtualBox or Nvidia drivers unusable
#"module.sig_enforce=1"
# prevents user space code excalation
"lockdown=confidentiality"
# "rd.udev.log_level=3"
# "udev.log_priority=3"
];
boot.blacklistedKernelModules = [
# Obscure networking protocols
"dccp" # Datagram Congestion Control Protocol
"sctp" # Stream Control Transmission Protocol
"rds" # Reliable Datagram Sockets
"tipc" # Transparent Inter-Process Communication
"n-hdlc" # High-level Data Link Control
"ax25" # Amateur X.25
"netrom" # NetRom
"x25" # X.25
"rose"
"decnet"
"econet"
"af_802154" # IEEE 802.15.4
"ipx" # Internetwork Packet Exchange
"appletalk"
"psnap" # SubnetworkAccess Protocol
"p8023" # Novell raw IEE 802.3
"p8022" # IEE 802.3
"can" # Controller Area Network
"atm"
# Various rare filesystems
"cramfs"
"freevxfs"
"jffs2"
"hfs"
"hfsplus"
# "udf"
# "nfs" # Network File System
# "nfsv3"
# "nfsv4"
"gfs2" # Global File System 2
# vivid driver is only useful for testing purposes and has been the
# cause of privilege escalation vulnerabilities
"vivid"
];
};
}

55
kernel/standard.nix Normal file
View file

@ -0,0 +1,55 @@
{
config,
lib,
...
}:
let
cfg = config.custom.kernel.standard;
in
{
options.custom.kernel.standard = {
enable = lib.mkOption {
type = lib.types.bool;
default = true;
};
};
config = lib.mkIf (cfg.enable) {
hardware.nvidia = {
modesetting.enable = true;
powerManagement.enable = true;
powerManagement.finegrained = false;
open = true;
nvidiaSettings = false;
package = config.boot.kernelPackages.nvidiaPackages.stable;
};
services.xserver.videoDrivers = [
"nvidia"
"amdgpu"
];
boot = {
initrd.availableKernelModules = [
"nvme"
"xhci_pci"
"ahci"
"usbhid"
"sd_mod"
"uas"
"usbcore"
"usb_storage"
"vfat"
"nls_cp437"
"nls_iso8859_1"
"virtio_pci"
"virtio_blk"
"virtio_net"
"virtio_ring"
];
initrd.kernelModules = [
"amdgpu"
"nvidia"
"nvidia_modeset"
"nvidia_drm"
];
};
};
}

70
kernel/vfio.nix Normal file
View file

@ -0,0 +1,70 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.custom.kernel.vfio;
in
{
options.custom.kernel.vfio = {
enable = lib.mkOption {
type = lib.types.bool;
default = false;
};
};
config = lib.mkIf cfg.enable {
hardware.nvidia = lib.mkForce {
modesetting.enable = false;
};
services.xserver.videoDrivers = [
"amdgpu"
];
boot = {
extraModulePackages = [ config.boot.kernelPackages.kvmfr ];
extraModprobeConfig = ''
options kvmfr static_size_mb=128
'';
kernelParams = [
"vfio-pci.ids=10de:2204,10de:1aef"
];
kernelModules = [ "kvmfr" ];
initrd.availableKernelModules = [
"nvme"
"xhci_pci"
"ahci"
"usbhid"
"sd_mod"
"uas"
"usbcore"
"usb_storage"
"vfat"
"nls_cp437"
"nls_iso8859_1"
"virtio_pci"
"virtio_blk"
"virtio_net"
"virtio_ring"
];
initrd.kernelModules = [
"vfio_pci"
"vfio_iommu_type1"
"vfio"
"amdgpu"
"kvmfr"
"kvm-amd"
];
};
services = {
udev.extraRules = ''
SUBSYSTEM=="kvmfr", OWNER="user", GROUP="kvm", MODE="0660"
'';
};
environment.systemPackages = [
pkgs.looking-glass-client
];
};
}

156
networking.nix Normal file
View file

@ -0,0 +1,156 @@
{
config,
hostname,
pkgs,
...
}:
{
imports = [ ./wireguard.nix ];
sops.secrets = {
"wg0/conf".sopsFile = ./secrets/vpn.yaml;
"wg-br0/conf".sopsFile = ./secrets/vpn.yaml;
"wg-us0/conf".sopsFile = ./secrets/vpn.yaml;
"wg-uk0/conf".sopsFile = ./secrets/vpn.yaml;
};
networking = {
hostName = hostname;
nameservers = [ "192.168.88.3" ];
networkmanager.enable = false;
firewall.trustedInterfaces = [ "vlan66" ];
useDHCP = false;
useNetworkd = true;
# vlans.vlan66 = {
# id = 66;
# interface = "br0";
# };
# interfaces = {
# br0.useDHCP = true;
# vlan66.useDHCP = true;
# };
# bridges.br0 = {
# interfaces = [ inetInterface ];
# };
firewall.allowedTCPPorts = [
9003
10000
10001
11000
11001
12000
12001
13000
13001
];
};
systemd.network = {
enable = true;
netdevs."20-br0" = {
netdevConfig = {
Kind = "bridge";
Name = "br0";
};
};
networks."10-tap" = {
matchConfig.Name = [
"en*"
"eth*"
];
networkConfig.Bridge = "br0";
};
networks."20-br0" = {
matchConfig.Name = "br0";
networkConfig = {
DHCP = "yes";
};
linkConfig.RequiredForOnline = "routable";
};
# netdevs."30-vlan66" = {
# netdevConfig = {
# Kind = "vlan";
# Name = "vlan66";
# };
# vlanConfig = {
# Id = 66;
# };
# };
# networks."30-vlan66" = {
# matchConfig.Name = "vlan66";
# networkConfig.DHCP = "yes";
# };
};
services.wireguard-netns = {
enable = true;
namespaces = {
wg0 = {
dns = "10.2.0.1";
address = "10.2.0.2/32";
conf = "wg0/conf";
};
wg-br0 = {
dns = "10.2.0.1";
address = "10.2.0.2/32";
conf = "wg-br0/conf";
};
wg-us0 = {
dns = "10.2.0.1";
address = "10.2.0.2/32";
conf = "wg-us0/conf";
};
wg-uk0 = {
dns = "10.2.0.1";
address = "10.2.0.2/32";
conf = "wg-uk0/conf";
};
};
};
# systemd.services."netns@wg0ns" = {
# description = "wg0 network namespace";
# before = [ "network.target" ];
# serviceConfig = {
# Type = "oneshot";
# RemainAfterExit = true;
# ExecStart = pkgs.writers.writeBash "wg0ns-up" ''
# ${pkgs.coreutils}/bin/mkdir -p /etc/netns/wg0ns
# echo "nameserver $(cat ${config.sops.secrets."wg0/dns".path})" > /etc/netns/wg0ns/resolv.conf
# ${pkgs.iproute2}/bin/ip netns add wg0ns
# '';
# ExecStop = "${pkgs.iproute2}/bin/ip netns del wg0ns";
# };
# };
#
# systemd.services.wg0 = {
# description = "wg0 network interface";
# bindsTo = [ "netns@wg0ns.service" ];
# requires = [ "network-online.target" ];
# after = [ "netns@wg0ns.service" ];
# wants = [ "network-online.target" ];
# wantedBy = [ "multi-user.target" ];
# serviceConfig = {
# Type = "oneshot";
# RemainAfterExit = true;
# ExecStart = pkgs.writers.writeBash "wg-up" ''
# ${pkgs.iproute2}/bin/ip link add wg0 type wireguard
# ${pkgs.iproute2}/bin/ip link set wg0 netns wg0ns
# ${pkgs.iproute2}/bin/ip -n wg0ns address add $(< ${config.sops.secrets."wg0/address".path}) dev wg0
# ${pkgs.iproute2}/bin/ip netns exec wg0ns \
# ${pkgs.wireguard-tools}/bin/wg setconf wg0 ${config.sops.secrets."wg0/conf".path}
# ${pkgs.iproute2}/bin/ip -n wg0ns link set lo up
# ${pkgs.iproute2}/bin/ip -n wg0ns link set wg0 up
# ${pkgs.iproute2}/bin/ip -n wg0ns route add default dev wg0
# '';
# ExecStop = pkgs.writers.writeBash "wg-down" ''
# ${pkgs.iproute2}/bin/ip -n wg0ns route del default dev wg0
# ${pkgs.iproute2}/bin/ip -n wg0ns link del wg0
# '';
# };
# };
}

248
packages.nix Normal file
View file

@ -0,0 +1,248 @@
{ config, pkgs, ... }:
{
environment.systemPackages = with pkgs; [
bat
black
blade-formatter
cmake
cifs-utils
coreutils
bluetuith
bluez
bluez-tools
cargo
clang
clang-tools
clevis
cliphist
distrobox
dos2unix
dnsutils
dunst
(import ./templates/extract.sh.nix { inherit pkgs; })
eza
fd
ffmpeg
fira-code-symbols
fish
fractal
freetube
fuzzel
fzf
git
gh
ghostty
go
google-chrome
gopass
gopass-jsonapi
gopls
hyprpicker
htmx-lsp2
imagemagick
inkscape
pavucontrol
pciutils
poppler
jetbrains.datagrip
jq
lazygit
(pkgs.writeShellScriptBin "lf" ''
cd_file="/tmp/lf-lastdir-$$"
${pkgs.lf}/bin/lf "$@"
if [ -f "$cd_file" ]; then
cd "$(cat "$cd_file")"
rm "$cd_file"
fi
'')
libreoffice
libvirt
linux-firmware
lldb
lua-language-server
luarocks
lutris
mpv
nerd-fonts.monaspace
niri
niri-scratchpad
nixd
nixfmt
(wrapOBS {
plugins = with obs-studio-plugins; [
wlrobs
obs-pipewire-audio-capture
];
})
# ols
php
php84Packages.composer
php84Packages.php-cs-fixer
phpactor
podman-compose
podman-tui
prettierd
playerctl
qemu_full
qmk
quickshell
resvg
ripgrep
ripdrag
rust-analyzer
sshfs
starship
step-cli
stow
stylua
sops
swayimg
texlab
texlive.combined.scheme-full
tmux
thunderbird
tor-browser
ungoogled-chromium
unzip
virt-manager
virt-viewer
vscode-langservers-extracted
watchexec
wayland
wgsl-analyzer
# wineWowPackages.waylandFull
wineWow64Packages.waylandFull
winetricks
wl-clipboard
xxd
xdg-user-dirs
xwayland-satellite
yazi
zathura
# zig_0_15
# zls_0_15
zoxide
];
hardware.keyboard.qmk.enable = true;
programs = {
fish.enable = true;
virt-manager.enable = true;
direnv.enable = true;
gnupg.agent = {
enable = true;
enableSSHSupport = true;
};
nix-ld.enable = true;
niri.enable = true;
dconf.enable = true;
thunar.enable = true;
};
virtualisation.containers.enable = true;
virtualisation.podman = {
enable = true;
dockerCompat = true;
# rootless = {
# enable = true;
# setSocketVariable = true;
# };
defaultNetwork.settings.dns_enabled = true;
# storageDriver = "btrfs";
};
virtualisation.spiceUSBRedirection.enable = true;
virtualisation.libvirtd = {
enable = true;
extraConfig = ''
user="user"
'';
onBoot = "ignore";
onShutdown = "shutdown";
qemu = {
package = pkgs.qemu_full;
verbatimConfig = ''
cgroup_device_acl = [
"/dev/null", "/dev/full", "/dev/zero",
"/dev/random", "/dev/urandom", "/dev/ptmx",
"/dev/kvm", "/dev/kvmfr0"
]
'';
runAsRoot = true;
swtpm.enable = true;
};
};
programs.steam = {
enable = true;
remotePlay.openFirewall = true;
dedicatedServer.openFirewall = true;
localNetworkGameTransfers.openFirewall = true;
};
programs.firejail = {
enable = true;
wrappedBinaries = {
chromium-sandbox = {
executable = "${pkgs.chromium}/bin/chromium";
profile = "${pkgs.firejail}/etc/firejail/chromium-browser.profile";
extraArgs = [
"--netns=wg0ns"
"--whitelist=/home/user/downloads"
"--env=TGK_THEME=Adwaita:dark"
"--dns=10.2.0.1"
"--private=/data/sandbox/chromium"
];
};
google-chrome-stable = {
# executable = "${chrome-argumented}/bin/google-chrome-stable";
executable = "${pkgs.google-chrome}/bin/google-chrome-stable";
profile = "${pkgs.firejail}/etc/firejail/google-chrome-stable.profile";
extraArgs = [
"--env=GTK_THEME=Adwaita:dark"
"--netns=wg-br0ns"
"--dns=10.2.0.1"
"--whitelist=/home/user/downloads"
"--whitelist=/home/user/pictures"
];
};
tor-browser = {
executable = "${pkgs.tor-browser}/bin/tor-browser";
profile = "${pkgs.firejail}/etc/firejail/tor-browser-en-us.profile";
extraArgs = [
"--netns=wg0ns"
"--dns=1.1.1.1"
];
};
freetube = {
executable = "${pkgs.freetube}/bin/freetube";
profile = "${pkgs.firejail}/etc/firejail/freetube.profile";
extraArgs = [
"--netns=wg0ns"
"--dns=1.1.1.1"
];
};
};
};
# services.ollama = {
# enable = true;
# package = pkgs.ollama-cuda;
# home = "/data/ollama";
# user = "ollama";
# group = "user";
# loadModels = [
# "llama3"
# ];
# };
# services.open-webui = {
# enable = true;
# port = 11347;
# environment = {
# OLLAMA_API_BASE_URL = "${config.services.ollama.host}:${toString config.services.ollama.port}";
# };
# };
}

36
secrets/home.yaml Normal file
View file

@ -0,0 +1,36 @@
root:
password: ENC[AES256_GCM,data:qA7sbNvWvfmWiLX4pIYOzDmuCwc3+7I1KvTHHYF5jDHR0CNRuya0XglP8TNK5qGLEJkdmD9WphdWvYY60NTOf5NFhDnqtm3ZIw==,iv:B42jl40hQbRqMRfV39fNne0E3KKCwriAQ5MQ0DF4QQA=,tag:hH/u0Qw6A2is4XOOQpnr2Q==,type:str]
ssh:
desktop: ENC[AES256_GCM,data:mjenzt2rI9vZ4VnpWttAmojzEaGDgDTO0Tc5ICTZzKRC8fDIWc/YH81mCkU8BpPJGPiCQSCPz+/r7MEjcfjC0Xhf5xnSxespw/2eiCoeR4qWGMC4SlizusR+DBxwvW71MBn8icZkJyWc9zKPAfDyZQhtVUthvcQYTVsJEvMkRYSkTQgKJI0iEpq2x4j8fKr/FY4EQsidm5BFykOYOFejECWkkcMf0i+FBS60EsH3aZV+5c5fNNlj9GIdmX5rMp8iHa/RlGfxiu0QIaZRa+Q9h+h/d2Utf5qRFv2qpq+Fk7tI9JErtwwB+tKdXbavPjgWt3YtqI4O0f+01BWKRTPzFeiRANoONuGRgcx+b/GkAaHV4P+7uqIgmWgkwqCliYkgdwFtE2dWS6M/DEO+KP+GyKUykusdx2xODp+gA/CpiJM5OWPxmsgkDkuM/aLr3cBdzUHSJ028RCWBkTO/LsUOKNBpQ/9ePM5AoGVG7//gPlPEqURh5QrsJRJnr75E1qAI1Eu5XVIFrprPx3lxJEDWv6YPjUm6Aj6g2+b9,iv:rvJGN0ha+cACzf8u9dBRu1HOMx1jyx8Gwe2MJpJ6pb4=,tag:wkbymeExmj98vzDMSG5uTQ==,type:str]
user:
password: ENC[AES256_GCM,data:s5U1eR8aM4AM4J8j1VcRM8eSS/V6iJY/Dj9Ggis466Yxg+eUYWEimY1IWWVvFReRZ8WkbACysA56YZrLdEDowc1+lzeZQvPwYg==,iv:T7W1APCEozr7Eck06cDb3+VdVwjhQ3QoOrp7t+Hr2/4=,tag:MCH47JYaLyNEboW1oHC1kQ==,type:str]
gpg:
legacy_fnzr: ENC[AES256_GCM,data:aokSG2rHR6iX94GCcCmCxcz1lXSRvO03uhaUVGrNJT5aDjNjrjLya4TpUEVLvZXy87VaZobfuR+KQIcyi5zFHNCberq3q7h2zmtKhCEsnuy97oam7X39o/Jker/36geFxkDW5kvYAtcYL16Zi6cMB2+VvI7Sv0YGawGvvFfYjHjaV/wl0QPv+jyvM/HT9V7EMqJ7CER2QQVdkPuJHQSy/LsZqtcFGEDTuiGY7jZO4wWPByQ6PNPg4SiGeRfHz9rPvKO4LxQGt6keynM7VApZjTKOMcmaTgdOEgop5Sa9nZ+b5J7BJuP69vwnyREiP6u5T5l7QSxhFei+2K9iBMke+nbwM468lwnV9pUlpbBNxMwqVIRKiAGkcyZilmhlQd9Q4Dy5LwszItOM97+gN70IFkrzf6sO3nKsQWRr7kSwOAwvn1EQGEjj+KE1KM+cu+Y31PBBzkxtWBISJA1qzGcO35aeKNv8wGDGFliecg0Dz4SmtPXwP5Cd46U5XM/w8GMGRFg0b6anink1se7hOuYYSm9PzfJKbDOEL7eV5mhAGC2Qwv1q3Vwss+r1l6/b4myesP+OeVyeGCDMhCqO9VA6sQuBuddS2hYzh7tjI+p+0VXW7wonApNCKAMY90QLu2+8f33twYncWETbK/PkTz1jr4VVQIzh65kDclGPePfC2QMMRlT8eAL8y7u2RvDl6jteeQKiLsG7sz1NCXg484GfD8l9eMW3slLtY0b+9XHuCv09kkGkoktSw2udjoF4s9iJsGKOFRGZ/nLnpoXh0ftlKHvohhfqrTmwwvbPLYg9rn59dO6ZgIwH8hVi+ERWTtfpPERYPbIGLh7iRQf6iPI2d5oiE4wVjOsidiR5fhrb+0S8oALAV1Jhm5Y5Md0ePLrnPJxOXvAqCE1WXpjmZzl37qj+1FV/FFyh3Pq+GfmSN4fEllj7rkFppGBOQ19+8mV1JFkOVfkzwqV4NP8c08FigeO3T4vyvxu4jfMbovjS6Mi8Olia7pNGB6kZogHQXIGH7WE1tqmGFnEOvOHz+YKJ48apCtZ/ePo02lu/xZM4GNO5Q1LXpZtO67tpuepBjtmSGhBhUtt2ssdLpJVsDNbuLKDrT8ZA1wD7gA2C9wx+ycT5+JqE3o99J3unxVI=,iv:o3mrSYQINgMTaQBleo9hdNZZaPC4/OFf7RaJIvDVjv0=,tag:5v0SZBOpjdkFAezrKzQfLQ==,type:str]
ssh:
desktop: ENC[AES256_GCM,data:BXzI4BQmwunDe+PnmRjeDdZgaDVaJCqErhfUiDPtgOtx0+y1wPSTQoHBWM0+qnPTo0GtK2LmjlDGSoXUUl/aavwGyHG+/XMZcRs3LLGZqAOCWCAoA4N8nJIv2PFPCDdboK99HSR70eqHWgHk8McRrqpwDUaCsTaizU+e7R7bKX08kBp+k02fE2OW7OJHTfK6H+qH8yV8PpBXGpiPG/8OMsLa9m+1burb0KeMVRVk8Jd+kQ9meYj0xUH05l/VV7jeZ5XmZQOCtNxIRDnALjfWDJ44QLcdt1OoA8WZCoUV1437u88hYi45vo8IvFK46DBMnQGnGioITkOoIzBYAPFf/RWzyJZChrQ8qgCIfoOszbGrjXu5j1upa0KvLe4EkOBCeGNi+vPG75Q6Ma25jBLXxIq+xBeoSZyW5w9GLNEjXT7D6hXiVpypBQK3AW4JjOLywXDYG5dJzqrS4QQvilKIqj/EWSGiYkrHNJd+jVaBx/M6HrgW9Vfs0JuSz1bWgzASjqn/3zx6UzbaREm+j5Is6t1PPsix+zPKAo09,iv:gmtvM+6auTFlH3fxtq+HXq5PuzGTnIRgLIVfvPuk3hw=,tag:d0kpvr12rA/HcYOw7hDBUw==,type:str]
legacy_ed25519: ENC[AES256_GCM,data:0VbcLU1XtHfgi5mM+qYZ74ZE48DiKc2lzw2Ro13XyLvQMHP6fiTKZkrPFA/LQW+sQBG2Ax5dbAYaf4Gxe5wIWKn0kU8uai17//IS2ahN91fQ2GKC3ofURfwmTEZXnQPHXjK1Kygb6uMGWYOjjiY4JCXsEr2Ur5OTH5Cn7wrOPLFp4V7rK66RlrUuYoB52uGC8ds/KZWSRKZKSRJ2fIJkAZtOFUTzU1nbScKC7pVjt1pZmCI2dYzVD3xo/p90wZAXTTq6B6pprtnApHnzEW8kP5RVUdc9euMQm/HlQUh0RexUlfJCkloVxYD0RRiGL9RFABxQCdeyK6VNcgRZ32M6cUji0FSDgE04qTz0HN062Um/ebB65zFmTAN4GZLClIUqJVa9qXweESGkzbUV5t1J7IXDtG+ZdJmPokX31ozF69Vtcf53LQOee1d4zseP+niOR63tYOkZWkthRRLx4yxXSODyZcVe7ASbwICnxmf3bh1Qbn/4LU3NSBF1DZXiVIKR9COMdO8vqx4H2zZLrkn06I33J9sjoI4ejEFX,iv:Tjts8BRYhE+Nn9dyAL2yNpvPF8oMTwyVvoQkmwAsjrA=,tag:PuHmgIvHt+Dv85EI2WFxng==,type:str]
legacy_rsa: ENC[AES256_GCM,data:PWBAlpIUbxVGDZlxJGvYRiUgeGnR7Bl1hXjzVC6MIt4hLPmc5SksBj5TmW+i6j312RLaFzYTb6SerFlFs6vn3Rtmk5Q7p/IO4JSKondJrsw1xw52YePP29FfIe7gtOxDK/F9YJhHb1VJQghQoWTN++iZjjg80iNVofBtAcrPPkyfH43Yw8In6jMla/p2w6vAhXAJS+8SpEdmdILOQT4OnDvkzi4Nwae2uFEWcGJI/kxstgAL97MTmu06qK9/zt8vuq6bTYeBOOFnasoIL+V/0A5dAz3aw8QwdSoRrN5+PEHmeB9y4+WCf10MTv8P2JxJ9ul4pCwZd2IEdg2gWSQa/vbSJdG7ZjaCih+blNEf8DTVtRQT9soTvti7MYYYziLfHw8LUT+c3nt+c0nXxVxOgRx0sGSuHsHT5HHGBOjw9PsZ+arZuwnIyrmfkm8TkvD+j7je2fqLACRBkp3CXg8a647To4GQXK0Y0yuJUpt18YSbVak9etGj5z7+PWs86NKHQ+gOrMxeirRuHGIa53FQSORYlpBZnM9FCAs4g5Fs4IZard/0LkX1lrhVUOk7OokRstiIWKJMOqRFuN+toZbCWMNVX4Hd8otSFG3U/vo3vK08TEF+HDIhp4rStPjJpOdyZStKyol6sKlHcVdMcgk79fmzesMZoSpITP/GTYe46xZ6vMQ2tvqvd0QcVJfBZuy4q2UUgjY+r8zgVRKw+Wo37YUUr4UFK2W5E23cLFkr+H9nV/cz08V1vUXk2eCi62czFXpjkpubQXysK246wHtdVFGpOmtDxicS4Zn3iqm/UxF5vKKguRaTdejXxfkRbsFhwn6/GrRAW6nxJLLVnSIzJEnNivt88m9cxmb1VYAHRZDNlGDK7ca8IhCb5yzPalRI0KqUMKen3MDLae+eEjcZH/lgGdhsZJg6Kxg/v0XC4dCJxNW+/ckIs5bjYMoQRfJ6OknAVFLwZcY0QZ+5EWrWNDkBEfPKub/r+KcF2sxIrv7KB0qXDsW4pCEcE+85i8xEiFs49ebeV4d42tsLzdYi9iVdZO4I1gzi929rbUFJLzVJCfkyn7Eic1u92UgBAa58lUFZ5LMEH9+P1V4Gm0GPIwXYvd4X+LIGhbqGDjGel+YeeLUHVRj4qeuoz1aLUCF8Cv0e3djBuhJ+tRSKdZM2aSz6PNrK2THfdFSiaHZmfH7McVXNum8TD8lhbkKIPDYdb2La3MVgvvgRN8Gw8L/Xkp/ivuFWXJ8Gc41AIUI2gY2mWbq1YAiwEuMeDJCXXAROKLQrd2rJYI7epbcw2EyLgtc90zsjWa1gFrHfBJVh23B69vWc5uEVyt/ryg3n4tp9QLjNpg0DRMjPTR+vEbsEHl7oLJdPO3XKof0h6QXXJ1ZtgHkSFS9MoXCnNhiC9ZKWA6PM31Yftp82RKiy5CSpf2AIVIOa3p9GUhVI0YlEN5tfejZuhybUL44JZ2FcVlhbC+zzLPIhROguHF0Az4Qg7tzKM9poin4xVV3HXs/mvXAcA8qnlQxnpuei3Lob4Iq1MUb9oNoA0QJ7nKIybSYUhKnGOq0Cv4ZVmxlk+RkXfTS4si3qEg3UxXjonP/e2oU334CsdSS32taP0KNTQzEP6j3AM4Zv/iEobz6XvRsKXHzbrG4UQefuQ+ZDw1DGlr/BKDLq9T+adGCWaCTKJChOIwoEQvij6rxtnGOJu9XC+/Tk8FVuzI/gPnRBOia0uUwaYskgoXmNjMgT+P8yNkdqx7MFJVdrFADyGhSaefVNszQlxfJlpAZtKazEltxmUdW6/pPoG42dfIcX+HxhbX1GqQa3japO7NrPQwLIw4LusBXd/ShLMyxkkYCcAidSgaSWgTWCzEIO5RbxRoxFA4qnEd6fqH61Y6W5Wg0Yk4DwWW/KcKn8xkgPdNk4hPq7IPL/x6Z1Dwb9zYf3T0G+97iezI9ZCNImd82aDOT5UcMZGYDD9yW9sEfohniaJcbu5DocNLTJDBa3UgiWluY7u+q4I/zPKN+9chA7h8p5BYAZJc03d1UBvHL8Mv2D7JIb/GlLhseRoV3A+b2qm6uwcxHA6zksAAx1XOZYlU88ls9ykeKKIB+AmlZEx4EuZG+E98DFp5dndMCYzL/1uy+LEsmTNrdmG+gl7bQlbYq5hOHaqtOAEsft/0VFzteRjg5Lagh0cqB1Rc92EGzQq84G5QYQb/DDZLC4i17SZEvwJoo3Ai476B6JpKefcQ5pPNf219mD/V/rodHMxm5vMC3hBQLpaSQ7ZtZ2jmwsxsWkrVaMU+NGBShqVLV+wsPlIWrSybnZC7hOLawZLev1CJ9Udiy82U1SyL9vB4dfHnhEra85nzdNvKwKzAwnr5mG2w1leJOzdg5kz8I0D98GFxmgrnOeQZ6S7/pjD7Qcw38exvEtCI8fpreAb7djbZN2sUNaSe8oitgukqNajBHlFA2z75P4dZ+IH+tTH25Inem/COojWnVQHsic03eus8x4jinbU6fxgZ6sT0SlyItTFgAAv3XqT3oHBiXIvF8DCcw7EtOeW1aZx6wR6D8JFLXTkMiXeug2wvQxV7MMXt7DBNUPqWLYnsrzAY+UaGkQtlnGiffDjCwbWkDfRpHMutMoYMk9o4yD26XTv3oV79Rod4+R39A8WEQhiHKPKFBS7TW+NttKMQVGgcmWvfRFeem4Iqy6JG22xxQqM9GK8gsUKa8MmEuLAQCGenXHIj+dOVWJugjG4IOdaQR3nB4LgwWIXHciYD9KmjlDsvd0MjQTc1bUBkS3aqOF6uvnWJ5LlWv5gs+OsFRMTe8wyqwCOLkj1EmmfJW3leEl/ONMkuaKCJTl7Yw1gto9itnC7rXFSIuGtLfW+4BSJQDYNRBskBkf81t1b6qejkS/3FFKEl8CzLdnuZU5WM5EOkR8ReAYTF/c4LYQjKDYbTAkXsuf9I8QbJXNwwJrP0C7zR4MgZQ5csKyxMNhxy5prTol5TIfCtwjy9UWOurI8WRwfqf6dyq31HtIDxD+elAJOlnZ+yHYH0Al/AETNhzWej35XJTMpYAK5wyBInM3enzBDqzEXQh4OLBwFMP0RaaZYnGTjbjyTUjmOj6GnMSbjwKECCEJFXfmpGsaxIG5RXIU71eu1x7k/Ifm3HgvQ9Q5Jw+EF6vYo1WNjOf9UbQhEhI1+uKEYEkwD/qFu07l9/WqLp3/nDNS66lnyag8jGKeO9WDb07WBBpxywtlN0vE8q9VmY7wXDdkaf5Gb13VwpaAyStANeP2Mp7XlmvYIFudzAY6kxkxCBA5rCY5guI0ebREHnJgR9ARzK3MD85X5abk8sY7Urx01tVdmx7eqVVrjBsEq65SUg8Z0jA7uVHTpOkVxboxJEWsdQN3JYYXz7Tg7M0t1PEYlxhUFIlIyvD8hrmUfxyZB0YgVI3faTezb+1I6xqAyxGFEVKPBzjyv36V9eMJmGB1wiZv3mNFqMUI9AMUHJAmIg==,iv:oGGm7bveDXNmxqe5Zina03vSVLJx9P3VBC/DbuI6KzY=,tag:taEL0E4Uinv1MQPxeRbPzA==,type:str]
sops:
age:
- recipient: age1y0tj3kt67pfnj38t9c8g2ghry3a0mhcq8rrqv5xr4jekwepxaelqzu3dkf
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBOZ3dERmxMbEJmSVhVRmVw
TUZZL0FzTHprci9ENHFodnRVWmRSbk1ab2xnCnZYeXN3MWRJZENlYmtWeFM0azdR
ZDFFYSsxRE9pdGZWV05hWnlacDlKbWcKLS0tIGNveTkwWEN2ajhaZ1JiaXRENzk0
bFhna3RyY1kxK0IyVWlnR2FpMXk3UjQKvJaecXqAecBljJ9cNcHX13nxSxVey3LG
NdvJaMfEV4m3SqRF7YUoTzGYhucYjtX58E5SuvHnaP2qa21aDF+AZQ==
-----END AGE ENCRYPTED FILE-----
- recipient: age16v8w7q4wmn22hhakq2uzaus2508rhldm7lcwh0kukshzjzyhuqesqz44ze
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBSZnV1T0JKTXoyeUlZQ3o4
QkR4OHZUbzQrbjdSK2VLTDNzMU9qdlUvVVUwClI4SDhMZTRDRXY3NlJSZ3REdmpJ
L2Y1N1p1VDFuK3VhbjJLaEVaenNQbWMKLS0tIEZGN3VlRFZqUFZQR1hCMTA5SEZN
NVlzQlQvMVc2dHZNK2pEWk80MStwY1UK38mxk1dJWi6XRKSwzcDA9qt8i1Grw2KJ
ac2EbJIFwYLOaMNOKF9hu+NrMdruRpU/2B8HlYYZjpNWmLb9jwI4wQ==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2025-12-22T18:37:14Z"
mac: ENC[AES256_GCM,data:BQj7pieRocXUP7as5/coL76WrpBPOwEzHlR+vTEOkBTD89hOnfUo0f2gJi/Ho4KldnCHKjQVGoLhA7l/NIuKXGs8OQ+0IYW9EOoEROi01EePW++Nw7rtOLBRQWojO9HpOmrOz8/fqMHq/vxrdrLql5gomNHJgPllBoAcALCUl2E=,iv:sPB13YhGgDO+KA+azVBqtIPrqRuoPpRfEljV5Xg4ig4=,tag:BnCePF7ii63Glq6dCI7YDg==,type:str]
unencrypted_suffix: _unencrypted
version: 3.11.0

37
secrets/vpn.yaml Normal file
View file

@ -0,0 +1,37 @@
wg0:
private_key: ENC[AES256_GCM,data:nr7y3wp7EtVW6uI6MBSwyMO9YuMyx/F0AZmD8GmuA3BPQTVTsVSctoKIxLE=,iv:KN68DwGuDo+aPP4mBk1MqY+lxFjisKSwXn0w+yngDRQ=,tag:gpjxIFWaZE+5hbYHVsO1ZQ==,type:str]
address: ENC[AES256_GCM,data:9Tnph2SHKeEt9Ss=,iv:CPR1N7fqqlaThGltSpfqeAOc5bAe13KWskGWj3jI8LQ=,tag:xha/hQOVqfUoGyfKbHhnuQ==,type:str]
conf: ENC[AES256_GCM,data:SRDnI+2PvK7Zz1L5XBvrBNejgJEg8DK+qVO5XEtx6Nal+f7IeB3Ascp8Bkit5fd5myn/RxiK80wYmvLkDmcJAk46UjHKOpbxJl1s5FmKDuZJ3c3MXLwH7k2PeZP14VDDlyQqlcyGBrSu74L64ZMh/6EWGKbONTD1Wt3Ykg+/RegzQFDr2CPbj6XQeXsNS2p0ugicP5ffBMTUa9KSYDMQVV80mjSZ246aeY0owU1VUsitdvsCbfxtFd5gr/9zdfOXOvGY/BKxAlvVbszCalNs9DgJDHt/,iv:FP90SvUGnsZJS7F/uxtbOqTvGOgtC4+r2+YgF5FBoQY=,tag:9G1tkXHTpbytmG9T6sTpMw==,type:str]
wg-br0:
private_key: ENC[AES256_GCM,data:y8djTiZ00DIhdN1YNUNmjqCPeSUrS+YWKfr4iLT8h54fSlgDn1OPAoyJllM=,iv:Wz/EiISOlaiSXSIa4h8L2wJPm34rxpQkErpuqAR/uC8=,tag:RS100jf8INb12q6+hGQDiw==,type:str]
conf: ENC[AES256_GCM,data:3TJ34y3NI1wO2Oyh7D72t2WmlOq/pMS43Ck3gDFV9QPSC7E3GHtc86aZqRQm8qbRqnkJco1+nlJZmsC+BQdmW+U5RkRL7vUyDmrCyeDyJvEkNIJLzcFi4lppbYXG3A+fRO2l5guQA5qNSQmnyux51yY11/OCahoFEEenL1wH+R0ugMK/LIPwK0/Y2u7c+7SvNwwtK9xQUlb4azqUsdLX0xkUFbBje2BgN50BB2TxLZJexUNjOTW9TE57tw3yI4VjYEvIyBXOK4uuuST+TcNJY5B45tjd8PmO9vPYDw==,iv:BdNVbiMjzGtX0rzkLBb/YCDxqBv15L7bJcLTPzvt/yM=,tag:NmEMTPasBSsg1GSkYi/HxA==,type:str]
wg-us0:
private_key: ENC[AES256_GCM,data:PWzeqoYLICXjYL5AV+dxUxYn6RL/tUMkfTMYhm+OdC85cPBPU135Ov15nUk=,iv:1CdiayG3F4xp3eid/ilboFUOL4Y92RUqipASOPT8Jl0=,tag:cJulFquSHjmKF+lUegp37Q==,type:str]
conf: ENC[AES256_GCM,data:x4bpjE/icVqe3lXHRNWf5W9w8EVAiNyqmFUR2LslSrEz0ekmJ4YDKDuU//+VoomsZzHhY6GhKtEVJ4Me/xY0G1+dSBBgpZlO8UyZVg8aydsVmA09fHYgwKovzHXc0XM9cbbovf2yyK8kO+X8e3ODy1dSu+a1SA3YsEp2G46kbRRxTdYazeHMJ+hJdmCONEvx0p0NcPlPAW8eZWEsAbVT/eQzB3TnF2THy/t4oWrn4r/MVvcsWgc1LJ3rVJ6L8RREU/PYBJoyDMgAs3DP79E8iOQS+bwdJj0+gcrP,iv:Ngqn/EA3QpOnBxz+slSyn+oOO33hLZlGhAg+RIlN+wI=,tag:BZFjGxI0nRsdPn99rvijdA==,type:str]
wg-uk0:
private_key: ENC[AES256_GCM,data:r1BEMvO+Q/6VTuC7N/iqWf1Cyp4WZsUitVF14n5QXPzYK9PoWgPGylQQpmg=,iv:wR1gsHzzu0C629bs7yGW6evjdieONyzMnc9vvtTEh2Q=,tag:lMk4DuTDFtOW0heIJ0Ef2A==,type:str]
conf: ENC[AES256_GCM,data:tCQaQEJ1Pa0jw96q/LRtqhAWes04GkGOUYAlZu3W3C9EGHj123dYZiWXcUgymSsi999M3RbO2mWNllSl17zkV1iGZAkA23aQLKDUU0/m/SSCLm2ohJOdLKkGeRK41TMGFRRydfYZJ9rg0iJdacfXrX2t4RGWgtprSlS2crOcrAQ+jhMjCNxKuAg0gaJyUh1lcTmOohe0aBiqxGJE+mc4mKMwCiUytNoprAc2TrnKUTQ7/eyIv9hn0PeRvLL59KFNrcVpCb7VRkufYpYlNtO1zyl2oczYzw+IXCHt,iv:S74FbQLM5sXhvoSaUD0wuMeAd7U4rL/dneaVS3ah8Bo=,tag:0U/k1Cyy4XbvmnlApg2BYg==,type:str]
sops:
age:
- recipient: age1y0tj3kt67pfnj38t9c8g2ghry3a0mhcq8rrqv5xr4jekwepxaelqzu3dkf
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBtOHZSRkpBVVdUUk9OYUFH
cVBra014WXJyRTJ0QWFKallLQlc0SXhNSlFBCmpwME92M2lCN2liVjZBRndlSVBk
OEpUU1YyakdCa0xVaHdhRlpXbGxYdUEKLS0tIDFlV1k0Qkx1UDd2NUVHTTI3NDZE
OWhIdUxDcHB4Z3dTdDkyZWF6NEJCYzAKfPB9AZFQ08yqil+4AhIi6EMy8PXI4CAz
lK4ON/M67T0UrlWN/m3pryOOr4Lj4oiZvdOR0BCO3kn4Pj0nq5jQOA==
-----END AGE ENCRYPTED FILE-----
- recipient: age16v8w7q4wmn22hhakq2uzaus2508rhldm7lcwh0kukshzjzyhuqesqz44ze
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBMSC9Td1NTMzk2NlJDTDNM
UVUzTSt1dGkrUVRGT1UzeXcwR1REN1U0dW5JCnNJRzdKZHVyR0dzaUw2TlVzQnQ2
SHhSSGlDWUNBSXZiME5GM0JPTFRseDQKLS0tIEFnOXgzWFo2Rmo2THN4VFFIY1h0
OEZ4WUp1QlVrTkVTN1BHMG0yaXFuSk0KLw3ZuvWTurJDTpyoq5YafLm8YFT4v4Vh
s+ay8ju3kA1CKjMF3gBQF08EoCdP/jU6tZerNwwcs17el5zIvRmG7Q==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2026-02-08T19:15:21Z"
mac: ENC[AES256_GCM,data:IxRNC13NPMIhVon0vCH3df+JxQlIPzv79H6JQzj8hkQNLxbFtPzk9DlHW1PWX90UAQbaXnzUM9uyHZhg4TypVsuKiEhpGyE4LXn/O7wD3U66YMkbqGrpKtnWbCQNdj///N5nDf71QwdLTEAzH+Kn9zRmiZ60rEu3w1Wm5vitB78=,iv:jWQvvO8kJDULepEUbWRym57whNpd23Q2PkXCDNQeJLo=,tag:wPBtn7Onr3S9ksEmdCZFzg==,type:str]
unencrypted_suffix: _unencrypted
version: 3.11.0

128
storage.nix Normal file
View file

@ -0,0 +1,128 @@
{ ... }:
{
disko.devices.disk = {
data = {
type = "disk";
device = "/dev/disk/by-uuid/9878027d-209e-44f1-9030-13aa84c1fe0c";
content = {
type = "gpt";
partitions = {
root = {
size = "100%";
content = {
type = "btrfs";
extraArgs = [ "-f" ];
mountpoint = "/data/.root-disk";
mountOptions = [
"compress=zstd"
"noatime"
];
subvolumes = {
"@vm" = {
mountpoint = "/data/vm";
mountOptions = [
"compress=zstd"
"noatime"
];
};
"@games" = {
mountpoint = "/data/games";
mountOptions = [
"compress=zstd"
"noatime"
];
};
};
};
};
};
};
};
main = {
type = "disk";
device = "/dev/nvme0n1";
content = {
type = "gpt";
partitions = {
ESP = {
size = "2G";
type = "EF00";
start = "1M";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "umask=0077" ];
};
};
root = {
size = "100%";
content = {
type = "btrfs";
extraArgs = [ "-f" ];
mountpoint = "/.root-disk";
mountOptions = [
"compress=zstd"
"noatime"
];
subvolumes = {
"@root" = {
mountpoint = "/";
mountOptions = [
"compress=zstd"
"noatime"
];
};
"@home" = {
mountpoint = "/home";
mountOptions = [
"compress=zstd"
"noatime"
];
};
"@nix" = {
mountpoint = "/nix";
mountOptions = [
"compress=zstd"
"noatime"
];
};
"@persist" = {
mountpoint = "/.persist";
mountOptions = [
"compress=zstd"
"noatime"
];
};
"@nobackup" = {
mountpoint = "/.nobackup";
mountOptions = [
"compress=zstd"
"noatime"
];
};
"@snapshots" = {
mountpoint = "/.snapshots";
mountOptions = [
"compress=zstd"
"noatime"
];
};
};
};
};
};
};
};
};
fileSystems."/.persist".neededForBoot = true;
fileSystems."/.nobackup".neededForBoot = true;
fileSystems."/home".neededForBoot = true;
swapDevices = [
{
device = "/.nobackup/swapfile";
size = 64 * 1024;
}
];
}

View file

@ -0,0 +1,11 @@
-----BEGIN CERTIFICATE-----
MIIBoDCCAUagAwIBAgIRAKiOpK5oYiVMtiIaoo1yW9wwCgYIKoZIzj0EAwIwLjER
MA8GA1UEChMISHlkcmEgQ0ExGTAXBgNVBAMTEEh5ZHJhIENBIFJvb3QgQ0EwHhcN
MjYwMTIzMDEzMDExWhcNMzYwMTIxMDEzMDExWjAuMREwDwYDVQQKEwhIeWRyYSBD
QTEZMBcGA1UEAxMQSHlkcmEgQ0EgUm9vdCBDQTBZMBMGByqGSM49AgEGCCqGSM49
AwEHA0IABIR4VHh4dTyuf0Uyi1WHdjD7OAppJAbCVZ5yyxycLYa72FDOAILFQW4l
veu2lHJ2SbpXoYiojwbRt3iDdNVbA7ijRTBDMA4GA1UdDwEB/wQEAwIBBjASBgNV
HRMBAf8ECDAGAQH/AgEBMB0GA1UdDgQWBBQXrqLKUW1JM7vAL3AlmcDvMvqAMzAK
BggqhkjOPQQDAgNIADBFAiEA7OMwkoWP9UTvXz8FIo+m4sB52nw8nVfx7wwxIMwC
unECIHNHJkFPkbJoxDBz0FWc20oIQ9O8ykPclsqrNBv9wzOf
-----END CERTIFICATE-----

79
templates/extract.sh.nix Normal file
View file

@ -0,0 +1,79 @@
{ pkgs }:
pkgs.writeShellScriptBin "extract" ''
set -euo pipefail
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
NC='\033[0m'
log_info() { echo -e "''${GREEN}[INFO]''${NC} $1"; }
log_warn() { echo -e "''${YELLOW}[WARN]''${NC} $1"; }
log_error() { echo -e "''${RED}[ERROR]''${NC} $1"; }
usage() {
echo "Usage: $0 <archive1> [archive2] ..."
echo "Extracts zip and 7z files into folders named after the archive."
exit 1
}
extract_file() {
local archive="$1"
local archive_name
local output_dir
local extension
if [[ ! -f "$archive" ]]; then
log_error "File not found: $archive"
return 1
fi
archive_name=$(${pkgs.coreutils}/bin/basename "$archive")
extension="''${archive_name##*.}"
extension="''${extension,,}"
if [[ "$extension" != "zip" && "$extension" != "7z" ]]; then
log_error "Unsupported file type: $archive (only .zip and .7z supported)"
return 1
fi
output_dir="''${archive%.*}"
${pkgs.coreutils}/bin/mkdir -p "$output_dir"
log_info "Extracting: $archive -> $output_dir/"
if ${pkgs.p7zip}/bin/7z x "$archive" -o"$output_dir" -y >/dev/null 2>&1; then
log_info "Successfully extracted with 7zip: $archive"
return 0
fi
log_warn "7zip failed for: $archive"
if [[ "$extension" == "zip" ]]; then
if ${pkgs.unzip}/bin/unzip -o "$archive" -d "$output_dir" >/dev/null 2>&1; then
log_info "Successfully extracted with unzip: $archive"
return 0
fi
log_error "unzip also failed for: $archive"
return 1
fi
log_error "Failed to extract: $archive"
return 1
}
if [[ $# -eq 0 ]]; then
usage
fi
exit_code=0
for archive in "$@"; do
if ! extract_file "$archive"; then
exit_code=1
fi
done
exit $exit_code
''

View file

@ -0,0 +1,221 @@
[Desktop Entry]
Version=1.0
Name=Google Chrome
# Only KDE 4 seems to use GenericName, so we reuse the KDE strings.
# From Ubuntu's language-pack-kde-XX-base packages, version 9.04-20090413.
GenericName=Web Browser
GenericName[ar]=متصفح الشبكة
GenericName[bg]=Уеб браузър
GenericName[ca]=Navegador web
GenericName[cs]=WWW prohlížeč
GenericName[da]=Browser
GenericName[de]=Web-Browser
GenericName[el]=Περιηγητής ιστού
GenericName[en_GB]=Web Browser
GenericName[es]=Navegador web
GenericName[et]=Veebibrauser
GenericName[fi]=WWW-selain
GenericName[fr]=Navigateur Web
GenericName[gu]=
GenericName[he]=דפדפן אינטרנט
GenericName[hi]=
GenericName[hu]=Webböngésző
GenericName[it]=Browser Web
GenericName[ja]=
GenericName[kn]=
GenericName[ko]=
GenericName[lt]=Žiniatinklio naršyklė
GenericName[lv]=Tīmekļa pārlūks
GenericName[ml]=
GenericName[mr]=
GenericName[nb]=Nettleser
GenericName[nl]=Webbrowser
GenericName[pl]=Przeglądarka WWW
GenericName[pt]=Navegador Web
GenericName[pt_BR]=Navegador da Internet
GenericName[ro]=Navigator de Internet
GenericName[ru]=Веб-браузер
GenericName[sl]=Spletni brskalnik
GenericName[sv]=Webbläsare
GenericName[ta]= ி
GenericName[th]=
GenericName[tr]=Web Tarayıcı
GenericName[uk]=Навігатор Тенет
GenericName[zh_CN]=
GenericName[zh_HK]=
GenericName[zh_TW]=
# Not translated in KDE, from Epiphany 2.26.1-0ubuntu1.
GenericName[bn]=
GenericName[fil]=Web Browser
GenericName[hr]=Web preglednik
GenericName[id]=Browser Web
GenericName[or]=
GenericName[sk]=WWW prehliadač
GenericName[sr]=Интернет прегледник
GenericName[te]= ి
GenericName[vi]=B duyt Web
# Gnome and KDE 3 uses Comment.
Comment=Access the Internet
Comment[ar]=الدخول إلى الإنترنت
Comment[bg]=Достъп до интернет
Comment[bn]=ি
Comment[ca]=Accedeix a Internet
Comment[cs]=Přístup k internetu
Comment[da]=Få adgang til internettet
Comment[de]=Internetzugriff
Comment[el]=Πρόσβαση στο Διαδίκτυο
Comment[en_GB]=Access the Internet
Comment[es]=Accede a Internet.
Comment[et]=Pääs Internetti
Comment[fi]=Käytä internetiä
Comment[fil]=I-access ang Internet
Comment[fr]=Accéder à Internet
Comment[gu]=
Comment[he]=גישה אל האינטרנט
Comment[hi]= ि
Comment[hr]=Pristup Internetu
Comment[hu]=Internetelérés
Comment[id]=Akses Internet
Comment[it]=Accesso a Internet
Comment[ja]=
Comment[kn]= ಿಿ
Comment[ko]=
Comment[lt]=Interneto prieiga
Comment[lv]=Piekļūt internetam
Comment[ml]=
Comment[mr]=
Comment[nb]=Gå til Internett
Comment[nl]=Verbinding maken met internet
Comment[or]=
Comment[pl]=Skorzystaj z internetu
Comment[pt]=Aceder à Internet
Comment[pt_BR]=Acessar a internet
Comment[ro]=Accesaţi Internetul
Comment[ru]=Доступ в Интернет
Comment[sk]=Prístup do siete Internet
Comment[sl]=Dostop do interneta
Comment[sr]=Приступите Интернету
Comment[sv]=Gå ut på Internet
Comment[ta]=
Comment[te]= ి
Comment[th]=
Comment[tr]=İnternet'e erişin
Comment[uk]=Доступ до Інтернету
Comment[vi]=Truy cp Internet
Comment[zh_CN]=访
Comment[zh_HK]=
Comment[zh_TW]=
Exec=@GOOGLE_CHROME_STABLE@ %U
StartupNotify=true
Terminal=false
Icon=google-chrome
Type=Application
Categories=Network;WebBrowser;
MimeType=application/pdf;application/rdf+xml;application/rss+xml;application/xhtml+xml;application/xhtml_xml;application/xml;image/gif;image/jpeg;image/png;image/webp;text/html;text/xml;x-scheme-handler/http;x-scheme-handler/https;
Actions=new-window;new-private-window;
[Desktop Action new-window]
Name=New Window
Name[am]=
Name[ar]=نافذة جديدة
Name[bg]=Нов прозорец
Name[bn]=
Name[ca]=Finestra nova
Name[cs]=Nové okno
Name[da]=Nyt vindue
Name[de]=Neues Fenster
Name[el]=Νέο Παράθυρο
Name[en_GB]=New Window
Name[es]=Nueva ventana
Name[et]=Uus aken
Name[fa]=پنجره جدید
Name[fi]=Uusi ikkuna
Name[fil]=New Window
Name[fr]=Nouvelle fenêtre
Name[gu]= િ
Name[hi]= ि
Name[hr]=Novi prozor
Name[hu]=Új ablak
Name[id]=Jendela Baru
Name[it]=Nuova finestra
Name[iw]=חלון חדש
Name[ja]=
Name[kn]= ಿ
Name[ko]=
Name[lt]=Naujas langas
Name[lv]=Jauns logs
Name[ml]=ി ി
Name[mr]= ि
Name[nl]=Nieuw venster
Name[no]=Nytt vindu
Name[pl]=Nowe okno
Name[pt]=Nova janela
Name[pt_BR]=Nova janela
Name[ro]=Fereastră nouă
Name[ru]=Новое окно
Name[sk]=Nové okno
Name[sl]=Novo okno
Name[sr]=Нови прозор
Name[sv]=Nytt fönster
Name[sw]=Dirisha Jipya
Name[ta]=ி
Name[te]= ి
Name[th]=
Name[tr]=Yeni Pencere
Name[uk]=Нове вікно
Name[vi]=Ca s Mi
Name[zh_CN]=
Name[zh_TW]=
Exec=@GOOGLE_CHROME_STABLE@
[Desktop Action new-private-window]
Name=New Incognito Window
Name[ar]=نافذة جديدة للتصفح المتخفي
Name[bg]=Нов прозорец инкогнито
Name[bn]=
Name[ca]=Finestra d'incògnit nova
Name[cs]=Nové anonymní okno
Name[da]=Nyt inkognitovindue
Name[de]=Neues Inkognito-Fenster
Name[el]=Νέο παράθυρο για ανώνυμη περιήγηση
Name[en_GB]=New Incognito window
Name[es]=Nueva ventana de incógnito
Name[et]=Uus inkognito aken
Name[fa]=پنجره جدید حالت ناشناس
Name[fi]=Uusi incognito-ikkuna
Name[fil]=Bagong Incognito window
Name[fr]=Nouvelle fenêtre de navigation privée
Name[gu]= િ
Name[hi]= ि
Name[hr]=Novi anoniman prozor
Name[hu]=Új Inkognitóablak
Name[id]=Jendela Penyamaran baru
Name[it]=Nuova finestra di navigazione in incognito
Name[iw]=חלון חדש לגלישה בסתר
Name[ja]=
Name[kn]= ಿ
Name[ko]= 릿
Name[lt]=Naujas inkognito langas
Name[lv]=Jauns inkognito režīma logs
Name[ml]=ി ി
Name[mr]= ि
Name[nl]=Nieuw incognitovenster
Name[no]=Nytt inkognitovindu
Name[pl]=Nowe okno incognito
Name[pt]=Nova janela de navegação anónima
Name[pt_BR]=Nova janela anônima
Name[ro]=Fereastră nouă incognito
Name[ru]=Новое окно в режиме инкогнито
Name[sk]=Nové okno inkognito
Name[sl]=Novo okno brez beleženja zgodovine
Name[sr]=Нови прозор за прегледање без архивирања
Name[sv]=Nytt inkognitofönster
Name[ta]=ி ி
Name[te]= ి
Name[th]=
Name[tr]=Yeni Gizli pencere
Name[uk]=Нове вікно в режимі анонімного перегляду
Name[vi]=Ca s n danh mi
Name[zh_CN]=
Name[zh_TW]=
Exec=@GOOGLE_CHROME_STABLE@ --incognito

60
users.nix Normal file
View file

@ -0,0 +1,60 @@
{
config,
pkgs,
...
}:
{
imports = [
./home/user.nix
./home/root.nix
];
sops.secrets."user/password" = {
neededForUsers = true;
sopsFile = ./secrets/home.yaml;
};
sops.secrets."root/password" = {
neededForUsers = true;
sopsFile = ./secrets/home.yaml;
};
users = {
mutableUsers = true;
users = {
root = {
homeMode = "700";
hashedPasswordFile = config.sops.secrets."root/password".path;
};
microvm = {
uid = 999;
isSystemUser = true;
};
# agent = {
# uid = 1001;
# homeMode = "770";
# shell = pkgs.fish;
# isNormalUser = true;
# group = "agents";
# extraGroups = [ "user" ];
# };
user = {
uid = 1000;
homeMode = "700";
home = "/home/user";
shell = pkgs.fish;
isNormalUser = true;
group = "user";
extraGroups = [
"libvirt"
"systemd-journal"
"kvm"
"agents"
];
hashedPasswordFile = config.sops.secrets."user/password".path;
};
};
groups = {
user.gid = 1000;
agents.gid = 777;
};
};
}

571
vms/default.nix Normal file
View file

@ -0,0 +1,571 @@
{
nixpkgs,
sops-nix,
impermanence,
home-manager,
...
}:
{
systemd.network.netdevs."20-microbr".netdevConfig = {
Kind = "bridge";
Name = "microbr";
};
systemd.network.networks."20-microbr" = {
matchConfig.Name = "microbr";
addresses = [ { Address = "192.168.77.1/24"; } ];
networkConfig = {
ConfigureWithoutCarrier = true;
};
};
systemd.network.networks."21-microvm-tap" = {
matchConfig.Name = "vm-*";
networkConfig.Bridge = "microbr";
};
networking.nat = {
enable = true;
internalInterfaces = [ "microbr" ];
externalInterface = "enp7e0";
};
networking.nftables = {
enable = true;
tables.nat = {
family = "ip";
content = ''
chain postrouting {
type nat hook postrouting priority srcnat;
iifname "microbr" masquerade
}
'';
};
};
microvm.vms = {
"dealwise" = {
pkgs = import nixpkgs {
system = "x86_64-linux";
config.allowUnfreePredicate =
pkg:
builtins.elem (nixpkgs.lib.getName pkg) [
"claude-code"
];
};
config =
let
hostname = "ai-sandbox";
mac = "02:00:00:00:00:06";
in
{
config,
pkgs,
...
}:
{
imports = [
impermanence.nixosModules.impermanence
sops-nix.nixosModules.sops
home-manager.nixosModules.home-manager
];
sops = {
defaultSopsFile = ./secrets/secrets.yaml;
age.keyFile = "/.persist/root/.config/sops/age/keys.txt";
secrets = {
"wg0/private_key" = { };
};
};
boot.kernel.sysctl."kernel.unprivileged_userns_clone" = 1;
systemd.network = {
enable = true;
networks = {
"10-net" = {
matchConfig.MACAddress = mac;
linkConfig.RequiredForOnline = "routable";
addresses = [ { Address = "192.168.77.2/24"; } ];
routes = [
{
Gateway = "192.168.77.1";
Metric = 100;
}
{
Destination = "103.69.224.4/32";
Gateway = "192.168.77.1";
}
];
};
};
};
services.resolved.enable = false;
environment.etc."resolv.conf".text = ''
nameserver 10.2.0.1
'';
networking = {
hostName = hostname;
useNetworkd = true;
useDHCP = false;
firewall.enable = false;
wireguard.interfaces.wg0 = {
ips = [ "10.2.0.2/32" ];
listenPort = 45974;
privateKeyFile = config.sops.secrets."wg0/private_key".path;
metric = 10;
peers = [
{
publicKey = "D8Sqlj3TYwwnTkycV08HAlxcXXS3Ura4oamz8rB5ImM=";
endpoint = "103.69.224.4:51820";
allowedIPs = [
"0.0.0.0/0"
"::/0"
];
persistentKeepalive = 25;
}
];
};
};
users.mutableUsers = false;
users.users.root = {
password = "";
home = "/root";
openssh.authorizedKeys.keys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILABd/iSJ4gn/ystDqNxLJTG0n0z5VIC9YXlmdUfOhHf desktop@icefox.sh"
];
};
users.users.user = {
linger = true;
home = "/home/user";
password = "";
group = "user";
isNormalUser = true;
uid = 1000;
shell = pkgs.fish;
openssh.authorizedKeys.keys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILABd/iSJ4gn/ystDqNxLJTG0n0z5VIC9YXlmdUfOhHf desktop@icefox.sh"
];
};
users.groups.user.gid = 1000;
home-manager = {
useGlobalPkgs = true;
useUserPackages = true;
users.user = {
home.username = "user";
home.homeDirectory = "/home/user";
home.stateVersion = "25.11";
home.enableNixpkgsReleaseCheck = false;
xdg.configFile."containers/containers.conf".text = ''
[engine]
compose_warning_logs=false
events_logger="file"
[containers]
log_driver="k8s-file"
'';
xdg.configFile."opencode/opencode.json".text = builtins.toJSON {
"$schema" = "https://opencode.ai/config.json";
plugin = [ "opencode-antigravity-auth@latest" ];
provider = {
google = {
models = {
antigravity-gemini-3-pro = {
name = "Gemini 3 Pro (Antigravity)";
limit = {
context = 1048576;
output = 65535;
};
modalities = {
input = [
"text"
"image"
"pdf"
];
output = [ "text" ];
};
variants = {
low = {
thinkingLevel = "low";
};
high = {
thinkingLevel = "high";
};
};
};
antigravity-gemini-3-flash = {
name = "Gemini 3 Flash (Antigravity)";
limit = {
context = 1048576;
output = 65536;
};
modalities = {
input = [
"text"
"image"
"pdf"
];
output = [ "text" ];
};
variants = {
minimal = {
thinkingLevel = "minimal";
};
low = {
thinkingLevel = "low";
};
medium = {
thinkingLevel = "medium";
};
high = {
thinkingLevel = "high";
};
};
};
antigravity-claude-sonnet-4-5 = {
name = "Claude Sonnet 4.5 (Antigravity)";
limit = {
context = 200000;
output = 64000;
};
modalities = {
input = [
"text"
"image"
"pdf"
];
output = [ "text" ];
};
};
antigravity-claude-sonnet-4-5-thinking = {
name = "Claude Sonnet 4.5 Thinking (Antigravity)";
limit = {
context = 200000;
output = 64000;
};
modalities = {
input = [
"text"
"image"
"pdf"
];
output = [ "text" ];
};
variants = {
low = {
thinkingConfig = {
thinkingBudget = 8192;
};
};
max = {
thinkingConfig = {
thinkingBudget = 32768;
};
};
};
};
antigravity-claude-opus-4-5-thinking = {
name = "Claude Opus 4.5 Thinking (Antigravity)";
limit = {
context = 200000;
output = 64000;
};
modalities = {
input = [
"text"
"image"
"pdf"
];
output = [ "text" ];
};
variants = {
low = {
thinkingConfig = {
thinkingBudget = 8192;
};
};
max = {
thinkingConfig = {
thinkingBudget = 32768;
};
};
};
};
antigravity-claude-opus-4-6-thinking = {
name = "Claude Opus 4.6 Thinking (Antigravity)";
limit = {
context = 200000;
output = 64000;
};
modalities = {
input = [
"text"
"image"
"pdf"
];
output = [ "text" ];
};
variants = {
low = {
thinkingConfig = {
thinkingBudget = 8192;
};
};
max = {
thinkingConfig = {
thinkingBudget = 32768;
};
};
};
};
"gemini-2.5-flash" = {
name = "Gemini 2.5 Flash (Gemini CLI)";
limit = {
context = 1048576;
output = 65536;
};
modalities = {
input = [
"text"
"image"
"pdf"
];
output = [ "text" ];
};
};
"gemini-2.5-pro" = {
name = "Gemini 2.5 Pro (Gemini CLI)";
limit = {
context = 1048576;
output = 65536;
};
modalities = {
input = [
"text"
"image"
"pdf"
];
output = [ "text" ];
};
};
gemini-3-flash-preview = {
name = "Gemini 3 Flash Preview (Gemini CLI)";
limit = {
context = 1048576;
output = 65536;
};
modalities = {
input = [
"text"
"image"
"pdf"
];
output = [ "text" ];
};
};
gemini-3-pro-preview = {
name = "Gemini 3 Pro Preview (Gemini CLI)";
limit = {
context = 1048576;
output = 65535;
};
modalities = {
input = [
"text"
"image"
"pdf"
];
output = [ "text" ];
};
};
};
};
};
};
};
};
fileSystems = {
"/.persist".neededForBoot = true;
};
environment.systemPackages = with pkgs; [
coreutils
jq
git
fzf
claude-code
neovim
ripgrep
fd
podman-compose
opencode
php
php.packages.composer
pkgs.nodejs_24
pkgs.dotnet-sdk_9
pkgs.go_1_24
];
programs = {
fish.enable = true;
starship.enable = true;
ssh = {
knownHosts = {
"github.com".publicKey =
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl";
};
};
};
systemd.tmpfiles.rules = [
"d /var/log/laravel 0755 1000 1000"
];
environment.persistence."/.persist" = {
enable = true;
hideMounts = true;
directories = [
"/var/lib/nixos"
];
files = [
"/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"
];
users.root = {
files = [
".config/sops/age/keys.txt"
];
};
users.user = {
files = [
".claude.json"
".claude.json.backup"
];
directories = [
".claude"
".local/share/containers"
".local/share/opencode"
];
};
};
services = {
openssh = {
enable = true;
ports = [ 22 ];
settings = {
PasswordAuthentication = false;
KbdInteractiveAuthentication = false;
PermitRootLogin = "yes";
AllowUsers = [
"user"
"root"
];
};
};
getty = {
autologinUser = "root";
autologinOnce = true;
};
};
virtualisation = {
containers.enable = true;
podman = {
enable = true;
defaultNetwork.settings.dns_enabled = true;
dockerCompat = true;
};
};
environment.sessionVariables = {
EDITOR = "nvim";
};
microvm = {
hypervisor = "qemu";
vcpu = 4;
mem = 8192;
socket = "control.sock";
interfaces = [
{
id = "vm-${hostname}";
type = "tap";
mac = mac;
}
];
volumes = [
{
mountPoint = "/.persist";
image = "persist.img";
size = 1024 * 128;
}
{
mountPoint = "/nix/.rw-store";
image = "nix-store.img";
size = 1024 * 128;
}
];
writableStoreOverlay = "/nix/.rw-store";
shares = [
{
proto = "virtiofs";
tag = "downloads";
source = "/home/user/downloads";
mountPoint = "/home/user/downloads";
}
{
proto = "virtiofs";
tag = "pictures";
source = "/home/user/pictures";
mountPoint = "/home/user/pictures";
}
{
proto = "virtiofs";
tag = "dealwise";
source = "/home/user/work/dealwise";
mountPoint = "/home/user/work/dealwise";
}
{
proto = "virtiofs";
tag = "php-data-transfer-object";
source = "/home/user/dev/icefox/php/data-transfer-object";
mountPoint = "/home/user/dev/icefox/php/data-transfer-object";
}
{
proto = "virtiofs";
tag = "uni";
source = "/home/user/uni";
mountPoint = "/home/user/uni";
}
{
proto = "virtiofs";
tag = "dev";
source = "/home/user/dev";
mountPoint = "/home/user/dev";
}
{
proto = "virtiofs";
tag = "ro-store";
source = "/nix/store";
mountPoint = "/nix/.ro-store";
}
];
qemu.extraArgs = [
"-cpu"
"host"
];
};
system.stateVersion = "25.11";
};
};
};
}

32
vms/secrets/secrets.yaml Normal file
View file

@ -0,0 +1,32 @@
ssh:
private_key: ENC[AES256_GCM,data:0EDM0MEirn14hmU3UV0yFcB6kG8zjYyt+sbIb6v2vr3sqaK0EmBf4VxX1TRxjlT3nDk7NZG60+Lw6+c45COhZX0ApYkElUxPtKXsLM4HZKruJiL3cLicdFDsL7/53tMyp6waFVejq5dbog/kya+dbUujM8p2ILqO9GjB2k0truipq6ThpinytRqu5xccjJzUbEqCrr1U2lKTIm6s83zAJfcrnwEipIoV2WqxjPpeGjGBKwfi4284O1J5zZHvbxB+CQ+4rHrkcsio4CEDLdV6s93d5FHARGt/Ni/ZXfcP2Yve5M4PrakliHVv7dVNrGqX6tDXZkhVlhzCHk0vY9dSTONuvlmeoFtSWpJXG8MfaGzmfkT+/F39U1TpbRyIPL4OyeidvBV4hTJuKmCOSraXnFwz5l3EnZhBbrCBRw8XFTZGS8v8jMcC7QTFYxCDL4GcrSJac9cgDlggygr3Vv8627a+zYSwauuNWPYEf1Nr56owZdJUc6LJpOBd+QR2fiV+coA/cNbNhWOaRS3K/NRS,iv:1lU+UUhH4m5OjyDO5s/sNGGGoT/7NxI5Cs1GL5CEIGU=,tag:EG8YZERDyeG/XkCNO7f/cQ==,type:str]
wg0:
private_key: ENC[AES256_GCM,data:nr7y3wp7EtVW6uI6MBSwyMO9YuMyx/F0AZmD8GmuA3BPQTVTsVSctoKIxLE=,iv:KN68DwGuDo+aPP4mBk1MqY+lxFjisKSwXn0w+yngDRQ=,tag:gpjxIFWaZE+5hbYHVsO1ZQ==,type:str]
address: ENC[AES256_GCM,data:9Tnph2SHKeEt9Ss=,iv:CPR1N7fqqlaThGltSpfqeAOc5bAe13KWskGWj3jI8LQ=,tag:xha/hQOVqfUoGyfKbHhnuQ==,type:str]
conf: ENC[AES256_GCM,data:SRDnI+2PvK7Zz1L5XBvrBNejgJEg8DK+qVO5XEtx6Nal+f7IeB3Ascp8Bkit5fd5myn/RxiK80wYmvLkDmcJAk46UjHKOpbxJl1s5FmKDuZJ3c3MXLwH7k2PeZP14VDDlyQqlcyGBrSu74L64ZMh/6EWGKbONTD1Wt3Ykg+/RegzQFDr2CPbj6XQeXsNS2p0ugicP5ffBMTUa9KSYDMQVV80mjSZ246aeY0owU1VUsitdvsCbfxtFd5gr/9zdfOXOvGY/BKxAlvVbszCalNs9DgJDHt/,iv:FP90SvUGnsZJS7F/uxtbOqTvGOgtC4+r2+YgF5FBoQY=,tag:9G1tkXHTpbytmG9T6sTpMw==,type:str]
wg-br0:
private_key: ENC[AES256_GCM,data:AwGwtS6Bkx5SUwxfaz/UaogGQIwqJidHzyOC0EWCA1UzEo1XV+bFKpdvOjg=,iv:O5RTjtNHC3lY+uVb6JBTwCrxpDSOsVAy8VOvsSatr0M=,tag:HelKY1PtxI3Zi+9Alrw+Ow==,type:str]
sops:
age:
- recipient: age1y0tj3kt67pfnj38t9c8g2ghry3a0mhcq8rrqv5xr4jekwepxaelqzu3dkf
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBtOHZSRkpBVVdUUk9OYUFH
cVBra014WXJyRTJ0QWFKallLQlc0SXhNSlFBCmpwME92M2lCN2liVjZBRndlSVBk
OEpUU1YyakdCa0xVaHdhRlpXbGxYdUEKLS0tIDFlV1k0Qkx1UDd2NUVHTTI3NDZE
OWhIdUxDcHB4Z3dTdDkyZWF6NEJCYzAKfPB9AZFQ08yqil+4AhIi6EMy8PXI4CAz
lK4ON/M67T0UrlWN/m3pryOOr4Lj4oiZvdOR0BCO3kn4Pj0nq5jQOA==
-----END AGE ENCRYPTED FILE-----
- recipient: age16v8w7q4wmn22hhakq2uzaus2508rhldm7lcwh0kukshzjzyhuqesqz44ze
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBMSC9Td1NTMzk2NlJDTDNM
UVUzTSt1dGkrUVRGT1UzeXcwR1REN1U0dW5JCnNJRzdKZHVyR0dzaUw2TlVzQnQ2
SHhSSGlDWUNBSXZiME5GM0JPTFRseDQKLS0tIEFnOXgzWFo2Rmo2THN4VFFIY1h0
OEZ4WUp1QlVrTkVTN1BHMG0yaXFuSk0KLw3ZuvWTurJDTpyoq5YafLm8YFT4v4Vh
s+ay8ju3kA1CKjMF3gBQF08EoCdP/jU6tZerNwwcs17el5zIvRmG7Q==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2026-02-06T22:06:59Z"
mac: ENC[AES256_GCM,data:IJXeoVdP8/R51hHNTkpYSj9f1bGRBh5PtlEdbcXuD12DFGZtEFcAeBgfKHSnYBRxZMedd/IxhsQYNatW8T/spAuPi0dEh2mnn9yz3evGjkc1WKGOy24Ou3xhZBboo9tzYfkX3PVGd10kx+vTJh3by7Eq4LjAfyq1vyGj1g3S5nU=,iv:wQsntFE/TO0Z5An9U7yYUIQ/nXbo5nnUQ9ukVMm0KRo=,tag:D9HpVrYEbzaCktzGmD0xvg==,type:str]
unencrypted_suffix: _unencrypted
version: 3.11.0

166
wireguard.nix Normal file
View file

@ -0,0 +1,166 @@
{
config,
lib,
pkgs,
...
}:
with lib;
let
cfg = config.services.wireguard-netns;
vpnOptions =
{ name, ... }:
{
options = {
name = mkOption {
type = types.str;
default = name;
description = "Name of the network namespace and WireGuard interface";
};
dns = mkOption {
type = types.str;
description = "Network DNS";
example = "1.1.1.1";
};
address = mkOption {
type = types.str;
description = "Address";
example = "10.2.0.1/32";
};
conf = mkOption {
type = types.str;
description = "Path to sops secret containing WireGuard configuration";
example = "wg0/conf";
};
};
};
in
{
options.services.wireguard-netns = {
enable = mkEnableOption "WireGuard network namespaces";
namespaces = mkOption {
type = types.attrsOf (types.submodule vpnOptions);
default = { };
description = "WireGuard VPN configurations in separate network namespaces";
example = literalExpression ''
{
wg0ns = {
dns = "10.2.0.1";
address = "10.2.0.2/32";
conf = "wg0/conf";
};
wg1ns = {
dns = "10.3.0.1";
address = "10.3.0.2/32";
conf = "wg1/conf";
};
}
'';
};
};
config = mkIf cfg.enable {
systemd.services = listToAttrs (
flatten (
imap1 (
index: elem:
let
name = elem.name;
vpnCfg = elem.value;
ns = "${name}ns";
nsIP = "10.200.${toString index}.2/24";
hostIP = "10.200.${toString index}.1/24";
vethHost = "veth-${name}";
vethNS = "veth-${name}-ns";
proxyIP = "10.200.${toString index}.2"; # IP without CIDR for binding
proxyPort = 1080;
in
[
{
name = "netns@${ns}";
value = {
description = "${ns} network namespace";
before = [ "network.target" ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
ExecStart = pkgs.writers.writeBash "${ns}-up" ''
${pkgs.coreutils}/bin/mkdir -p /etc/netns/${ns}
echo "nameserver ${vpnCfg.dns}" > /etc/netns/${ns}/resolv.conf
${pkgs.iproute2}/bin/ip netns add ${ns}
${pkgs.iproute2}/bin/ip link add ${vethHost} type veth peer name ${vethNS}
${pkgs.iproute2}/bin/ip link set ${vethNS} netns ${ns}
${pkgs.iproute2}/bin/ip addr add ${hostIP} dev ${vethHost}
${pkgs.iproute2}/bin/ip link set ${vethHost} up
${pkgs.iproute2}/bin/ip -n ${ns} addr add ${nsIP} dev ${vethNS}
${pkgs.iproute2}/bin/ip -n ${ns} link set ${vethNS} up
'';
ExecStop = pkgs.writers.writeBash "${ns}-down" ''
${pkgs.iproute2}/bin/ip link del ${vethHost} || true
${pkgs.iproute2}/bin/ip netns del ${ns}
'';
};
};
}
{
name = name;
value = {
description = "${name} network interface";
bindsTo = [ "netns@${ns}.service" ];
requires = [ "network-online.target" ];
after = [ "netns@${ns}.service" ];
wants = [ "network-online.target" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
ExecStart = pkgs.writers.writeBash "${name}-up" ''
${pkgs.iproute2}/bin/ip link add ${name} type wireguard
${pkgs.iproute2}/bin/ip link set ${name} netns ${ns}
${pkgs.iproute2}/bin/ip -n ${ns} address add ${vpnCfg.address} dev ${name}
${pkgs.iproute2}/bin/ip netns exec ${ns} \
${pkgs.wireguard-tools}/bin/wg setconf ${name} ${config.sops.secrets.${vpnCfg.conf}.path}
${pkgs.iproute2}/bin/ip -n ${ns} link set lo up
${pkgs.iproute2}/bin/ip -n ${ns} link set ${name} up
${pkgs.iproute2}/bin/ip -n ${ns} route add default dev ${name}
'';
ExecStop = pkgs.writers.writeBash "${name}-down" ''
${pkgs.iproute2}/bin/ip -n ${ns} route del default dev ${name} || true
${pkgs.iproute2}/bin/ip -n ${ns} link del ${name} || true
'';
};
};
}
{
name = "socks-${name}";
value = {
description = "SOCKS5 proxy for ${name} namespace";
after = [ "${name}.service" ];
requires = [ "${name}.service" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
Type = "simple";
Restart = "always";
RestartSec = "5s";
ExecStart = "${pkgs.iproute2}/bin/ip netns exec ${ns} ${pkgs.microsocks}/bin/microsocks -i ${proxyIP} -p ${toString proxyPort}";
};
};
}
]
) (lib.attrsToList cfg.namespaces)
)
);
};
}