├── ansible ├── provision.yml ├── .dir-locals.el ├── configure.yml ├── inventories │ └── home │ │ ├── host_vars │ │ ├── hass.yml │ │ ├── dnscontrol.yml │ │ ├── qdevice-vm.yml │ │ └── pbs-hetzner.yml │ │ └── hosts.yml ├── roles │ └── sshd │ │ ├── tests │ │ ├── inventory │ │ └── test.yml │ │ ├── vars │ │ └── main.yml │ │ ├── handlers │ │ └── main.yml │ │ ├── templates │ │ ├── authorized_principals.j2 │ │ ├── trusted_user_ca_keys.j2 │ │ └── authorized_keys.j2 │ │ ├── tasks │ │ ├── safe-sshd-restart.yml │ │ └── main.yml │ │ ├── meta │ │ └── main.yml │ │ ├── defaults │ │ └── main.yml │ │ └── README.md ├── handlers │ └── update-initramfs.yaml ├── playbooks │ ├── manage-ssh-access.yml │ ├── pve │ │ ├── tasks │ │ │ ├── empty-systctl-conf.yml │ │ │ ├── interactive-cli.yml │ │ │ ├── no-nag.yml │ │ │ ├── sanoid.yml │ │ │ ├── tailscale-docker.yml │ │ │ ├── tailscale-hosts.yml │ │ │ ├── backup-raum-to-bael.yml │ │ │ ├── etckeeper.yml │ │ │ ├── tpm2-ssh.yml │ │ │ └── debian-non-free-firmware.yml │ │ ├── files │ │ │ ├── backup-raum.service │ │ │ ├── no-nag-script │ │ │ ├── backup-raum.timer │ │ │ ├── backup-raum │ │ │ ├── valak-sanoid.conf │ │ │ ├── raum-sanoid.conf │ │ │ └── bael-sanoid.conf │ │ ├── vars │ │ │ └── main.yml │ │ └── main.yml │ ├── clevis-luks.yml │ └── tailscale-exit-nodes.yml ├── tasks │ └── dropbear-authorized-keys.yaml ├── requirements.yml ├── ansible.cfg ├── .ansible-lint └── files │ └── initramfs-curl-hook ├── secrets ├── iso │ ├── secrets.yaml │ └── user-binarin.yaml ├── mail │ ├── user-binarin.yaml │ ├── dovecot-passwd.bin │ └── secrets.yaml ├── media │ └── user-binarin.yaml ├── qdevice │ ├── secrets.yaml │ ├── user-binarin.yaml │ ├── luks.jwe │ ├── ssh_host_rsa_key.jwe │ └── ssh_host_ed25519_key.jwe ├── forgejo │ ├── user-binarin.yaml │ └── secrets.yaml ├── monitor │ ├── user-binarin.yaml │ └── secrets.yaml ├── nix-cache │ ├── user-binarin.yaml │ └── secrets.yaml ├── docker-on-nixos │ └── user-binarin.yaml ├── dnscontrol │ ├── creds.json │ └── dnsconfig.js ├── furfur │ ├── ssh_host_ed25519_key.pub │ ├── ssh_host_ecdsa_key.pub │ ├── ssh_host_rsa_key.pub │ ├── user-binarin-age │ ├── ssh_host_ed25519_key │ ├── ssh_host_ecdsa_key │ ├── user-binarin.yaml │ └── secrets.yaml ├── ishamael │ ├── secrets.yaml │ └── user-binarin.yaml └── demandred │ ├── secrets.yaml │ └── user-binarin.yaml ├── files ├── yasnippets │ └── devicetree-ts-mode │ │ ├── .gitkeep │ │ └── zmk-macro-0 ├── .dir-locals.el ├── agares-guest.git-crypt ├── dashboard-icons │ ├── pbs.png │ ├── sipeed.png │ ├── tiny-pilot.png │ ├── tube-archivist-logo-dark.png │ └── sipeed.png.base64 ├── ublock-filters.txt ├── org-protocol.desktop ├── website-google.css ├── nixos-configuration-nix-config-for-impure-nix-commands.nix ├── org-protocol.ps1 ├── nixos-configuration-overlays-for-impure-nix-commands.nix ├── rust-fix.patch ├── firefox-userChrome.css ├── git-commit-template.txt ├── 0002-remove-fapi-message.patch ├── website-hackernews.css ├── pve-impermanence-hook.pl ├── byte-compile.el └── wezterm.lua ├── .envrc ├── AGENTS.md ├── bootstrap.gpg ├── .beads ├── metadata.json ├── .gitignore ├── config.yaml └── README.md ├── terraform ├── network-config.cfg ├── qdevice-vm.tfvars ├── cloud-init.cfg ├── outputs.tf ├── variables.tf └── main.tf ├── modules ├── standard-linux-tools.nix ├── disko.nix ├── systemd-boot.nix ├── binarin │ ├── swaync.nix │ ├── wl-kbptr │ │ ├── default.nix │ │ └── config │ ├── gnupg.nix │ ├── nix-dev.nix │ ├── syncthing.nix │ ├── podman.nix │ ├── hypridle.nix │ └── workstation.nix ├── programs │ ├── fuzzel.nix │ ├── direnv.nix │ ├── insync.nix │ ├── claude-code │ │ └── default.nix │ ├── git.nix │ └── linkwarden.nix ├── packages │ ├── caddy-with-cloudflare-dns.nix │ ├── sshmenu.nix │ └── brightnessctl-all.nix ├── eternal-terminal.nix ├── srvos.nix ├── devshell.nix ├── gc.nix ├── security.nix ├── large-console-fonts.nix ├── wayland.nix ├── home-modules.nix ├── formatter.nix ├── default.nix ├── bluetooth.nix ├── fonts.nix ├── nixos-hardware.nix ├── sshd.nix ├── tpm2-ssh.nix ├── machines │ ├── devcontainer.nix │ ├── mail.nix │ └── demandred.nix ├── impure-nix-setup.nix ├── flake-packages.nix ├── home-manager.nix ├── nix.nix ├── sops.nix ├── foot.nix ├── baseline │ └── default.nix ├── deploy.nix ├── lxc.nix ├── expose-local-http.nix ├── wezterm.nix ├── monitored.nix ├── public-keys.nix ├── inputs-old.nix ├── niri │ └── default.nix ├── xdg-autostart.nix └── initrd-ssh.nix ├── .gitattributes ├── .gitignore ├── inventory ├── users-groups.nix └── host-id.nix ├── scripts ├── niri-layout-toggle.sh ├── build-nixos.sh ├── iommu.sh ├── ai-container.sh ├── org-capture-clipboard.sh ├── eval-nixos.sh ├── nixos-anywhere.sh └── check-module-keys.sh ├── machines ├── furfur │ ├── hardware-configuration.nix │ └── disko.nix ├── qdevice │ ├── hardware-configuration.nix │ └── disko.nix └── demandred │ └── disko.nix ├── .forgejo └── workflows │ └── master.yaml ├── TODO.org └── lib └── networks-lookup.nix /ansible/provision.yml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /secrets/iso/secrets.yaml: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /secrets/iso/user-binarin.yaml: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /secrets/mail/user-binarin.yaml: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /secrets/media/user-binarin.yaml: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /secrets/qdevice/secrets.yaml: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /secrets/forgejo/user-binarin.yaml: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /secrets/monitor/user-binarin.yaml: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /secrets/nix-cache/user-binarin.yaml: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /secrets/qdevice/user-binarin.yaml: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /files/yasnippets/devicetree-ts-mode/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /secrets/docker-on-nixos/user-binarin.yaml: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /.envrc: -------------------------------------------------------------------------------- 1 | watch_file modules/devshell.nix 2 | use flake 3 | -------------------------------------------------------------------------------- /ansible/.dir-locals.el: -------------------------------------------------------------------------------- 1 | ((yaml-mode . ((mode . ansible)))) 2 | -------------------------------------------------------------------------------- /ansible/configure.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - import_playbook: playbooks/pve/main.yml 3 | -------------------------------------------------------------------------------- /files/.dir-locals.el: -------------------------------------------------------------------------------- 1 | ((org-mode . ((org-confirm-babel-evaluate . nil)))) 2 | -------------------------------------------------------------------------------- /AGENTS.md: -------------------------------------------------------------------------------- 1 | BEFORE ANYTHING ELSE: run 'bd onboard' and follow the instructions 2 | -------------------------------------------------------------------------------- /ansible/inventories/home/host_vars/hass.yml: -------------------------------------------------------------------------------- 1 | --- 2 | ssh_users: 3 | - binarin 4 | -------------------------------------------------------------------------------- /bootstrap.gpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binarin/nixos-config/HEAD/bootstrap.gpg -------------------------------------------------------------------------------- /ansible/inventories/home/host_vars/dnscontrol.yml: -------------------------------------------------------------------------------- 1 | --- 2 | ssh_users: 3 | - binarin 4 | -------------------------------------------------------------------------------- /ansible/inventories/home/host_vars/qdevice-vm.yml: -------------------------------------------------------------------------------- 1 | --- 2 | ansible_host: 192.168.2.17 3 | -------------------------------------------------------------------------------- /ansible/roles/sshd/tests/inventory: -------------------------------------------------------------------------------- 1 | #SPDX-License-Identifier: MIT-0 2 | localhost 3 | 4 | -------------------------------------------------------------------------------- /ansible/inventories/home/host_vars/pbs-hetzner.yml: -------------------------------------------------------------------------------- 1 | --- 2 | ansible_host: pbs-hetzner.binarin.info 3 | -------------------------------------------------------------------------------- /ansible/roles/sshd/vars/main.yml: -------------------------------------------------------------------------------- 1 | #SPDX-License-Identifier: MIT-0 2 | --- 3 | # vars file for sshd 4 | -------------------------------------------------------------------------------- /secrets/qdevice/luks.jwe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binarin/nixos-config/HEAD/secrets/qdevice/luks.jwe -------------------------------------------------------------------------------- /files/agares-guest.git-crypt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binarin/nixos-config/HEAD/files/agares-guest.git-crypt -------------------------------------------------------------------------------- /files/dashboard-icons/pbs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binarin/nixos-config/HEAD/files/dashboard-icons/pbs.png -------------------------------------------------------------------------------- /secrets/dnscontrol/creds.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binarin/nixos-config/HEAD/secrets/dnscontrol/creds.json -------------------------------------------------------------------------------- /secrets/dnscontrol/dnsconfig.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binarin/nixos-config/HEAD/secrets/dnscontrol/dnsconfig.js -------------------------------------------------------------------------------- /files/dashboard-icons/sipeed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binarin/nixos-config/HEAD/files/dashboard-icons/sipeed.png -------------------------------------------------------------------------------- /files/ublock-filters.txt: -------------------------------------------------------------------------------- 1 | janet.guide##*:style(font-family: "Noto Sans" !important; src: local(Noto Sans) !important;) 2 | -------------------------------------------------------------------------------- /files/dashboard-icons/tiny-pilot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binarin/nixos-config/HEAD/files/dashboard-icons/tiny-pilot.png -------------------------------------------------------------------------------- /secrets/qdevice/ssh_host_rsa_key.jwe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binarin/nixos-config/HEAD/secrets/qdevice/ssh_host_rsa_key.jwe -------------------------------------------------------------------------------- /.beads/metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "database": "beads.db", 3 | "jsonl_export": "issues.jsonl", 4 | "last_bd_version": "0.27.2" 5 | } -------------------------------------------------------------------------------- /secrets/qdevice/ssh_host_ed25519_key.jwe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binarin/nixos-config/HEAD/secrets/qdevice/ssh_host_ed25519_key.jwe -------------------------------------------------------------------------------- /secrets/furfur/ssh_host_ed25519_key.pub: -------------------------------------------------------------------------------- 1 | ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEAd05jj+pcxIX1kimt603tyAGSvmjAimud2i4NEwIgS binarin@demandred 2 | -------------------------------------------------------------------------------- /ansible/roles/sshd/handlers/main.yml: -------------------------------------------------------------------------------- 1 | #SPDX-License-Identifier: MIT-0 2 | --- 3 | - name: restart sshd 4 | include_tasks: safe-sshd-restart.yml 5 | -------------------------------------------------------------------------------- /ansible/roles/sshd/tests/test.yml: -------------------------------------------------------------------------------- 1 | #SPDX-License-Identifier: MIT-0 2 | --- 3 | - hosts: localhost 4 | remote_user: root 5 | roles: 6 | - sshd 7 | -------------------------------------------------------------------------------- /files/dashboard-icons/tube-archivist-logo-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binarin/nixos-config/HEAD/files/dashboard-icons/tube-archivist-logo-dark.png -------------------------------------------------------------------------------- /ansible/roles/sshd/templates/authorized_principals.j2: -------------------------------------------------------------------------------- 1 | # {{ ansible_managed }} 2 | {%+ for principal in ssh_principals %} 3 | {{ principal }} 4 | {% endfor %} 5 | -------------------------------------------------------------------------------- /ansible/handlers/update-initramfs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Update initrd via initramfs-tools 3 | listen: update-initramfs 4 | command: update-initramfs -u -k all 5 | -------------------------------------------------------------------------------- /ansible/playbooks/manage-ssh-access.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: manage_ssh_access 3 | tags: ssh 4 | vars_files: 5 | - ./ssh-public-keys.yaml 6 | roles: 7 | - sshd 8 | -------------------------------------------------------------------------------- /files/org-protocol.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=org-protocol 3 | Exec=emacsclient %u 4 | Type=Application 5 | Terminal=false 6 | Categories=System; 7 | MimeType=x-scheme-handler/org-protocol; 8 | -------------------------------------------------------------------------------- /ansible/playbooks/pve/tasks/empty-systctl-conf.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Make sure /etc/sysctl.conf is empty 3 | ansible.builtin.copy: 4 | content: "" 5 | dest: /etc/sysctl.conf 6 | force: true 7 | -------------------------------------------------------------------------------- /secrets/furfur/ssh_host_ecdsa_key.pub: -------------------------------------------------------------------------------- 1 | ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBKTHfBGb2gce76k86ByRcbIWB1MTUGRuiLtVzbiD+sQ6vI8tA5BiCVGGyevS9IGspq43MMYb1NFCWt88rkYeITI= binarin@demandred 2 | -------------------------------------------------------------------------------- /terraform/network-config.cfg: -------------------------------------------------------------------------------- 1 | network: 2 | version: 2 3 | ethernets: 4 | ens3: 5 | addresses: [${static_ip}/24] 6 | gateway4: ${gateway} 7 | nameservers: 8 | addresses: [${dns_servers}] 9 | -------------------------------------------------------------------------------- /ansible/roles/sshd/tasks/safe-sshd-restart.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Test sshd_config 3 | ansible.builtin.command: sshd -t 4 | changed_when: false 5 | - name: Restart sshd service 6 | ansible.builtin.service: 7 | name: sshd 8 | state: restarted 9 | -------------------------------------------------------------------------------- /files/website-google.css: -------------------------------------------------------------------------------- 1 | @-moz-document domain("www.google.com"), domain("google.com") { 2 | @media (-moz-bool-pref: "user.theme.dark.zenburn") { 3 | .g, #appbar { 4 | background-color: var(--zenburn-bg) !important; 5 | } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /ansible/roles/sshd/templates/trusted_user_ca_keys.j2: -------------------------------------------------------------------------------- 1 | # {{ ansible_managed }} 2 | {%+ for key, value in ssh_keys.items() %} 3 | {% if ('tags' in value and 'user-ca' in value.tags) %} 4 | {{ value.public_key }} {{ value.description }} 5 | {% endif %} 6 | {% endfor %} 7 | -------------------------------------------------------------------------------- /ansible/tasks/dropbear-authorized-keys.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Provision authorized_keys for initramfs 3 | vars: 4 | username: root 5 | template: 6 | src: authorized_keys.j2 7 | dest: "/etc/dropbear/initramfs/authorized_keys" 8 | notify: update-initramfs 9 | -------------------------------------------------------------------------------- /modules/standard-linux-tools.nix: -------------------------------------------------------------------------------- 1 | { ... }: 2 | { 3 | flake.nixosModules.standard-linux-tools = 4 | { 5 | ... 6 | }: 7 | { 8 | key = "nixos-config.modules.nixos.standard-linux-tools"; 9 | 10 | config = { 11 | }; 12 | }; 13 | } 14 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | priv-*.nix filter=git-crypt diff=git-crypt 2 | secrets/dnscontrol/* filter=git-crypt diff=git-crypt 3 | *.jwe filter=git-crypt diff=git-crypt 4 | *.git-crypt filter=git-crypt diff=git-crypt 5 | 6 | # Use bd merge for beads JSONL files 7 | .beads/issues.jsonl merge=beads 8 | -------------------------------------------------------------------------------- /ansible/playbooks/pve/tasks/interactive-cli.yml: -------------------------------------------------------------------------------- 1 | - name: Install some generic linux tools 2 | ansible.builtin.package: 3 | state: present 4 | name: 5 | - emacs-nox 6 | - sysstat 7 | - iotop 8 | - net-tools #netstat 9 | - tmux 10 | - mosh 11 | -------------------------------------------------------------------------------- /ansible/playbooks/pve/files/backup-raum.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Backup ZFS volumes from raum->bael using syncoid 3 | Wants=backup-raum.timer 4 | 5 | [Service] 6 | Type=oneshot 7 | ExecStart=/usr/local/bin/backup-raum 8 | User=root 9 | 10 | [Install] 11 | WantedBy=multi-user.target 12 | -------------------------------------------------------------------------------- /ansible/playbooks/pve/files/no-nag-script: -------------------------------------------------------------------------------- 1 | DPkg::Post-Invoke { "dpkg -V proxmox-widget-toolkit | grep -q '/proxmoxlib\.js$'; if [ $? -eq 1 ]; then { echo 'Removing subscription nag from UI...'; sed -i '/.*data\.status.*/{s/\!//;s/active/NoMoreNagging/}' /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js; }; fi"; }; 2 | -------------------------------------------------------------------------------- /terraform/qdevice-vm.tfvars: -------------------------------------------------------------------------------- 1 | vm_name = "qdevice-vm" 2 | memory = 1024 3 | vcpu = 1 4 | disk_size = 10737418240 # 10GB 5 | static_ip = "192.168.2.17" 6 | gateway = "192.168.2.1" 7 | dns_servers = ["192.168.2.1"] 8 | network_name = "br0" 9 | libvirt_uri = "qemu+ssh://root@192.168.2.16/system" 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | result 3 | .direnv 4 | /ansible/home-network.yaml 5 | /.idea/ 6 | /.claude/ 7 | /ansible/ext-roles/ 8 | /terraform/.terraform/ 9 | /terraform/.terraform.lock.hcl 10 | /terraform/.terraform.tfstate.lock.info 11 | *.backup 12 | /ansible/ext-collections/ 13 | /.beads/.local_version 14 | /.ansible/ 15 | /ansible/.ansible/ 16 | -------------------------------------------------------------------------------- /modules/disko.nix: -------------------------------------------------------------------------------- 1 | { self, inputs, ... }: 2 | { 3 | flake.nixosModules.disko = 4 | { inventoryHostName, ... }: 5 | { 6 | key = "nixos-config.modules.nixos.disko"; 7 | imports = [ 8 | inputs.disko.nixosModules.disko 9 | "${self}/machines/${inventoryHostName}/disko.nix" 10 | ]; 11 | }; 12 | } 13 | -------------------------------------------------------------------------------- /modules/systemd-boot.nix: -------------------------------------------------------------------------------- 1 | { ... }: 2 | { 3 | 4 | flake.nixosModules.systemd-boot = 5 | { ... }: 6 | { 7 | key = "nixos-config.modules.nixos.systemd-boot"; 8 | config = { 9 | boot.loader.systemd-boot.enable = true; 10 | boot.loader.efi.canTouchEfiVariables = true; 11 | }; 12 | }; 13 | 14 | } 15 | -------------------------------------------------------------------------------- /ansible/playbooks/pve/files/backup-raum.timer: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Regularly copy snaphots of raum volumes that are important, but not enough to include them into a proper PBS backups 3 | Requires=backup-raum.service 4 | 5 | [Timer] 6 | Unit=backup-raum.service 7 | OnStartupSec=600 8 | OnUnitActiveSec=3600 9 | 10 | [Install] 11 | WantedBy=timers.target 12 | -------------------------------------------------------------------------------- /ansible/requirements.yml: -------------------------------------------------------------------------------- 1 | collections: 2 | - name: victoriametrics.cluster 3 | version: "2.37.0" 4 | - name: prometheus.prometheus 5 | version: "0.27.4" 6 | - name: ansible.posix 7 | version: "2.1.0" 8 | - name: artis3n.tailscale 9 | version: "1.1.0" 10 | 11 | roles: 12 | - name: aroberts.zfs_exporter 13 | version: "v1.0.0" 14 | -------------------------------------------------------------------------------- /modules/binarin/swaync.nix: -------------------------------------------------------------------------------- 1 | { ... }: 2 | { 3 | flake.homeModules.swaync = 4 | { ... }: 5 | { 6 | key = "nixos-config.modules.home.swaync"; 7 | services.swaync = { 8 | enable = true; 9 | settings = { 10 | positionX = "right"; 11 | positionY = "bottom"; 12 | }; 13 | }; 14 | }; 15 | } 16 | -------------------------------------------------------------------------------- /ansible/ansible.cfg: -------------------------------------------------------------------------------- 1 | [defaults] 2 | inventory = inventories/home/hosts.yml 3 | stdout_callback = debug 4 | # ansible_managed = Ansible managed: {file} modified on %Y-%m-%d %H:%M:%S by {uid} on {host} 5 | interpreter_python = auto_silent 6 | roles_path = ext-roles/:roles/ 7 | collections_path = ext-collections/:collections/ 8 | 9 | [ssh_connection] 10 | pipelining=True 11 | -------------------------------------------------------------------------------- /modules/programs/fuzzel.nix: -------------------------------------------------------------------------------- 1 | { ... }: 2 | { 3 | flake.homeModules.fuzzel = 4 | { pkgs, ... }: 5 | { 6 | key = "nixos-config.modules.home.fuzzel"; 7 | 8 | home.packages = with pkgs; [ 9 | fuzzel 10 | ]; 11 | 12 | impermanence.local-files = [ ".cache/fuzzel" ]; 13 | 14 | stylix.targets.fuzzel.enable = true; 15 | }; 16 | } 17 | -------------------------------------------------------------------------------- /files/nixos-configuration-nix-config-for-impure-nix-commands.nix: -------------------------------------------------------------------------------- 1 | let 2 | flake = 3 | with builtins; 4 | getFlake (unsafeDiscardStringContext (readFile /etc/nix/inputs/self-absolute-path)); 5 | host = builtins.head (builtins.split "\n" (builtins.readFile "/etc/hostname")); 6 | configuration = flake.nixosConfigurations."${host}"; 7 | in 8 | configuration.config.nixpkgs.config 9 | -------------------------------------------------------------------------------- /files/org-protocol.ps1: -------------------------------------------------------------------------------- 1 | Set-Location Registry::HKEY_CLASSES_ROOT 2 | New-Item -Path .\org-protocol -Value 'URL:Org Protocol' 3 | New-ItemProperty -Path .\org-protocol -Name 'URL Protocol' -Value '' 4 | New-Item -Path .\org-protocol\shell 5 | New-Item -Path .\org-protocol\shell\open 6 | New-Item -Path .\org-protocol\shell\open\command -Value '"C:\Windows\System32\wsl.exe" emacsclient "%1"' 7 | -------------------------------------------------------------------------------- /inventory/users-groups.nix: -------------------------------------------------------------------------------- 1 | { 2 | # System users/groups with fixed UIDs/GIDs for Docker volume permissions 3 | # This provides a centralized place to track UID/GID allocations across all services 4 | systemUsers = { 5 | archivebox = { 6 | uid = 2001; 7 | gid = 2001; 8 | }; 9 | karakeep = { 10 | uid = 2002; 11 | gid = 2002; 12 | }; 13 | }; 14 | } 15 | -------------------------------------------------------------------------------- /scripts/niri-layout-toggle.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | 4 | # Get the app_id of the focused window 5 | app_id=$(niri msg -j focused-window | jq -r '.app_id') 6 | 7 | # If it's emacs, use wtype to send Ctrl+\, otherwise toggle layout 8 | if [[ "$app_id" == "emacs" ]]; then 9 | wtype -M ctrl -M shift -M alt -M win '|' 10 | else 11 | niri msg action switch-layout next 12 | fi 13 | -------------------------------------------------------------------------------- /modules/packages/caddy-with-cloudflare-dns.nix: -------------------------------------------------------------------------------- 1 | { ... }: 2 | { 3 | perSystem = 4 | { pkgs, ... }: 5 | { 6 | packages.caddy-with-cloudflare-dns = pkgs.caddy.withPlugins { 7 | plugins = [ 8 | "github.com/caddy-dns/cloudflare@v0.0.0-20251022184029-2fc25ee62f40" 9 | ]; 10 | hash = "sha256-sexPn0LzErmK8ptUICUPSSqLNLYIy7F9M3JBJfyCpJQ"; 11 | }; 12 | }; 13 | } 14 | -------------------------------------------------------------------------------- /ansible/playbooks/pve/tasks/no-nag.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Install no-nag-script 3 | register: proxmox_no_nag_script 4 | ansible.builtin.copy: 5 | dest: /etc/apt/apt.conf.d/no-nag-script 6 | src: no-nag-script 7 | - name: Re-install proxmox-widget-toolkit to trigger no-nag-script 8 | when: proxmox_no_nag_script.changed 9 | ansible.builtin.command: 10 | apt --reinstall install proxmox-widget-toolkit -y 11 | -------------------------------------------------------------------------------- /files/yasnippets/devicetree-ts-mode/zmk-macro-0: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: zmk-macro-0 3 | # key: &&m0 4 | # expand-env: ((yas-indent-line 'fixed)) 5 | # -- 6 | ${1:$$(s-replace-regexp (rx (group (+ (not (in "A-Z" "a-z" "0-9" "_"))))) "_" yas-text)}: $1 { 7 | label = "${1:$(upcase yas-text)}"; 8 | compatible = "zmk,behavior-macro"; 9 | #binding-cells = <0>; 10 | bindings 11 | = <$0>; 12 | }; 13 | -------------------------------------------------------------------------------- /ansible/.ansible-lint: -------------------------------------------------------------------------------- 1 | --- 2 | # ansible-lint configuration 3 | 4 | exclude_paths: 5 | - ext-collections/ 6 | - ext-roles/ 7 | - .cache/ 8 | 9 | skip_list: 10 | - yaml[line-length] # Allow longer lines for readability 11 | - package-latest # Allow installing latest packages in home lab 12 | - var-naming[no-role-prefix] # Not using roles, so no role prefix needed 13 | 14 | # Use default rules for everything else 15 | -------------------------------------------------------------------------------- /modules/eternal-terminal.nix: -------------------------------------------------------------------------------- 1 | { ... }: 2 | { 3 | 4 | flake.nixosModules.eternal-terminal = 5 | { config, ... }: 6 | { 7 | key = "nixos-config.modules.nixos.eternal-terminal"; 8 | 9 | config = { 10 | services.eternal-terminal = { 11 | enable = config.services.openssh.enable; 12 | }; 13 | networking.firewall.allowedTCPPorts = [ config.services.eternal-terminal.port ]; 14 | }; 15 | }; 16 | } 17 | -------------------------------------------------------------------------------- /inventory/host-id.nix: -------------------------------------------------------------------------------- 1 | { 2 | # hostId command: head -c4 /dev/urandom | od -A none -t x4 | perl -nE 'm,(\w+), && print $1' 3 | forgejo = "9914ebd7"; 4 | monitor = "08779236"; 5 | media = "87bfa995"; 6 | nix-cache = "57f08d9c"; 7 | ishamael = "d545b657"; 8 | mail = "d8d300dc"; 9 | demandred = "e312c428"; 10 | iso = "aaaaaaaa"; 11 | docker-on-nixos = "be23faba"; 12 | qdevice = "f72453f5"; 13 | furfur = "0a7be8e6"; 14 | devcontainer = "86c445cf"; 15 | } 16 | -------------------------------------------------------------------------------- /ansible/playbooks/pve/tasks/sanoid.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Install sanoid 3 | ansible.builtin.package: 4 | state: present 5 | name: sanoid 6 | - name: Create sanoid config directory 7 | ansible.builtin.file: 8 | path: /etc/sanoid 9 | state: directory 10 | mode: '0755' 11 | owner: root 12 | group: root 13 | - name: Copy sanoid config 14 | ansible.builtin.copy: 15 | src: "{{ inventory_hostname }}-sanoid.conf" 16 | dest: /etc/sanoid/sanoid.conf 17 | mode: '0644' 18 | -------------------------------------------------------------------------------- /ansible/playbooks/pve/tasks/tailscale-docker.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Set UDP receive buffer size (for tailscale in CTs) 3 | ansible.posix.sysctl: 4 | name: net.core.rmem_max 5 | sysctl_file: /etc/sysctl.d/50-udp-buffers.conf 6 | value: '10000000' 7 | sysctl_set: true 8 | - name: Set UDP send buffer size (for tailscale in CTs) 9 | ansible.posix.sysctl: 10 | name: net.core.wmem_max 11 | value: '10000000' 12 | sysctl_file: /etc/sysctl.d/50-udp-buffers.conf 13 | sysctl_set: true 14 | -------------------------------------------------------------------------------- /files/nixos-configuration-overlays-for-impure-nix-commands.nix: -------------------------------------------------------------------------------- 1 | final: prev: 2 | let 3 | flake = 4 | with builtins; 5 | getFlake (unsafeDiscardStringContext (readFile /etc/nix/inputs/self-absolute-path)); 6 | host = builtins.head (builtins.split "\n" (builtins.readFile "/etc/hostname")); 7 | configuration = flake.nixosConfigurations."${host}"; 8 | configurationOverlays = configuration.config.nixpkgs.overlays; 9 | lib = configuration.pkgs.lib; 10 | in 11 | lib.composeManyExtensions configurationOverlays final prev 12 | -------------------------------------------------------------------------------- /modules/srvos.nix: -------------------------------------------------------------------------------- 1 | { inputs, ... }: 2 | { 3 | flake-file.inputs.srvos.url = "github:nix-community/srvos"; 4 | flake-file.inputs.srvos.inputs.nixpkgs.follows = "nixpkgs"; 5 | 6 | flake.nixosModules.srvos-bits = { 7 | key = "nixos-config.module.nixos.srvos-bits"; 8 | imports = [ 9 | "${inputs.srvos}/nixos/common/networking.nix" 10 | "${inputs.srvos}/nixos/common/nix.nix" 11 | "${inputs.srvos}/nixos/mixins/mdns.nix" 12 | "${inputs.srvos}/shared/common/well-known-hosts.nix" 13 | ]; 14 | }; 15 | } 16 | -------------------------------------------------------------------------------- /ansible/playbooks/pve/tasks/tailscale-hosts.yml: -------------------------------------------------------------------------------- 1 | - name: Redirect tailscale DNS names to local IPs 2 | ansible.builtin.lineinfile: 3 | dest: /etc/hosts 4 | state: present 5 | line: '{{ ip_allocation[item].home.primary.address }} {{ item }}.lynx-lizard.ts.net' 6 | loop: "{{ groups['pve_at_home'] | select('ne', inventory_hostname) }}" 7 | - name: Also resolve own tailscale DNS name 8 | ansible.builtin.lineinfile: 9 | dest: /etc/hosts 10 | state: present 11 | line: '127.0.0.1 {{ inventory_hostname }}.lynx-lizard.ts.net' 12 | -------------------------------------------------------------------------------- /modules/binarin/wl-kbptr/default.nix: -------------------------------------------------------------------------------- 1 | { inputs, ... }: 2 | { 3 | flake.homeModules.wl-kbptr = 4 | { pkgs, config, ... }: 5 | { 6 | key = "nixos-config.modules.home.wl-kbptr"; 7 | home.packages = [ 8 | inputs.nixpkgs-unstable.legacyPackages."${pkgs.stdenv.hostPlatform.system}".wl-kbptr 9 | ]; 10 | 11 | xdg.configFile."wl-kbptr/config".source = 12 | config.lib.file.mkOutOfStoreSymlink "${config.home.homeDirectory}/personal-workspace/nixos-config/modules/binarin/wl-kbptr/config"; 13 | }; 14 | } 15 | -------------------------------------------------------------------------------- /ansible/roles/sshd/meta/main.yml: -------------------------------------------------------------------------------- 1 | #SPDX-License-Identifier: MIT-0 2 | galaxy_info: 3 | author: nixos-config maintainer 4 | description: SSH daemon configuration and security hardening 5 | license: MIT-0 6 | min_ansible_version: 2.9 7 | platforms: 8 | - name: Ubuntu 9 | versions: 10 | - all 11 | - name: Debian 12 | versions: 13 | - all 14 | - name: EL 15 | versions: 16 | - all 17 | galaxy_tags: 18 | - ssh 19 | - sshd 20 | - security 21 | - system 22 | 23 | dependencies: [] 24 | -------------------------------------------------------------------------------- /terraform/cloud-init.cfg: -------------------------------------------------------------------------------- 1 | #cloud-config 2 | hostname: qdevice-vm 3 | 4 | users: 5 | - name: root 6 | ssh_authorized_keys: 7 | - ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIA4xASs6c8Mbl9tvIulPTf0LIgIaDL/TIW3StFk9hb7b binarin@demandred 8 | 9 | ssh_pwauth: True 10 | disable_root: false 11 | 12 | growpart: 13 | mode: auto 14 | devices: ['/'] 15 | 16 | runcmd: 17 | - sed -i '/PermitRootLogin/s/.*/PermitRootLogin yes/' /etc/ssh/sshd_config 18 | - systemctl restart sshd 19 | 20 | final_message: "The system is finally up, after $UPTIME seconds" 21 | -------------------------------------------------------------------------------- /modules/devshell.nix: -------------------------------------------------------------------------------- 1 | { 2 | perSystem = 3 | { pkgs, ... }: 4 | { 5 | devShells.default = pkgs.mkShell { 6 | name = "nixos-unified-template-shell"; 7 | meta.description = "Shell environment for modifying this Nix configuration"; 8 | packages = with pkgs; [ 9 | just 10 | nixd 11 | dnscontrol 12 | (terraform_1.withPlugins (p: with p; [ dmacvicar_libvirt ])) 13 | cloud-init 14 | ansible 15 | ansible-lint 16 | ]; 17 | }; 18 | }; 19 | } 20 | -------------------------------------------------------------------------------- /modules/gc.nix: -------------------------------------------------------------------------------- 1 | { 2 | self, 3 | ... 4 | }: 5 | { 6 | flake.homeModules.gc = { 7 | key = "nixos-config.modules.home.gc"; 8 | 9 | # Garbage collect the Nix store 10 | nix.gc = { 11 | automatic = true; 12 | # Change how often the garbage collector runs (default: weekly) 13 | # frequency = "monthly"; 14 | }; 15 | }; 16 | 17 | flake.nixosModules.gc = 18 | { ... }: 19 | { 20 | key = "nixos-config.modules.nixos.gc"; 21 | 22 | config.home-manager.sharedModules = [ self.homeModules.gc ]; 23 | }; 24 | } 25 | -------------------------------------------------------------------------------- /.beads/.gitignore: -------------------------------------------------------------------------------- 1 | # SQLite databases 2 | *.db 3 | *.db?* 4 | *.db-journal 5 | *.db-wal 6 | *.db-shm 7 | 8 | # Daemon runtime files 9 | daemon.lock 10 | daemon.log 11 | daemon.pid 12 | bd.sock 13 | 14 | # Legacy database files 15 | db.sqlite 16 | bd.db 17 | 18 | # Merge artifacts (temporary files from 3-way merge) 19 | beads.base.jsonl 20 | beads.base.meta.json 21 | beads.left.jsonl 22 | beads.left.meta.json 23 | beads.right.jsonl 24 | beads.right.meta.json 25 | 26 | # Keep JSONL exports and config (source of truth for git) 27 | !issues.jsonl 28 | !metadata.json 29 | !config.json 30 | -------------------------------------------------------------------------------- /secrets/furfur/ssh_host_rsa_key.pub: -------------------------------------------------------------------------------- 1 | ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDedGseRy9REYVyaA+9AlBHOQkvNPFCRPvrLNUkabvbA5f8/6ToElAaHes6zpszVmb+uMahVQ6kfFs6sTWdgDYD2NxqjXEpsJtA4wVcNTdsUCwBjM6imb0XMgmU2qxhMZdxkcUvl8M+5CoMlC9UkVizsOBjI3d8TBX+/nO80I1AsERI9olP6vZvhn4YFnY0b0C1U5Nhab8f8Y7P84bmz6RkZOaatgHm8Q8dHZUMgs2oZClImRAVMa1bW51y3eb3cpXSGaLuJ5OTguq9jIl+hDu9raF9gvqj5hExYMtKqgdm2MtfJfJJ5fzfQurmpBxGyssNUStOGqht75dyr9x5c05WozfwHXS6K7QWsjPFkTN/t0H5gRIkJql2I06ja2UeV+vCEWE70VqWVVwuwujWDGYR/tgtseYf+WVRBkfgrjsgxLC3V9d6Avwg3CPi0JPlm4Q8ch7Mzu+7pZh24bioAvdwkuP1Pz45aKVruln25JsyUGR6T3fvkbCM7kUDaXHiMFs= binarin@demandred 2 | -------------------------------------------------------------------------------- /modules/packages/sshmenu.nix: -------------------------------------------------------------------------------- 1 | { self, ... }: 2 | { 3 | perSystem = 4 | { pkgs, lib, ... }: 5 | { 6 | packages.sshmenu = pkgs.writeTextFile { 7 | name = "sshmenu"; 8 | destination = "/bin/sshmenu"; 9 | executable = true; 10 | text = '' 11 | #!${lib.getExe pkgs.zsh} 12 | export PATH="${ 13 | lib.makeBinPath [ 14 | pkgs.rxvt-unicode 15 | pkgs.xxd 16 | ] 17 | }:$PATH" 18 | '' 19 | + (builtins.readFile "${self}/files/sshmenu"); 20 | }; 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /ansible/files/initramfs-curl-hook: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | PREREQS="" 3 | case $1 in 4 | prereqs) echo "${PREREQS}"; exit 0;; 5 | esac 6 | . /usr/share/initramfs-tools/hook-functions 7 | #copy curl binary 8 | 9 | copy_exec /usr/bin/curl /bin 10 | #fix DNS lib (needed for Debian 11) 11 | 12 | cp -a /usr/lib/x86_64-linux-gnu/libnss_dns* $DESTDIR/usr/lib/x86_64-linux-gnu/ 13 | #fix DNS resolver (needed for Debian 11 + 12) 14 | 15 | echo "nameserver 1.1.1.1\n" > ${DESTDIR}/etc/resolv.conf 16 | #copy ca-certs for curl 17 | 18 | mkdir -p $DESTDIR/usr/share 19 | cp -ar /usr/share/ca-certificates $DESTDIR/usr/share/ 20 | cp -ar /etc/ssl $DESTDIR/etc/ 21 | -------------------------------------------------------------------------------- /modules/security.nix: -------------------------------------------------------------------------------- 1 | { ... }: 2 | { 3 | flake.nixosModules.security = 4 | { ... }: 5 | { 6 | key = "nixos-config.modules.nixos.security"; 7 | 8 | config = { 9 | security.polkit.enable = true; 10 | 11 | networking.firewall.enable = true; 12 | 13 | security.sudo = { 14 | enable = true; 15 | wheelNeedsPassword = false; 16 | }; 17 | 18 | security.pam.loginLimits = [ 19 | { 20 | domain = "*"; 21 | type = "-"; 22 | item = "nofile"; 23 | value = "131072"; 24 | } 25 | ]; 26 | }; 27 | }; 28 | } 29 | -------------------------------------------------------------------------------- /modules/programs/direnv.nix: -------------------------------------------------------------------------------- 1 | { ... }: 2 | { 3 | 4 | flake.homeModules.direnv = 5 | { ... }: 6 | { 7 | key = "nixos-config.modules.home.direnv"; 8 | config = { 9 | programs.direnv = { 10 | enable = true; 11 | enableZshIntegration = true; 12 | enableBashIntegration = true; 13 | nix-direnv = { 14 | enable = true; 15 | }; 16 | config.global = { 17 | # Make direnv messages less verbose 18 | hide_env_diff = true; 19 | }; 20 | }; 21 | 22 | impermanence.local-directories = [ ".local/share/direnv" ]; 23 | }; 24 | }; 25 | } 26 | -------------------------------------------------------------------------------- /modules/large-console-fonts.nix: -------------------------------------------------------------------------------- 1 | { ... }: 2 | { 3 | flake.nixosModules.large-console-fonts = 4 | { 5 | pkgs, 6 | lib, 7 | ... 8 | }: 9 | { 10 | key = "nixos-config.modules.nixos.large-console-fonts"; 11 | 12 | options = { 13 | console.useLargeFonts = lib.mkEnableOption "Use large console fonts (for HiDPI screens)"; 14 | }; 15 | 16 | config = { 17 | console = { 18 | earlySetup = true; 19 | font = "${pkgs.terminus_font}/share/consolefonts/ter-132n.psf.gz"; 20 | packages = with pkgs; [ terminus_font ]; 21 | keyMap = "us"; 22 | }; 23 | }; 24 | }; 25 | } 26 | -------------------------------------------------------------------------------- /ansible/playbooks/clevis-luks.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: pve 3 | become: true 4 | handlers: 5 | - import_tasks: handlers/update-initramfs.yaml 6 | tasks: 7 | - name: Install Clevis with necessary pins/integrations 8 | package: 9 | state: present 10 | name: 11 | - clevis 12 | - clevis-tpm2 13 | - clevis-initramfs 14 | - clevis-luks 15 | 16 | - name: Copy initramfs curl hook for network access in initrd 17 | copy: 18 | src: files/initramfs-curl-hook 19 | dest: /usr/share/initramfs-tools/hooks/curl 20 | mode: '0755' 21 | owner: root 22 | group: root 23 | notify: update-initramfs -------------------------------------------------------------------------------- /ansible/playbooks/pve/vars/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | zfs_exporter_version: '2.3.4' 3 | # zfs_exporter_options: '--collector.dataset-snapshot' 4 | vmagent_remote_write_host: http://192.168.2.2:8428 5 | vmagent_scrape_config: 6 | global: 7 | external_labels: 8 | bhost: "{{ inventory_hostname }}" 9 | scrape_configs: 10 | - job_name: "node" 11 | scrape_interval: "10s" 12 | static_configs: 13 | - targets: ["127.0.0.1:9100"] 14 | - job_name: "zfs" 15 | scrape_interval: "10s" 16 | static_configs: 17 | - targets: ["127.0.0.1:9134"] 18 | - job_name: "vmagent" 19 | scrape_interval: "10s" 20 | static_configs: 21 | - targets: ["127.0.0.1:8429"] 22 | -------------------------------------------------------------------------------- /modules/packages/brightnessctl-all.nix: -------------------------------------------------------------------------------- 1 | { ... }: 2 | { 3 | perSystem = 4 | { pkgs, ... }: 5 | { 6 | packages.brightnessctl-all = pkgs.writeShellApplication { 7 | name = "brightnessctl-all"; 8 | runtimeInputs = with pkgs; [ 9 | ddcutil 10 | coreutils 11 | ]; 12 | text = '' 13 | mapfile -t all < <(ddcutil detect --brief | perl -nE 'say $1 if m,/dev/i2c-(\d+),') 14 | pids=() 15 | for dev in "''${all[@]}" ; do 16 | ddcutil -b "$dev" setvcp 10 "$1" & 17 | pids[dev]=$! 18 | done 19 | for pid in "''${pids[@]}"; do 20 | wait "$pid" 21 | done 22 | ''; 23 | }; 24 | }; 25 | } 26 | -------------------------------------------------------------------------------- /modules/wayland.nix: -------------------------------------------------------------------------------- 1 | { 2 | ... 3 | }: 4 | { 5 | flake.nixosModules.wayland = 6 | { ... }: 7 | { 8 | key = "nixos-config.modules.nixos.wayland"; 9 | 10 | programs.dconf.enable = true; 11 | }; 12 | 13 | flake.homeModules.wayland = 14 | { 15 | ... 16 | }: 17 | { 18 | key = "nixos-config.modules.home.wayland"; 19 | 20 | config = { 21 | home.sessionVariables = { 22 | MOZ_ENABLE_WAYLAND = "1"; 23 | NIXOS_OZONE_WL = "1"; 24 | SDL_VIDEODRIVER = "wayland"; 25 | 26 | # needs qt5.qtwayland in systemPackages 27 | QT_QPA_PLATFORM = "wayland"; 28 | QT_WAYLAND_DISABLE_WINDOWDECORATION = "1"; 29 | }; 30 | }; 31 | }; 32 | } 33 | -------------------------------------------------------------------------------- /files/rust-fix.patch: -------------------------------------------------------------------------------- 1 | diff --recursive -u linux-6.15.9.orig/scripts/generate_rust_target.rs linux-6.15.9/scripts/generate_rust_target.rs 2 | --- linux-6.15.9.orig/scripts/generate_rust_target.rs 2025-08-01 10:51:29.000000000 +0200 3 | +++ linux-6.15.9/scripts/generate_rust_target.rs 2025-11-29 12:30:54.792372738 +0100 4 | @@ -225,7 +225,7 @@ 5 | ts.push("features", features); 6 | ts.push("llvm-target", "x86_64-linux-gnu"); 7 | ts.push("supported-sanitizers", ["kcfi", "kernel-address"]); 8 | - ts.push("target-pointer-width", "64"); 9 | + ts.push("target-pointer-width", 64); 10 | } else if cfg.has("X86_32") { 11 | // This only works on UML, as i386 otherwise needs regparm support in rustc 12 | if !cfg.has("UML") { 13 | -------------------------------------------------------------------------------- /ansible/inventories/home/hosts.yml: -------------------------------------------------------------------------------- 1 | pve_at_home: 2 | hosts: 3 | raum: 4 | bael: 5 | valak: 6 | 7 | pve: 8 | children: 9 | pve_at_home: 10 | 11 | qdevices: 12 | hosts: 13 | qdevice-vm: 14 | 15 | pbs: 16 | hosts: 17 | bael: 18 | pbs-hetzner: 19 | 20 | debian_cts: 21 | hosts: 22 | paperless: 23 | docker: 24 | dnscontrol: 25 | nextcloud: 26 | unifi: 27 | 28 | console_autologin: 29 | children: 30 | debian_cts: 31 | debian_vms: 32 | 33 | manage_ssh_access: 34 | children: 35 | debian_cts: 36 | debian_vms: 37 | pve: 38 | pbs: 39 | 40 | manage_root_ssh_access: 41 | children: 42 | pve: 43 | pbs: 44 | 45 | all: 46 | children: 47 | pve: 48 | pbs: 49 | vars: 50 | ansible_user: root 51 | -------------------------------------------------------------------------------- /ansible/playbooks/pve/tasks/backup-raum-to-bael.yml: -------------------------------------------------------------------------------- 1 | - name: Copy and enable hourly raum->bael backup script 2 | ansible.builtin.copy: 3 | src: backup-raum 4 | dest: /usr/local/bin/backup-raum 5 | mode: '0755' 6 | owner: root 7 | group: root 8 | 9 | - name: create the backup-raum.service file 10 | ansible.builtin.copy: 11 | src: backup-raum.service 12 | dest: /etc/systemd/system/backup-raum.service 13 | 14 | - name: create the backup-raum.timer file 15 | ansible.builtin.copy: 16 | src: backup-raum.timer 17 | dest: /etc/systemd/system/backup-raum.timer 18 | 19 | - name: make sure that the backup-raum.timer is started 20 | ansible.builtin.systemd_service: 21 | name: backup-raum.timer 22 | state: started 23 | enabled: true 24 | daemon_reload: true 25 | -------------------------------------------------------------------------------- /modules/home-modules.nix: -------------------------------------------------------------------------------- 1 | { 2 | lib, 3 | # , self 4 | flake-parts-lib, 5 | moduleLocation, 6 | ... 7 | }: 8 | let 9 | inherit (lib) mapAttrs mkOption types; 10 | inherit (flake-parts-lib) mkSubmoduleOptions; 11 | in 12 | { 13 | options = { 14 | flake = mkSubmoduleOptions { 15 | homeModules = mkOption { 16 | type = types.lazyAttrsOf types.unspecified; 17 | default = { }; 18 | apply = mapAttrs ( 19 | k: v: { 20 | _file = "${toString moduleLocation}#homeModules.${k}"; 21 | imports = [ v ]; 22 | } 23 | ); 24 | description = '' 25 | Home Manager modules. 26 | 27 | You may use this for reusable pieces of configuration, service modules, etc. 28 | ''; 29 | }; 30 | }; 31 | }; 32 | } 33 | -------------------------------------------------------------------------------- /machines/furfur/hardware-configuration.nix: -------------------------------------------------------------------------------- 1 | # Do not modify this file! It was generated by ‘nixos-generate-config’ 2 | # and may be overwritten by future invocations. Please make changes 3 | # to /etc/nixos/configuration.nix instead. 4 | { 5 | config, 6 | lib, 7 | modulesPath, 8 | ... 9 | }: 10 | 11 | { 12 | imports = [ 13 | (modulesPath + "/installer/scan/not-detected.nix") 14 | ]; 15 | 16 | boot.initrd.availableKernelModules = [ 17 | "xhci_pci" 18 | "thunderbolt" 19 | "nvme" 20 | "usb_storage" 21 | "sd_mod" 22 | ]; 23 | boot.initrd.kernelModules = [ ]; 24 | boot.kernelModules = [ "kvm-intel" ]; 25 | boot.extraModulePackages = [ ]; 26 | 27 | nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; 28 | hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; 29 | } 30 | -------------------------------------------------------------------------------- /terraform/outputs.tf: -------------------------------------------------------------------------------- 1 | output "vm_name" { 2 | description = "Name of the created virtual machine" 3 | value = libvirt_domain.debian_vm.name 4 | } 5 | 6 | output "vm_id" { 7 | description = "ID of the created virtual machine" 8 | value = libvirt_domain.debian_vm.id 9 | } 10 | 11 | output "static_ip" { 12 | description = "Static IP address assigned to the VM" 13 | value = var.static_ip 14 | } 15 | 16 | output "ssh_command" { 17 | description = "SSH command to connect to the VM" 18 | value = "ssh binarin@${var.static_ip}" 19 | } 20 | 21 | output "vm_memory" { 22 | description = "Memory allocated to the VM (MB)" 23 | value = libvirt_domain.debian_vm.memory 24 | } 25 | 26 | output "vm_vcpu" { 27 | description = "Number of vCPUs allocated to the VM" 28 | value = libvirt_domain.debian_vm.vcpu 29 | } 30 | -------------------------------------------------------------------------------- /scripts/build-nixos.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | 4 | # Build a NixOS configuration 5 | # Usage: build-nixos.sh [nix-options...] 6 | # 7 | # Arguments: 8 | # configuration: The NixOS configuration to build (e.g., hostname) 9 | # output-path: Where to store the result symlink 10 | # nix-options: Additional options to pass to nix build (optional) 11 | 12 | if [ $# -lt 2 ]; then 13 | echo "Usage: $0 [nix-options...]" >&2 14 | exit 1 15 | fi 16 | 17 | configuration="$1" 18 | output_path="$2" 19 | shift 2 20 | nix_options=("$@") 21 | 22 | repo_root="$(pwd)" 23 | 24 | # Build the NixOS configuration 25 | nix build \ 26 | "${repo_root}#nixosConfigurations.${configuration}.config.system.build.toplevel" \ 27 | --keep-going \ 28 | -o "${output_path}" \ 29 | "${nix_options[@]}" 30 | -------------------------------------------------------------------------------- /modules/formatter.nix: -------------------------------------------------------------------------------- 1 | { inputs, lib, ... }: 2 | { 3 | flake-file.inputs = { 4 | treefmt-nix.url = "github:numtide/treefmt-nix"; 5 | }; 6 | 7 | imports = [ 8 | inputs.treefmt-nix.flakeModule 9 | ]; 10 | 11 | perSystem = 12 | { ... }: 13 | { 14 | treefmt = { 15 | projectRootFile = "flake.nix"; 16 | programs.nixf-diagnose.enable = lib.mkForce false; 17 | settings.global.excludes = [ 18 | ".gitattributes" 19 | "*.org" 20 | "*.el" 21 | "*.cfg" 22 | "*.gpg" 23 | "*.kdl" 24 | "*.sh" 25 | ".terraform" 26 | ".direnv" 27 | "secrets/**" 28 | "terraform/**" 29 | "ansible/**" 30 | ".forgejo/**" 31 | ".beads/**" 32 | "files/*" 33 | "justfile" 34 | ".sops.yaml" 35 | "**/config" 36 | ]; 37 | }; 38 | }; 39 | } 40 | -------------------------------------------------------------------------------- /ansible/roles/sshd/defaults/main.yml: -------------------------------------------------------------------------------- 1 | #SPDX-License-Identifier: MIT-0 2 | --- 3 | # defaults file for sshd 4 | 5 | # SSH configuration defaults 6 | sshd_config_path: /etc/ssh/sshd_config 7 | sshd_config_d_path: /etc/ssh/sshd_config.d 8 | authorized_keys_path: /etc/ssh/authorized_keys.d 9 | authorized_principals_path: /etc/ssh/authorized_principals.d 10 | trusted_user_ca_keys_path: /etc/ssh/trusted_user_ca_keys 11 | 12 | # SSH security settings 13 | sshd_permit_root_login: "prohibit-password" 14 | sshd_password_authentication: false 15 | sshd_kbd_interactive_authentication: false 16 | sshd_use_pam: false 17 | 18 | # Include config directory 19 | sshd_include_config_d: true 20 | 21 | # SSH users to provision authorized keys for (in addition to root) 22 | sshd_users: "{{ ssh_users | default([]) }}" 23 | 24 | # SSH keys data structure (should be provided via vars_files or group_vars) 25 | ssh_keys: {} 26 | 27 | sshd_root_principals: 28 | - root 29 | -------------------------------------------------------------------------------- /modules/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | self, 3 | lib, 4 | ... 5 | }: 6 | { 7 | flake.nixosModules.default = 8 | { ... }: 9 | { 10 | key = "nixos-config.modules.nixos.default"; 11 | 12 | imports = [ 13 | self.nixosModules.baseline 14 | self.nixosModules.emacs 15 | self.nixosModules.eternal-terminal 16 | self.nixosModules.git 17 | self.nixosModules.interactive-cli 18 | self.nixosModules.inventory-legacy 19 | self.nixosModules.nix 20 | self.nixosModules.security 21 | self.nixosModules.sops 22 | self.nixosModules.sshd 23 | self.nixosModules.tailscale 24 | self.nixosModules.use-nix-cache 25 | ]; 26 | 27 | config = { 28 | system.switch.enable = true; 29 | 30 | services.dbus.implementation = lib.mkDefault "broker"; 31 | 32 | time.timeZone = lib.mkDefault "Europe/Amsterdam"; 33 | }; 34 | }; 35 | } 36 | -------------------------------------------------------------------------------- /modules/programs/insync.nix: -------------------------------------------------------------------------------- 1 | { ... }: 2 | { 3 | flake.homeModules.insync = 4 | { pkgs, ... }: 5 | { 6 | key = "nixos-config.modules.home.insync"; 7 | 8 | home.packages = with pkgs; [ 9 | insync 10 | ]; 11 | 12 | xdg.configFile."autostart/insync.desktop".text = '' 13 | [Desktop Entry] 14 | Version=1.0 15 | Type=Application 16 | Name=Insync 17 | GenericName=Insync 18 | Comment=Launch Insync 19 | Icon=insync 20 | Categories=Network; 21 | Exec=insync start --no-daemon 22 | TryExec=insync 23 | Terminal=false 24 | X-GNOME-Autostart-Delay=3 25 | ''; 26 | 27 | impermanence.persist-directories = [ 28 | "OneDrive" 29 | ".config/Insync" 30 | ".local/share/Insync" 31 | ]; 32 | 33 | impermanence.local-directories = [ 34 | ".cache/Insync" 35 | ]; 36 | }; 37 | } 38 | -------------------------------------------------------------------------------- /ansible/playbooks/pve/tasks/etckeeper.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Install etckeeper 3 | ansible.builtin.apt: 4 | name: etckeeper 5 | 6 | - command: git config --get --global user.name 7 | register: git_user_name_cmd 8 | ignore_errors: true 9 | changed_when: False 10 | 11 | - name: Set git user.name for root 12 | when: git_user_name_cmd.rc != 0 13 | ansible.builtin.command: "git config --global user.name 'Root at {{ inventory_hostname }}'" 14 | 15 | - command: git config --get --global user.email 16 | register: git_user_email_cmd 17 | ignore_errors: true 18 | changed_when: False 19 | 20 | - name: Set git user.email for root 21 | when: git_user_email_cmd.rc != 0 22 | ansible.builtin.command: "git config --global user.email 'root@{{ inventory_hostname }}.binarin.info'" 23 | 24 | - name: Ignore pve dir 25 | ansible.builtin.lineinfile: 26 | create: true 27 | insertafter: EOF 28 | line: pve 29 | path: /etc/.gitignore 30 | 31 | -------------------------------------------------------------------------------- /modules/bluetooth.nix: -------------------------------------------------------------------------------- 1 | { ... }: 2 | { 3 | flake.nixosModules.bluetooth = 4 | { 5 | config, 6 | lib, 7 | pkgs, 8 | ... 9 | }: 10 | { 11 | key = "nixos-config.modules.nixos.bluetooth"; 12 | config = { 13 | environment.systemPackages = with pkgs; [ 14 | bluetui 15 | ]; 16 | 17 | services.blueman.enable = true; 18 | 19 | hardware.bluetooth = { 20 | enable = true; 21 | powerOnBoot = true; 22 | }; 23 | 24 | systemd.tmpfiles.settings."10-bluetooth-persist" = lib.mkIf config.impermanence.enable { 25 | "/persist/var/lib/bluetooth".d = { 26 | user = "root"; 27 | group = "root"; 28 | mode = "0700"; 29 | }; 30 | }; 31 | 32 | systemd.services.bluetooth.serviceConfig.BindPaths = lib.mkIf config.impermanence.enable [ 33 | "/persist/var/lib/bluetooth:/var/lib/bluetooth" 34 | ]; 35 | }; 36 | }; 37 | } 38 | -------------------------------------------------------------------------------- /modules/fonts.nix: -------------------------------------------------------------------------------- 1 | { 2 | ... 3 | }: 4 | { 5 | flake.homeModules.fonts = 6 | { 7 | pkgs, 8 | lib, 9 | osConfig, 10 | ... 11 | }: 12 | { 13 | key = "nixos-config.modules.home.fonts"; 14 | 15 | config = lib.mkIf osConfig.services.graphical-desktop.enable { 16 | fonts.fontconfig.enable = true; 17 | 18 | home.packages = with pkgs; [ 19 | corefonts 20 | font-awesome 21 | vista-fonts 22 | nerd-fonts.fira-code 23 | nerd-fonts.fira-mono 24 | nerd-fonts.inconsolata 25 | nerd-fonts.inconsolata-lgc # cyrillic 26 | nerd-fonts.iosevka 27 | nerd-fonts.iosevka-term 28 | nerd-fonts.jetbrains-mono 29 | nerd-fonts.liberation 30 | nerd-fonts.noto 31 | nerd-fonts.roboto-mono 32 | nerd-fonts.sauce-code-pro 33 | nerd-fonts.terminess-ttf 34 | nerd-fonts.ubuntu-mono 35 | ]; 36 | }; 37 | }; 38 | } 39 | -------------------------------------------------------------------------------- /scripts/iommu.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | shopt -s nullglob 4 | lastgroup="" 5 | for g in `find /sys/kernel/iommu_groups/* -maxdepth 0 -type d | sort -V`; do 6 | for d in $g/devices/*; do 7 | if [ "${g##*/}" != "$lastgroup" ]; then 8 | echo -en "Group ${g##*/}:\t" 9 | else 10 | echo -en "\t\t" 11 | fi 12 | lastgroup=${g##*/} 13 | lspci -nms ${d##*/} | awk -F'"' '{printf "[%s:%s]", $4, $6}' 14 | if [[ -e "$d"/reset ]]; then echo -en " [R] "; else echo -en " "; fi 15 | 16 | lspci -mms ${d##*/} | awk -F'"' '{printf "%s %-40s %s\n", $1, $2, $6}' 17 | for u in ${d}/usb*/; do 18 | bus=$(cat "${u}/busnum") 19 | lsusb -s $bus: | \ 20 | awk '{gsub(/:/,"",$4); printf "%s|%s %s %s %s|", $6, $1, $2, $3, $4; for(i=7;i<=NF;i++){printf "%s ", $i}; printf "\n"}' | \ 21 | awk -F'|' '{printf "USB:\t\t[%s]\t\t %-40s %s\n", $1, $2, $3}' 22 | done 23 | done 24 | done 25 | -------------------------------------------------------------------------------- /scripts/ai-container.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | 4 | if [ "$EUID" -ne 0 ] 5 | then echo "Root privileges needed to start container" 6 | exit 7 | fi 8 | 9 | container=$1 10 | 11 | profile=/nix/var/nix/profiles/per-container/$container 12 | gcroots=/nix/var/nix/gcroots/per-container/$container 13 | root=/var/lib/machines/$container 14 | 15 | config=$(nix build .#nixosConfigurations.$container.config.system.build.toplevel --print-out-paths --no-link) 16 | 17 | mkdir -p $profile /nix/var/nix/gcroots/per-container/$container 18 | nix-env --set $config --profile $profile/system 19 | 20 | mkdir -p $root 21 | 22 | systemd-nspawn \ 23 | --machine $container \ 24 | --directory $root \ 25 | --bind-ro=/nix/store \ 26 | --bind-ro=/nix/var/nix/db \ 27 | --bind-ro=/nix/var/nix/daemon-socket \ 28 | --bind="$profile:/nix/var/nix/profiles" \ 29 | --bind="$gcroots:/nix/var/nix/gcroots" \ 30 | --bind="/home/binarin/personal-workspace/nixos-config" \ 31 | --bind="/home/binarin/.claude" \ 32 | --bind="/home/binarin/.claude.json" \ 33 | $config/init 34 | -------------------------------------------------------------------------------- /modules/binarin/gnupg.nix: -------------------------------------------------------------------------------- 1 | { self, ... }: 2 | { 3 | flake.nixosModules.gnupg = 4 | { pkgs, ... }: 5 | { 6 | key = "nixos-config.modules.nixos.gnupg"; 7 | 8 | programs.gnupg = { 9 | agent.enable = true; 10 | agent.pinentryPackage = pkgs.pinentry-curses; 11 | }; 12 | 13 | }; 14 | 15 | flake.homeModules.gnupg = 16 | { osConfig, pkgs, ... }: 17 | { 18 | key = "nixos-config.modules.home.gnupg"; 19 | 20 | imports = [ 21 | self.homeModules.impermanence 22 | ]; 23 | 24 | impermanence.persist-directories = [ 25 | { 26 | directory = ".gnupg"; 27 | mode = "0700"; 28 | } 29 | ]; 30 | 31 | services.gpg-agent = { 32 | enable = true; 33 | defaultCacheTtl = 3600; 34 | maxCacheTtl = 14400; 35 | extraConfig = '' 36 | allow-preset-passphrase 37 | ''; 38 | pinentry.package = 39 | if osConfig.services.graphical-desktop.enable then pkgs.pinentry-gtk2 else pkgs.pinentry-curses; 40 | }; 41 | }; 42 | } 43 | -------------------------------------------------------------------------------- /ansible/playbooks/pve/files/backup-raum: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | datasets=( 3 | bpool/BOOT/debian 4 | rpool/ROOT/debian 5 | rpool/home 6 | rpool/home/root 7 | rpool/var 8 | rpool/var/lib 9 | rpool/var/spool 10 | spinning-zfs/pve-data/subvol-111-disk-0 #,mp=/media/movies 11 | spinning-zfs/pve-data/subvol-111-disk-1 #,mp=/media/music,backup=1 12 | spinning-zfs/pve-data/subvol-111-disk-2 #,mp=/media/torrents/regular 13 | spinning-zfs/pve-data/subvol-111-disk-3 #,mp=/media/torrents/incomplete,replicate=0 14 | spinning-zfs/pve-data/subvol-111-disk-4 #,mp=/media/torrents/rare,backup=1 15 | spinning-zfs/pve-data/subvol-111-disk-5 #,mp=/media/tubearchivist 16 | spinning-zfs/pve-data/subvol-111-disk-6 #,mp=/media/annex 17 | spinning-zfs/pve-data/subvol-111-disk-7 #,mp=/media/usenet 18 | ) 19 | target=spinning-zfs/raum-oob-backups 20 | 21 | for dataset in ${datasets[@]} 22 | do 23 | zfs create -p $target/$(dirname $dataset) 24 | syncoid --sshkey /root/.ssh/id_rsa --insecure-direct-connection=192.168.2.68:63333 --no-rollback root@192.168.2.44:$dataset $target/$dataset 25 | done 26 | -------------------------------------------------------------------------------- /modules/nixos-hardware.nix: -------------------------------------------------------------------------------- 1 | { self, inputs, ... }: 2 | { 3 | flake-file.inputs = { 4 | nixos-hardware.url = "github:NixOS/nixos-hardware/master"; 5 | }; 6 | 7 | flake.nixosModules.microsoft-surface = 8 | { config, ... }: 9 | { 10 | key = "nixos-config.modules.nixos.microsoft-surface"; 11 | imports = [ 12 | inputs.nixos-hardware.nixosModules.microsoft-surface-pro-intel 13 | self.modules.generic.flake-files 14 | ]; 15 | 16 | hardware.microsoft-surface.kernelVersion = "stable"; 17 | # XXX broken after rust was bumped 18 | boot.kernelPatches = [ 19 | { 20 | name = "rust-1.91-fix"; 21 | patch = config.lib.self.file "rust-fix.patch"; 22 | } 23 | ]; 24 | boot.initrd.kernelModules = [ 25 | "surface_aggregator" 26 | "surface_aggregator_registry" 27 | "surface_aggregator_hub" 28 | "surface_aggregator_tabletsw" 29 | "surface_hid_core" 30 | "surface_hid" 31 | "8250_dw" 32 | "intel_lpss_pci" 33 | "intel_lpss" 34 | "pinctrl_tigerlake" 35 | ]; 36 | }; 37 | } 38 | -------------------------------------------------------------------------------- /modules/sshd.nix: -------------------------------------------------------------------------------- 1 | { self, ... }: 2 | { 3 | flake.nixosModules.sshd = 4 | { lib, config, ... }: 5 | { 6 | key = "nixos-config.modules.nixos.sshd"; 7 | 8 | imports = [ 9 | self.nixosModules.public-keys 10 | ]; 11 | 12 | config = { 13 | networking.firewall.allowedTCPPorts = [ 22 ]; 14 | 15 | services.openssh.enable = true; 16 | services.openssh.authorizedKeysInHomedir = false; 17 | services.openssh.settings.PermitRootLogin = lib.mkDefault "prohibit-password"; 18 | services.openssh.settings.TrustedUserCaKeys = "/etc/ssh/trusted_user_ca_keys"; 19 | services.openssh.settings.AuthorizedPrincipalsFile = "/etc/ssh/authorized_principals.d/%u"; 20 | 21 | environment.etc."ssh/trusted_user_ca_keys".text = lib.concatStringsSep "\n" ( 22 | config.lib.publicKeys.secureWithTag "user-ca" 23 | ); 24 | 25 | users.users.root.openssh = { 26 | authorizedKeys.keys = lib.mkDefault (config.lib.publicKeys.ssh.secureForUser "root"); 27 | authorizedPrincipals = lib.mkDefault [ "root" ]; 28 | }; 29 | }; 30 | }; 31 | } 32 | -------------------------------------------------------------------------------- /modules/programs/claude-code/default.nix: -------------------------------------------------------------------------------- 1 | { self, ... }: 2 | { 3 | flake-file.inputs.nix-ai-tools.url = "github:numtide/nix-ai-tools"; 4 | flake-file.inputs.nix-ai-tools.inputs.nixpkgs.follows = "nixpkgs"; 5 | flake-file.inputs.beads.url = "github:steveyegge/beads"; 6 | 7 | flake.homeModules.claude-code = 8 | { pkgs, config, ... }: 9 | { 10 | key = "nixos-config.modules.home.claude-code"; 11 | imports = [ 12 | self.homeModules.impermanence 13 | ]; 14 | 15 | home.packages = [ 16 | self.inputs.beads.packages.${pkgs.stdenv.hostPlatform.system}.default 17 | self.inputs.nix-ai-tools.packages.${pkgs.stdenv.hostPlatform.system}.claude-code 18 | self.inputs.nix-ai-tools.packages.${pkgs.stdenv.hostPlatform.system}.claudebox 19 | ]; 20 | 21 | home.file.".claude/skills/".source = 22 | config.lib.file.mkOutOfStoreSymlink "${config.home.homeDirectory}/personal-workspace/nixos-config/files/claude-skills"; 23 | 24 | impermanence.persist-files = [ 25 | ".claude.json" 26 | ]; 27 | 28 | impermanence.persist-directories = [ 29 | ".claude" 30 | ]; 31 | }; 32 | } 33 | -------------------------------------------------------------------------------- /files/firefox-userChrome.css: -------------------------------------------------------------------------------- 1 | /* There is an zero-width space inside quotes, the matching setting should be set in Sidebery config */ 2 | #main-window[titlepreface="​"] { 3 | & #sidebar-box { 4 | margin-left: 6px; 5 | } 6 | & #sidebar-main { 7 | /* height: 0 !important; */ 8 | display: none; 9 | } 10 | & #browser { 11 | background-color: var(--toolbar-bgcolor); 12 | } 13 | } 14 | 15 | #TabsToolbar { 16 | display: none; 17 | } 18 | 19 | .titlebar-min, .titlebar-max, .titlebar-restore, .titlebar-spacer[type="post-tabs"] { 20 | display: none; 21 | } 22 | 23 | #main-window { 24 | & #tabbrowser-tabpanels browser[type] { 25 | border-radius: 10px 10px 10px 10px !important; 26 | border: solid 1px var(--toolbar-bgcolor); 27 | margin-bottom: 7px !important; 28 | margin-right: 4px; 29 | 30 | } 31 | /* one level up from the one above */ 32 | & .browserSidebarContainer, .devtools-side-splitter { 33 | background-color: var(--toolbar-bgcolor) !important;; 34 | } 35 | 36 | & #tabbrowser-tabbox { 37 | outline: 0px !important; 38 | box-shadow: none !important; 39 | border-radius: 10px; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /scripts/org-capture-clipboard.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | 4 | # Get clipboard content 5 | clipboard=$(wl-paste -p) 6 | 7 | # Extract first line as title (trimmed) 8 | title=$(echo "$clipboard" | head -n1 | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') 9 | 10 | # Extract rest as body and trim empty lines at start and end 11 | body=$(echo "$clipboard" | tail -n +2 | awk 'NF {p=1} p' | tac | awk 'NF {p=1} p' | tac) 12 | 13 | # URL encode function (RFC 3986) 14 | urlencode() { 15 | local LC_ALL=C # Process as bytes, not multi-byte characters 16 | local string="${1}" 17 | local strlen=${#string} 18 | local encoded="" 19 | local pos c o 20 | 21 | for (( pos=0 ; pos [nix-options...] 6 | # 7 | # Arguments: 8 | # configuration: The NixOS configuration to evaluate (e.g., hostname) 9 | # nix-options: Additional options to pass to nix eval (optional) 10 | # 11 | # This script evaluates the configuration and prints the .drv path, 12 | # ensuring all dependencies are instantiated but not built. 13 | # 14 | # Primary use case: Debugging infinite recursion and evaluation errors. 15 | # When `nix flake check` fails with infinite recursion, error diagnostics 16 | # are often insufficient to pinpoint the problem location. This script 17 | # helps isolate which configuration is problematic. 18 | 19 | if [ $# -lt 1 ]; then 20 | echo "Usage: $0 [nix-options...]" >&2 21 | exit 1 22 | fi 23 | 24 | configuration="$1" 25 | shift 26 | nix_options=("$@") 27 | 28 | repo_root="$(pwd)" 29 | 30 | # Evaluate and get the derivation path 31 | drv_path=$(nix eval --raw \ 32 | "${repo_root}#nixosConfigurations.${configuration}.config.system.build.toplevel.drvPath" \ 33 | "${nix_options[@]}") 34 | 35 | echo "${drv_path}" 36 | -------------------------------------------------------------------------------- /ansible/playbooks/pve/tasks/debian-non-free-firmware.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Enable non-free-firmware for debian 3 | register: debian_non_free_firmware_repo_1 4 | ansible.builtin.apt_repository: 5 | filename: debian-non-free-firmware 6 | repo: "deb http://deb.debian.org/debian {{ ansible_facts.distribution_release }} non-free-firmware" 7 | update_cache: false 8 | - name: Enable non-free-firmware for debian-updates 9 | register: debian_non_free_firmware_repo_2 10 | ansible.builtin.apt_repository: 11 | filename: debian-non-free-firmware 12 | repo: "deb http://deb.debian.org/debian {{ ansible_facts.distribution_release }}-updates non-free-firmware" 13 | update_cache: false 14 | - name: Enable non-free-firmware for debian-security 15 | register: debian_non_free_firmware_repo_3 16 | ansible.builtin.apt_repository: 17 | filename: debian-non-free-firmware 18 | repo: "deb http://security.debian.org/debian-security {{ ansible_facts.distribution_release }}-security non-free-firmware" 19 | update_cache: false 20 | - name: Refresh apt-cache 21 | when: debian_non_free_firmware_repo_2.changed or debian_non_free_firmware_repo_2.changed or debian_non_free_firmware_repo_3.changed 22 | ansible.builtin.apt: 23 | update_cache: true 24 | -------------------------------------------------------------------------------- /modules/binarin/nix-dev.nix: -------------------------------------------------------------------------------- 1 | { inputs, ... }: 2 | { 3 | flake-file.inputs.direnv-instant.url = "github:Mic92/direnv-instant"; 4 | flake.nixosModules.binarin-nix-dev = 5 | { 6 | config, 7 | lib, 8 | pkgs, 9 | ... 10 | }: 11 | { 12 | environment.systemPackages = with pkgs; [ 13 | nix-search-tv 14 | ]; 15 | 16 | programs.nh = { 17 | enable = true; 18 | flake = "${config.users.users.binarin.home}/personal-workspace/nixos-config"; 19 | }; 20 | 21 | programs.direnv.enableZshIntegration = lib.mkForce false; 22 | programs.direnv.enableBashIntegration = lib.mkForce false; 23 | 24 | # improve desktop responsiveness when updating the system 25 | nix.daemonCPUSchedPolicy = "idle"; 26 | }; 27 | 28 | flake.homeModules.binarin-nix-dev = 29 | { 30 | pkgs, 31 | config, 32 | ... 33 | }: 34 | { 35 | imports = [ 36 | inputs.direnv-instant.homeModules.direnv-instant 37 | ]; 38 | programs.direnv-instant.enable = true; 39 | 40 | programs.nix-search-tv.enable = true; 41 | home.packages = [ 42 | (pkgs.writeShellScriptBin "ns" (config.lib.self.read "nix-search-tv.sh")) 43 | ]; 44 | }; 45 | } 46 | -------------------------------------------------------------------------------- /modules/binarin/syncthing.nix: -------------------------------------------------------------------------------- 1 | { self, inputs, ... }: 2 | { 3 | flake.homeModules.syncthing = 4 | { config, ... }: 5 | { 6 | key = "nixos-config.modules.home.syncthing"; 7 | 8 | disabledModules = [ 9 | "${inputs.home-manager}/modules/services/syncthing.nix" 10 | ]; 11 | imports = [ 12 | "${inputs.home-manager-master}/modules/services/syncthing.nix" 13 | self.homeModules.impermanence 14 | ]; 15 | 16 | sops.secrets."syncthing-ui-password" = { }; 17 | 18 | impermanence.persist-directories = [ 19 | ".local/state/syncthing" 20 | ]; 21 | 22 | impermanence.persist-files = [ 23 | ".config/syncthingtray.ini" 24 | ]; 25 | 26 | services.syncthing = { 27 | enable = true; 28 | # tray.enable = true; 29 | passwordFile = config.sops.secrets."syncthing-ui-password".path; 30 | settings = { 31 | devices = { 32 | pixel8.id = "ROLQIQ6-OEAMVJA-KRM4IDA-KOGUXR6-RZW3LRR-C6VZSAF-LZL5ZEV-PWAQTQL"; 33 | }; 34 | folders = { 35 | "/home/binarin/Music" = { 36 | id = "demandred-music"; 37 | devices = [ "pixel8" ]; 38 | }; 39 | }; 40 | }; 41 | }; 42 | }; 43 | } 44 | -------------------------------------------------------------------------------- /modules/binarin/podman.nix: -------------------------------------------------------------------------------- 1 | { self, ... }: 2 | { 3 | 4 | flake.nixosModules.binarin-podman = 5 | { pkgs, ... }: 6 | { 7 | key = "nixos-config.modules.nixos.binarin-podman"; 8 | 9 | imports = [ 10 | self.nixosModules.impermanence 11 | ]; 12 | 13 | virtualisation = { 14 | containers.enable = true; 15 | podman = { 16 | enable = true; 17 | dockerCompat = true; 18 | defaultNetwork.settings.dns_enabled = true; # Required for containers under podman-compose to be able to talk to each other. 19 | }; 20 | }; 21 | 22 | users.users.binarin.extraGroups = [ 23 | "podman" 24 | ]; 25 | 26 | environment.systemPackages = with pkgs; [ 27 | distrobox 28 | ]; 29 | 30 | home-manager.users.binarin = self.homeModules.binarin-podman; 31 | }; 32 | 33 | flake.homeModules.binarin-podman = 34 | { ... }: 35 | { 36 | key = "nixos-config.modules.home.binarin-podman"; 37 | 38 | imports = [ 39 | self.homeModules.impermanence 40 | ]; 41 | 42 | impermanence.persist-directories = [ 43 | ".local/share/containers" 44 | ]; 45 | 46 | impermanence.local-directories = [ 47 | ".cache/containers" 48 | ]; 49 | }; 50 | } 51 | -------------------------------------------------------------------------------- /secrets/furfur/user-binarin-age: -------------------------------------------------------------------------------- 1 | { 2 | "data": "ENC[AES256_GCM,data:33Yhl/Bzfhkfuck1zjLP4s/IbYkoyWP+ghO8L1Rjm4zoW2OiOGNLQ/rfgZImluXk/thehVI28kOEHmPpdkXEapbpykVc+TTdCve+hC8Y7OF3xnNg7Xmx/nXGkybuS4kHFbRalB5WBSyWwNLutTtUoAeC+YK6dcqEQM1EMz1xtCGvWUR7dT5IpzaEhrBr7JgDsKf63mzusPf/EQPgBiG11PGZx9UOvEX35SaTR23xLSW7/Khdb0FarhCRr+YI,iv:UwMlGxySMSr8761hG93BArxqZKYTT9EvE0SwPS/vdgE=,tag:PU4w4ChBI+mSIMzUGr9RVw==,type:str]", 3 | "sops": { 4 | "age": [ 5 | { 6 | "recipient": "age1dvu92xmh3w23ghey4l0epcs27m89f3nswzs5twgnel36xxu42f4sfsv023", 7 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBla1F0MTRTV0EwM3Rma2tn\nOUZ1ZmgwR0hKajBOajZXLzdLU2pEa01maml3Ci91amVEVUhWMzJab0dIRXBaSG43\nTWYrYXdYQXh4MnhJK3hXdWV6eWV5YlUKLS0tIGd5Qnlnd3JLSWRBS29tNktTN3Vx\nTWRtZVZ2bklJREUzLzd3WW5EazlKYlkKgU0DjZVMtPnPipdD5oblVqVgMb2oYu3k\nd3JQnA9rdeUIveEOb7vwgisKwk4dFgS4fOG/+p5r1jY9pdKRT2HxUg==\n-----END AGE ENCRYPTED FILE-----\n" 8 | } 9 | ], 10 | "lastmodified": "2025-11-30T08:44:44Z", 11 | "mac": "ENC[AES256_GCM,data:YpzePza4+ZVJdCJyypAEGN/WdIuT66UzGXR3yRepZhwl3n88c5kTmSKwAnef82A5jPlUt2L0gkN7kPJnCxEO6RAZSzsXIWwZJqkBkIpUDgtEZQ4RgI8wsv+TcUaReiJbfV11GdGN+R7RlypQrQEKzb4y2XpMZ0R71wRIiT8giN4=,iv:m2k6GltVczpTokjXdE2lVzv8JV0wicqiAwB8y8t1sgI=,tag:Is1pRr927FtrKPqK26ChRw==,type:str]", 12 | "version": "3.11.0" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /modules/tpm2-ssh.nix: -------------------------------------------------------------------------------- 1 | { ... }: 2 | { 3 | flake.nixosModules.tpm2-ssh = 4 | { 5 | config, 6 | pkgs, 7 | lib, 8 | ... 9 | }: 10 | { 11 | key = "nixos-config.modules.nixos.tpm2-ssh"; 12 | 13 | options = { 14 | programs.openssh.tpm2.enable = lib.mkEnableOption "Configure system to be able to use SSH keys in TPM2 (user should be in 'tss' group)"; 15 | }; 16 | 17 | config = lib.mkIf config.programs.openssh.tpm2.enable { 18 | environment.systemPackages = with pkgs; [ 19 | tpm2-tools 20 | ]; 21 | 22 | programs.ssh.agentPKCS11Whitelist = "/run/current-system/sw/lib/*"; 23 | 24 | security.tpm2 = { 25 | enable = true; 26 | pkcs11.enable = true; 27 | abrmd.enable = true; 28 | pkcs11.package = pkgs.tpm2-pkcs11.overrideAttrs ( 29 | _f: p: { 30 | configureFlags = [ "--disable-fapi" ]; 31 | patches = p.patches ++ [ 32 | (config.lib.self.file "0002-remove-fapi-message.patch") 33 | ]; 34 | } 35 | ); 36 | tctiEnvironment.enable = true; # TPM2TOOLS_TCTI and TPM2_PKCS11_TCTI env variables 37 | }; 38 | 39 | environment.variables.TSS2_LOG = "fapi+NONE"; 40 | }; 41 | }; 42 | } 43 | -------------------------------------------------------------------------------- /modules/machines/devcontainer.nix: -------------------------------------------------------------------------------- 1 | { 2 | self, 3 | inputs, 4 | ... 5 | }: 6 | { 7 | flake.nixosConfigurations.devcontainer = inputs.nixpkgs.lib.nixosSystem { 8 | system = "x86_64-linux"; 9 | modules = [ 10 | self.nixosModules.devcontainer-configuration 11 | ]; 12 | }; 13 | 14 | flake.nixosModules.devcontainer-configuration = 15 | { 16 | lib, 17 | ... 18 | }: 19 | { 20 | key = "nixos-config.modules.nixos.devcontainer-configuration"; 21 | imports = [ 22 | self.nixosModules.baseline 23 | self.nixosModules.impermanence 24 | { 25 | home-manager.users.binarin = self.homeModules.claude-code; 26 | } 27 | ]; 28 | 29 | config = { 30 | nixos-config.export-metrics.enable = false; 31 | boot.isContainer = true; 32 | 33 | console.enable = true; 34 | security.sudo.enable = lib.mkForce false; 35 | 36 | networking.useNetworkd = true; 37 | networking.useHostResolvConf = false; 38 | 39 | services.getty.autologinUser = "binarin"; 40 | 41 | nix.extraOptions = '' 42 | experimental-features = nix-command flakes 43 | ''; 44 | 45 | networking.hostName = "devcontainer"; 46 | 47 | system.stateVersion = "25.05"; 48 | }; 49 | }; 50 | } 51 | -------------------------------------------------------------------------------- /files/git-commit-template.txt: -------------------------------------------------------------------------------- 1 | 2 | # : (If applied, this commit will...) (Max 50 char) 3 | # |<---- Using a Maximum Of 50 Characters ---->| 4 | 5 | 6 | # Explain why this change is being made 7 | # |<---- Try To Limit Each Line to a Maximum Of 72 Characters ---->| 8 | 9 | 10 | # Provide links or keys to any relevant tickets, articles or other resources 11 | # Example: Github issue #23 12 | 13 | # --- COMMIT END --- 14 | # Type can be 15 | # feat (new feature) 16 | # fix (bug fix) 17 | # refactor (refactoring production code) 18 | # style (formatting, missing semi colons, etc; no code change) 19 | # docs (changes to documentation) 20 | # test (adding or refactoring tests; no production code change) 21 | # chore (updating grunt tasks etc; no production code change) 22 | # -------------------- 23 | # Remember to 24 | # Capitalize the subject line 25 | # Use the imperative mood in the subject line 26 | # Do not end the subject line with a period 27 | # Separate subject from body with a blank line 28 | # Use the body to explain what and why vs. how 29 | # Can use multiple lines with "-" for bullet points in body 30 | # -------------------- 31 | # For more information about this template, check out 32 | # https://gist.github.com/adeekshith/cd4c95a064977cdc6c50 33 | -------------------------------------------------------------------------------- /.forgejo/workflows/master.yaml: -------------------------------------------------------------------------------- 1 | # auto-generated via: nix run .#ci-template-generator 2 | jobs: 3 | check: 4 | needs: 5 | - build-all-configurations-job 6 | runs-on: native 7 | steps: 8 | - uses: actions/checkout@v4 9 | - run: 'nix flake check 10 | 11 | ' 12 | nixos-configuration: 13 | runs-on: native 14 | steps: 15 | - uses: actions/checkout@v4 16 | - env: 17 | GIT_CRYPT_KEY: ${{ secrets.GIT_CRYPT_KEY }} 18 | run: "git crypt unlock <(echo \"$GIT_CRYPT_KEY\"|base64 -d)\nnix build \"$(pwd)#nixosConfigurations.${{\ 19 | \ matrix.nixosConfiguration }}.config.system.build.toplevel\" \\\n --keep-going\ 20 | \ \\\n -j auto \\\n -o \"$HOME/.cache/nixos-config/master/nixos-configuration/${{\ 21 | \ matrix.nixosConfiguration }}\"\n" 22 | strategy: 23 | fail-fast: false 24 | matrix: 25 | nixosConfiguration: 26 | - demandred 27 | - devcontainer 28 | - docker-on-nixos 29 | - forgejo 30 | - furfur 31 | - ishamael 32 | - iso 33 | - mail 34 | - media 35 | - monitor 36 | - nix-cache 37 | - qdevice 38 | 'on': 39 | pull_request: 40 | types: 41 | - opened 42 | - synchronize 43 | - reopened 44 | push: 45 | branches: 46 | - master 47 | -------------------------------------------------------------------------------- /modules/impure-nix-setup.nix: -------------------------------------------------------------------------------- 1 | { self, inputs, ... }: 2 | { 3 | flake.nixosModules.impure-nix-setup = 4 | { config, ... }: 5 | { 6 | key = "nixos-config.modules.nixos.impure-nix-setup"; 7 | 8 | imports = [ 9 | self.modules.generic.flake-files 10 | ]; 11 | 12 | config = { 13 | nix.extraOptions = '' 14 | gc-keep-outputs = true 15 | gc-keep-derivations = true 16 | ''; 17 | 18 | environment.etc."nix/inputs/nixpkgs".source = "${inputs.nixpkgs}"; 19 | environment.etc."nix/inputs/self-absolute-path".text = "${self}"; 20 | environment.etc."nix/overlays/self-overlays.nix".source = 21 | config.lib.self.file "nixos-configuration-overlays-for-impure-nix-commands.nix"; 22 | environment.etc."nix/nixpkgs-config.nix".source = 23 | config.lib.self.file "nixos-configuration-nix-config-for-impure-nix-commands.nix"; 24 | 25 | environment.variables.NIXPKGS_CONFIG = "/etc/nix/nixpkgs-config.nix"; 26 | 27 | nix.nixPath = [ 28 | "nixpkgs=/etc/nix/inputs/nixpkgs" 29 | "nixpkgs-overlays=/etc/nix/overlays" 30 | ]; 31 | 32 | nix.registry.nixpkgs.flake = inputs.nixpkgs; 33 | nix.registry.nixpkgs-unstable.flake = inputs.nixpkgs-unstable; 34 | nix.registry.flake-parts.flake = inputs.flake-parts; 35 | }; 36 | }; 37 | } 38 | -------------------------------------------------------------------------------- /terraform/variables.tf: -------------------------------------------------------------------------------- 1 | variable "vm_name" { 2 | description = "Name of the virtual machine" 3 | type = string 4 | default = "debian-vm" 5 | } 6 | 7 | variable "memory" { 8 | description = "Memory allocation for the VM in MB" 9 | type = number 10 | default = 2048 11 | } 12 | 13 | variable "vcpu" { 14 | description = "Number of virtual CPUs" 15 | type = number 16 | default = 2 17 | } 18 | 19 | variable "disk_size" { 20 | description = "Disk size in bytes" 21 | type = number 22 | default = 21474836480 # 20GB 23 | } 24 | 25 | variable "static_ip" { 26 | description = "Static IP address for the VM" 27 | type = string 28 | default = "192.168.2.17" 29 | } 30 | 31 | variable "gateway" { 32 | description = "Gateway IP address" 33 | type = string 34 | default = "192.168.2.1" 35 | } 36 | 37 | variable "dns_servers" { 38 | description = "List of DNS servers" 39 | type = list(string) 40 | default = ["192.168.2.1"] 41 | } 42 | 43 | variable "network_name" { 44 | description = "Name of the libvirt network" 45 | type = string 46 | default = "br0" 47 | } 48 | 49 | variable "libvirt_uri" { 50 | description = "Libvirt connection URI" 51 | type = string 52 | default = "qemu+ssh://user@remote-host/system?known_hosts=/persist/home/binarin/.ssh/known_hosts.d/known_hosts" 53 | } 54 | -------------------------------------------------------------------------------- /files/0002-remove-fapi-message.patch: -------------------------------------------------------------------------------- 1 | diff --color -ur source.orig/src/lib/backend.c source/src/lib/backend.c 2 | --- source.orig/src/lib/backend.c 1970-01-01 01:00:01.000000000 +0100 3 | +++ source/src/lib/backend.c 2025-10-28 18:43:38.657159855 +0100 4 | @@ -54,7 +54,7 @@ 5 | return rv; 6 | } 7 | if (rv != CKR_FUNCTION_NOT_SUPPORTED) { 8 | - LOGW(msg); 9 | + // LOGW(msg); 10 | } 11 | } else { 12 | fapi_init = true; 13 | diff --color -ur source.orig/src/lib/backend_fapi.c source/src/lib/backend_fapi.c 14 | --- source.orig/src/lib/backend_fapi.c 1970-01-01 01:00:01.000000000 +0100 15 | +++ source/src/lib/backend_fapi.c 2025-10-28 18:45:00.983488823 +0100 16 | @@ -34,9 +34,9 @@ 17 | } 18 | 19 | const char *version = is_release ? VERSION : "master"; 20 | - LOGW("Listing FAPI token objects failed: \"%s\"\n" 21 | - "Please see https://github.com/tpm2-software/tpm2-pkcs11/blob/%s/docs/FAPI.md " 22 | - "for more details", Tss2_RC_Decode(rc), version); 23 | + /* LOGW("Listing FAPI token objects failed: \"%s\"\n" */ 24 | + /* "Please see https://github.com/tpm2-software/tpm2-pkcs11/blob/%s/docs/FAPI.md " */ 25 | + /* "for more details", Tss2_RC_Decode(rc), version); */ 26 | } 27 | 28 | static CK_RV get_key(FAPI_CONTEXT *fapictx, tpm_ctx *tctx, const char *path, uint32_t *esysHandle, uint32_t *pid) { 29 | -------------------------------------------------------------------------------- /modules/flake-packages.nix: -------------------------------------------------------------------------------- 1 | # XXX get rid of it 2 | { self, lib, ... }: 3 | let 4 | dir = "${self}/packages"; 5 | allFiles = builtins.readDir dir; 6 | 7 | nameToPathWithSkipped = lib.mapAttrs' ( 8 | nm: tp: 9 | if tp == "regular" && lib.hasSuffix ".nix" nm then 10 | lib.nameValuePair (lib.removeSuffix ".nix" nm) "${dir}/${nm}" 11 | else if tp == "directory" && builtins.pathExists "${dir}/${nm}/default.nix" then 12 | lib.nameValuePair nm "${dir}/${nm}/default.nix" 13 | else 14 | lib.nameValuePair "__discard" null 15 | ) allFiles; 16 | 17 | nameToPath = builtins.removeAttrs nameToPathWithSkipped [ "__discard" ]; 18 | 19 | overlay = 20 | final: _prev: 21 | lib.genAttrs (lib.attrNames nameToPath) ( 22 | nm: 23 | let 24 | fn = import nameToPath."${nm}"; 25 | # Provide self as flake argument for compatibility with old packages 26 | packageFn = 27 | if (builtins.functionArgs fn) ? "flake" then 28 | fn { 29 | flake = { 30 | inputs = { inherit self; }; 31 | }; 32 | } 33 | else 34 | fn; 35 | in 36 | final.callPackage packageFn { } 37 | ); 38 | in 39 | { 40 | 41 | flake.nixosModules.flake-packages = 42 | { ... }: 43 | { 44 | key = "nixos-config.modules.nixos.flake-packages"; 45 | 46 | config = { 47 | nixpkgs.overlays = [ overlay ]; 48 | }; 49 | }; 50 | } 51 | -------------------------------------------------------------------------------- /modules/home-manager.nix: -------------------------------------------------------------------------------- 1 | { 2 | self, 3 | inputs, 4 | ... 5 | }: 6 | { 7 | flake-file.inputs = { 8 | home-manager.url = "github:nix-community/home-manager/release-25.11"; 9 | home-manager.inputs.nixpkgs.follows = "nixpkgs"; 10 | home-manager-master.url = "github:nix-community/home-manager"; 11 | home-manager-master.inputs.nixpkgs.follows = "nixpkgs"; 12 | }; 13 | 14 | flake.nixosModules.home-manager = 15 | { 16 | specialArgs, 17 | ... 18 | }: 19 | { 20 | key = "nixos-config.modules.nixos.home-manager"; 21 | 22 | imports = [ inputs.home-manager.nixosModules.home-manager ]; 23 | 24 | config = { 25 | home-manager.sharedModules = [ 26 | self.homeModules.home-misc 27 | ]; 28 | home-manager.extraSpecialArgs = specialArgs; 29 | home-manager.useGlobalPkgs = true; 30 | home-manager.backupFileExtension = "backup"; 31 | }; 32 | }; 33 | 34 | flake.homeModules.home-misc = 35 | { lib, ... }: 36 | { 37 | key = "nixos-config.modules.home.home-misc"; 38 | 39 | config = { 40 | home.keyboard = lib.mkDefault null; 41 | home.activation = { 42 | removeCommonConfictingFiles = lib.hm.dag.entryBefore [ "checkLinkTargets" ] '' 43 | $DRY_RUN_CMD rm -fv ~/.gtkrc-2.0 ~/.gtkrc-2.0.backup 44 | $DRY_RUN_CMD rm -fv ~/.config/emacs/{early-,}init.el{,c} 45 | ''; 46 | }; 47 | }; 48 | }; 49 | } 50 | -------------------------------------------------------------------------------- /machines/qdevice/hardware-configuration.nix: -------------------------------------------------------------------------------- 1 | # Do not modify this file! It was generated by ‘nixos-generate-config’ 2 | # and may be overwritten by future invocations. Please make changes 3 | # to /etc/nixos/configuration.nix instead. 4 | { 5 | config, 6 | lib, 7 | modulesPath, 8 | ... 9 | }: 10 | 11 | { 12 | imports = [ 13 | (modulesPath + "/installer/scan/not-detected.nix") 14 | ]; 15 | 16 | boot.initrd.availableKernelModules = [ 17 | "xhci_pci" 18 | "nvme" 19 | "usbhid" 20 | "usb_storage" 21 | "sd_mod" 22 | "sdhci_pci" 23 | ]; 24 | boot.initrd.kernelModules = [ ]; 25 | boot.kernelModules = [ "kvm-intel" ]; 26 | boot.extraModulePackages = [ ]; 27 | 28 | # Enables DHCP on each ethernet and wireless interface. In case of scripted networking 29 | # (the default) this is the recommended approach. When using systemd-networkd it's 30 | # still possible to use this option, but it's recommended to use it in conjunction 31 | # with explicit per-interface declarations with `networking.interfaces..useDHCP`. 32 | networking.useDHCP = lib.mkDefault true; 33 | # networking.interfaces.enp0s20f0u2.useDHCP = lib.mkDefault true; 34 | # networking.interfaces.enp2s0.useDHCP = lib.mkDefault true; 35 | # networking.interfaces.enp3s0.useDHCP = lib.mkDefault true; 36 | 37 | nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; 38 | hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; 39 | } 40 | -------------------------------------------------------------------------------- /modules/nix.nix: -------------------------------------------------------------------------------- 1 | { 2 | inputs, 3 | lib, 4 | ... 5 | }: 6 | let 7 | nixpkgsConfig = { 8 | allowUnfree = true; 9 | oraclejdk.accept_license = true; 10 | permittedInsecurePackages = [ 11 | # used by trezor-agent, but vulnerability is about leaking 12 | # generated keys - so doesn't matter, as keys do not leave 13 | # trezor 14 | "python3.13-ecdsa-0.19.1" 15 | ]; 16 | }; 17 | in 18 | { 19 | flake-file.inputs = { 20 | determinate.url = "https://flakehub.com/f/DeterminateSystems/determinate/*"; 21 | }; 22 | 23 | perSystem = 24 | { system, ... }: 25 | { 26 | _module.args.pkgs = import inputs.nixpkgs { 27 | inherit system; 28 | config = nixpkgsConfig; 29 | }; 30 | }; 31 | 32 | flake.nixosModules.nix = 33 | { ... }: 34 | { 35 | key = "nixos-config.modules.nixos.nix"; 36 | imports = [ 37 | # inputs.determinate.nixosModules.default 38 | ]; 39 | 40 | config = { 41 | nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; 42 | 43 | nix = { 44 | settings = { 45 | sandbox = true; 46 | substituters = [ "https://cache.nixos.org" ]; 47 | }; 48 | extraOptions = '' 49 | experimental-features = nix-command flakes ca-derivations 50 | ''; 51 | }; 52 | 53 | nix.settings.trusted-users = [ "root" ]; 54 | 55 | nixpkgs.config = nixpkgsConfig; 56 | }; 57 | }; 58 | 59 | } 60 | -------------------------------------------------------------------------------- /ansible/playbooks/pve/files/valak-sanoid.conf: -------------------------------------------------------------------------------- 1 | [bpool/BOOT/debian] 2 | use_template = host 3 | 4 | [rpool/ROOT/debian] 5 | use_template = host 6 | 7 | [rpool/home] 8 | use_template = host 9 | 10 | [rpool/home/root] 11 | use_template = host 12 | 13 | [rpool/var] 14 | use_template = host 15 | 16 | [rpool/var/lib] 17 | use_template = host 18 | 19 | [rpool/var/spool] 20 | use_template = host 21 | 22 | # [spinning-zfs/pve-data/subvol-111-disk-0] 23 | # # /media/movies 24 | # use_template = media 25 | 26 | # [spinning-zfs/pve-data/subvol-111-disk-1] 27 | # # /media/music 28 | # use_template = media 29 | 30 | # [spinning-zfs/pve-data/subvol-111-disk-2] 31 | # # /media/torrents/regular 32 | # use_template = media 33 | 34 | # # [spinning-zfs/pve-data/subvol-111-disk-3] 35 | # # # /media/torrents/incomplete 36 | 37 | # [spinning-zfs/pve-data/subvol-111-disk-4] 38 | # # /media/torrents/rare 39 | # use_template = media 40 | 41 | # [spinning-zfs/pve-data/subvol-111-disk-5] 42 | # # /media/tubearchivist 43 | # use_template = media 44 | 45 | # [spinning-zfs/pve-data/subvol-111-disk-6] 46 | # # /media/annex 47 | # use_template = media 48 | 49 | [template_media] 50 | daily = 30 51 | weekly = 8 52 | monthly = 4 53 | autosnap = yes 54 | autoprune = yes 55 | 56 | [template_host] 57 | frequent_period = 15 58 | frequently = 8 59 | hourly = 36 60 | daily = 30 61 | monthly = 11 62 | yearly = 2 63 | autosnap = yes 64 | autoprune = yes 65 | -------------------------------------------------------------------------------- /files/website-hackernews.css: -------------------------------------------------------------------------------- 1 | @-moz-document domain("news.ycombinator.com"), 2 | domain("news.ycombinator.com"), 3 | url-prefix("https://news.ycombinator.com/") { 4 | @media (-moz-bool-pref: "user.theme.dark.zenburn") { 5 | body, #hnmain { 6 | background-color: var(--zenburn-bg) !important; 7 | } 8 | 9 | a:link, .c00 a:link { 10 | color: var(--zenburn-fg); 11 | } 12 | 13 | .sitebit, .sitebit a { 14 | color: var(--zenburn-fg-1) !important; 15 | } 16 | 17 | .subline > a:last-of-type { 18 | color: var(--zenburn-yellow) !important; 19 | } 20 | 21 | .titleline > a:link { 22 | color: var(--zenburn-blue-1) !important; 23 | } 24 | #hnmain > tbody > tr:first-of-type > td { 25 | background-color: var(--zenburn-bg-pl2) !important; 26 | border-radius: 5px; 27 | 28 | img { 29 | border-radius: 5px; 30 | } 31 | } 32 | 33 | #hnmain > tbody > tr:last-of-type table td { 34 | background-color: var(--zenburn-bg-pl2) !important; 35 | } 36 | 37 | input[type="text"] { 38 | background-color: var(--zenburn-bg-08); 39 | color: var(--zenburn-fg); 40 | border-radius: 5px; 41 | } 42 | 43 | textarea { 44 | background-color: var(--zenburn-bg-1); 45 | } 46 | 47 | .c00 { 48 | color: var(--zenburn-fg) !important; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /modules/binarin/wl-kbptr/config: -------------------------------------------------------------------------------- 1 | # wl-kbptr can be configured with a configuration file. 2 | # The file location can be passed with the -c parameter. 3 | # Othewise the `$XDG_CONFIG_HOME/wl-kbptr/config` file will 4 | # be loaded if it exits. Below is the default configuration. 5 | 6 | [general] 7 | home_row_keys= 8 | cancellation_status_code=0 9 | 10 | [mode_tile] 11 | label_color=#fffd 12 | label_select_color=#fd0d 13 | unselectable_bg_color=#2226 14 | selectable_bg_color=#030a 15 | selectable_border_color=#040c 16 | label_font_family=sans-serif 17 | label_font_size=8 50% 100 18 | label_symbols=abcdefghijklmnopqrstuvwxyz 19 | 20 | [mode_floating] 21 | source=detect 22 | label_color=#fffd 23 | label_select_color=#fd0d 24 | unselectable_bg_color=#2226 25 | selectable_bg_color=#171 26 | selectable_border_color=#040c 27 | label_font_family=sans-serif 28 | label_font_size=12 50% 100 29 | label_symbols=abcdefghijklmnopqrstuvwxyz 30 | 31 | [mode_bisect] 32 | label_color=#fffd 33 | label_font_size=20 34 | label_font_family=sans-serif 35 | label_padding=12 36 | pointer_size=20 37 | pointer_color=#e22d 38 | unselectable_bg_color=#2226 39 | even_area_bg_color=#0304 40 | even_area_border_color=#0408 41 | odd_area_bg_color=#0034 42 | odd_area_border_color=#0048 43 | history_border_color=#3339 44 | 45 | [mode_split] 46 | pointer_size=20 47 | pointer_color=#e22d 48 | bg_color=#2226 49 | area_bg_color=#11111188 50 | vertical_color=#8888ffcc 51 | horizontal_color=#008800cc 52 | history_border_color=#3339 53 | 54 | [mode_click] 55 | button=left 56 | -------------------------------------------------------------------------------- /modules/sops.nix: -------------------------------------------------------------------------------- 1 | { 2 | self, 3 | inputs, 4 | ... 5 | }: 6 | { 7 | flake-file.inputs = { 8 | sops-nix.url = "github:Mic92/sops-nix"; 9 | sops-nix.inputs.nixpkgs.follows = "nixpkgs"; 10 | }; 11 | 12 | flake.nixosModules.sops = 13 | { config, ... }: 14 | { 15 | key = "nixos-config.modules.nixos.sops"; 16 | 17 | imports = [ 18 | inputs.sops-nix.nixosModules.sops 19 | self.modules.generic.flake-files 20 | ]; 21 | 22 | config = { 23 | # should be stringified path 24 | sops.defaultSopsFile = "${config.lib.self.file' "secrets/${config.networking.hostName}/secrets.yaml"}"; 25 | 26 | sops.age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ]; 27 | }; 28 | }; 29 | 30 | flake.homeModules.sops = 31 | { config, osConfig, ... }: 32 | { 33 | key = "nixos-config.modules.home.sops"; 34 | 35 | imports = [ 36 | inputs.sops-nix.homeManagerModules.sops 37 | self.modules.generic.flake-files 38 | ]; 39 | 40 | config = { 41 | sops = { 42 | age.keyFile = 43 | if osConfig.impermanence.enable then 44 | "/persist/${config.home.homeDirectory}/.config/age/nixos-config-keys.txt" 45 | else 46 | "${config.home.homeDirectory}/.config/age/nixos-config-keys.txt"; 47 | defaultSopsFile = config.lib.self.optionalFile' "secrets/${osConfig.networking.hostName}/user-${config.home.username}.yaml"; 48 | }; 49 | }; 50 | }; 51 | } 52 | -------------------------------------------------------------------------------- /ansible/roles/sshd/templates/authorized_keys.j2: -------------------------------------------------------------------------------- 1 | #jinja2: lstrip_blocks: True 2 | # {{ ansible_managed }} 3 | 4 | {% set ns = namespace(comment_printed = False, seen_keys = {}) %} 5 | {% for key, value in ssh_keys.items() %} 6 | {% if ('tags' in value and 'presence' in value.tags) %} 7 | {% if not ns.comment_printed -%} 8 | # Operators 9 | {% endif %} 10 | {% set ns.comment_printed = True %} 11 | {% set xxx = ns.seen_keys.__setitem__(key, True) -%} 12 | {{value.public_key }} {{ value.description }} 13 | {% endif %} 14 | {% endfor %} 15 | 16 | {% set ns.comment_printed = False %} 17 | {% for key, value in ssh_keys.items() %} 18 | {% if ('tags' in value and 'user-ca' in value.tags) %} 19 | {% if not ns.comment_printed -%} 20 | # Trusted CA keys 21 | {% endif %} 22 | {% set ns.comment_printed = True %} 23 | {% if (key not in ns.seen_keys) -%} 24 | {{ value.public_key }} {{ value.description }} 25 | {% else -%} 26 | # Already included above: {{ value.description }} 27 | {% endif %} 28 | {% endif %} 29 | {% endfor %} 30 | 31 | {% set ns.comment_printed = False %} 32 | {% for key, value in ssh_keys.items() %} 33 | {% if ('force_install_on' in value and inventory_hostname in value.force_install_on and username in value.force_install_on[inventory_hostname]) -%} 34 | {% if not ns.comment_printed -%} 35 | # Per-host overrides 36 | {% endif %} 37 | {% set ns.comment_printed = True -%} 38 | {{ value.public_key }} {{ value.description }} 39 | {% endif %} 40 | {% endfor %} 41 | -------------------------------------------------------------------------------- /files/pve-impermanence-hook.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | use strict; 3 | use warnings; 4 | 5 | print "GUEST HOOK: " . join(' ', @ARGV). "\n"; 6 | 7 | # First argument is the vmid 8 | my $vmid = shift; 9 | 10 | # Second argument is the phase 11 | my $phase = shift; 12 | 13 | if ($phase eq 'pre-start') { 14 | # First phase 'pre-start' will be executed before the guest 15 | # is started. Exiting with a code != 0 will abort the start 16 | print "$vmid is starting, doing preparations.\n"; 17 | 18 | print "zfs rollback -r rpool/data/subvol-$vmid-disk-0\@blank\n"; 19 | system("zfs rollback -r rpool/data/subvol-$vmid-disk-0\@blank") == 0 20 | or die "zfs rollback failed: $?"; 21 | 22 | # print "preparations failed, aborting." 23 | # exit(1); 24 | } elsif ($phase eq 'post-start') { 25 | # Second phase 'post-start' will be executed after the guest 26 | # successfully started. 27 | 28 | print "$vmid started successfully.\n"; 29 | } elsif ($phase eq 'pre-stop') { 30 | # Third phase 'pre-stop' will be executed before stopping the guest 31 | # via the API. Will not be executed if the guest is stopped from 32 | # within e.g., with a 'poweroff' 33 | print "$vmid will be stopped.\n"; 34 | } elsif ($phase eq 'post-stop') { 35 | # Last phase 'post-stop' will be executed after the guest stopped. 36 | # This should even be executed in case the guest crashes or stopped 37 | # unexpectedly. 38 | print "$vmid stopped. Doing cleanup.\n"; 39 | 40 | } else { 41 | die "got unknown phase '$phase'\n"; 42 | } 43 | 44 | exit(0); 45 | -------------------------------------------------------------------------------- /ansible/playbooks/pve/files/raum-sanoid.conf: -------------------------------------------------------------------------------- 1 | 2 | [bpool/BOOT/debian] 3 | use_template = host 4 | 5 | [rpool/ROOT/debian] 6 | use_template = host 7 | 8 | [rpool/home] 9 | use_template = host 10 | 11 | [rpool/home/root] 12 | use_template = host 13 | 14 | [rpool/var] 15 | use_template = host 16 | 17 | [rpool/var/lib] 18 | use_template = host 19 | 20 | [rpool/var/spool] 21 | use_template = host 22 | 23 | [spinning-zfs/pve-data/subvol-111-disk-0] 24 | # /media/movies 25 | use_template = media 26 | 27 | [spinning-zfs/pve-data/subvol-111-disk-1] 28 | # /media/music 29 | use_template = media 30 | 31 | [spinning-zfs/pve-data/subvol-111-disk-2] 32 | # /media/torrents/regular 33 | use_template = media 34 | 35 | # [spinning-zfs/pve-data/subvol-111-disk-3] 36 | # # /media/torrents/incomplete 37 | 38 | [spinning-zfs/pve-data/subvol-111-disk-4] 39 | # /media/torrents/rare 40 | use_template = media 41 | 42 | [spinning-zfs/pve-data/subvol-111-disk-5] 43 | # /media/tubearchivist 44 | use_template = media 45 | 46 | [spinning-zfs/pve-data/subvol-111-disk-6] 47 | # /media/annex 48 | use_template = media 49 | 50 | [spinning-zfs/pve-data/subvol-111-disk-7] 51 | # /media/usenet 52 | use_template = media 53 | 54 | [template_media] 55 | daily = 30 56 | weekly = 8 57 | monthly = 4 58 | autosnap = yes 59 | autoprune = yes 60 | 61 | [template_host] 62 | frequent_period = 15 63 | frequently = 8 64 | hourly = 36 65 | daily = 30 66 | monthly = 11 67 | yearly = 2 68 | autosnap = yes 69 | autoprune = yes 70 | -------------------------------------------------------------------------------- /secrets/furfur/ssh_host_ed25519_key: -------------------------------------------------------------------------------- 1 | { 2 | "data": "ENC[AES256_GCM,data:Q1AVHiAdq7VoglgononAsTEx1uOYfOQ1yvF0boxKum4PNU3wSewtKvmtCFNRoy+eztHtau+Yq99Hd7XWodPnQF/sq3i9vsPL1gJ3T8iG/q+laBcUbEY6Yk9OXNtpAwyD8j70Ojdo5llKhtpiX6SINUtC5+A7T9zIHFpV2a2d7JV9IBD/KLgKu9t9CxyCssmOXNisaePlUK0+DrkjNqTHmvFMTJKAE2RCXch/5Jytzgbs7TAUy/PW+o6PQ9pybIM0SSsFZdn6YSXZFBRtcoj38o5qZOH2pBpNBpFB+ln8b1dvx3XloPucbJBkF3BiZ0KukdPd4KYQ7ZngJz5IYhpK3HDkIWVd6Z2fsEzaRj1Yc3Zfu0vKu4E6OeZR+yN2KKWSx8gmatidtHCXo8U+M82oWevBPovzt2i37/scGj5IPMSh8ED4B5Ij3btlv9D90bkqaoYHjESY0VuCYz5hvTCrB28ApCGNPL68fz2ATQYe0fh1cjj+a5XnSgi0vN0xw5YzOnQaLDoU1t1ZwCfbqVU4e3T1pGcDLNd8J250,iv:1zIhTvhR7cZ/GLiTn73Hmh0ivg8f0HifitZvgHRpHLE=,tag:r7S4PgGR0jJHYffMq40vrg==,type:str]", 3 | "sops": { 4 | "age": [ 5 | { 6 | "recipient": "age1dvu92xmh3w23ghey4l0epcs27m89f3nswzs5twgnel36xxu42f4sfsv023", 7 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAwSWl1bjBTNllsTGtWZGhF\nZHE5L3pBd3VmblZiMXJtVlZVeXZBbjZlcjNRClFDUkxNM3lTUWxiUmJkT1FJeU5B\nTG1Ia09WZlhyVXBPb2xNQXh5YkRFUFkKLS0tIG1NUHhNYXB3d0xESldHTjc3T0dv\nMEIrKzQ4UWNQbGdTUWN6clVHQ0hDSmcKSHAMCvJFgUcpAmfMN5jZv6k4Y1tOz4cE\n5KJfX24YkpTeMLMQQPmbTr7rDiKCyNcolsg36mTtGGPXwZxIQ0SPSg==\n-----END AGE ENCRYPTED FILE-----\n" 8 | } 9 | ], 10 | "lastmodified": "2025-11-30T08:11:13Z", 11 | "mac": "ENC[AES256_GCM,data:G7pHjq/fdu6tZp6hriOX8O1rXzhBFgR+napCQV8QX5Ge7ACVzjkoqHG8hu83diy492w2XcipGmPj67FDX4F/E/j//XCUa1CpvubKetYVleI64dmI7bCp9RwWW0yUixTo/3E0C8BIvMdiHRk0F26I/ocPvwRk3y8xvYPRuQYayHY=,iv:v6KhmG9WPp0VpRnl2vZ4kxNx23gG4uW/YgFV3cXcHz0=,tag:4tkb6m2zOcj4xFe3nLqjvA==,type:str]", 12 | "version": "3.11.0" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /modules/foot.nix: -------------------------------------------------------------------------------- 1 | { 2 | ... 3 | }: 4 | { 5 | flake.homeModules.foot = 6 | { 7 | config, 8 | lib, 9 | osConfig, 10 | ... 11 | }: 12 | { 13 | key = "nixos-config.modules.home.foot"; 14 | 15 | config = lib.mkIf osConfig.services.graphical-desktop.enable { 16 | xdg.configFile."xdg-terminals.list".text = '' 17 | foot.desktop 18 | /execarg_default:org.codeberg.dnkl.foot.desktop:-- 19 | ''; 20 | 21 | programs.foot = { 22 | enable = true; 23 | settings = { 24 | main = { 25 | font = with config.stylix.fonts; "${monospace.name}:size=${toString sizes.terminal}"; 26 | locked-title = true; 27 | selection-target = "both"; 28 | }; 29 | url = { 30 | osc8-underline = "always"; 31 | }; 32 | colors = { 33 | background = "000000"; 34 | regular0 = "000000"; 35 | regular1 = "cd0000"; 36 | regular2 = "00cd00"; 37 | regular3 = "cdcd00"; 38 | regular4 = "0000cd"; 39 | regular5 = "cd00cd"; 40 | regular6 = "00cdcd"; 41 | regular7 = "faebd7"; 42 | bright0 = "404040"; 43 | bright1 = "ff0000"; 44 | bright2 = "00ff00"; 45 | bright3 = "ffff00"; 46 | bright4 = "0000ff"; 47 | bright5 = "ff00ff"; 48 | bright6 = "00ffff"; 49 | bright7 = "ffffff"; 50 | }; 51 | }; 52 | }; 53 | }; 54 | }; 55 | } 56 | -------------------------------------------------------------------------------- /modules/baseline/default.nix: -------------------------------------------------------------------------------- 1 | { self, inputs, ... }: 2 | { 3 | flake.nixosModules.baseline = 4 | { lib, pkgs, ... }: 5 | { 6 | key = "nixos-config.modules.nixos.baseline"; 7 | 8 | imports = [ 9 | "${inputs.srvos}/nixos/common/update-diff.nix" 10 | 11 | self.nixosModules.inventory 12 | self.nixosModules.nix 13 | self.nixosModules.sshd 14 | self.nixosModules.security 15 | self.nixosModules.monitored 16 | 17 | self.nixosModules.eternal-terminal 18 | 19 | self.nixosModules.binarin-baseline 20 | 21 | ]; 22 | 23 | environment.enableAllTerminfo = true; 24 | 25 | time.timeZone = lib.mkDefault "Europe/Amsterdam"; 26 | 27 | i18n.defaultLocale = "nl_NL.UTF-8"; 28 | i18n.extraLocales = [ "all" ]; 29 | 30 | environment.systemPackages = with pkgs; [ 31 | psmisc # pstree 32 | usbutils 33 | pciutils 34 | apg 35 | ]; 36 | 37 | programs.bat.enable = true; 38 | programs.direnv.enable = true; 39 | programs.fzf = { 40 | fuzzyCompletion = true; 41 | keybindings = true; 42 | }; 43 | programs.git.enable = true; 44 | programs.htop.enable = true; 45 | programs.iftop.enable = true; 46 | programs.iotop.enable = true; 47 | programs.mosh.enable = true; 48 | programs.ssh.startAgent = true; 49 | programs.tcpdump.enable = true; 50 | programs.tmux.enable = true; 51 | programs.traceroute.enable = true; 52 | programs.zoxide.enable = true; 53 | security.sudo = { 54 | enable = true; 55 | wheelNeedsPassword = false; 56 | }; 57 | }; 58 | } 59 | -------------------------------------------------------------------------------- /TODO.org: -------------------------------------------------------------------------------- 1 | #+STARTUP: nologdone nologrepeat nolognoteclock-out nologreschedule nologredeadline nologrefile 2 | #+TODO: TODO(t) | DONE(d) 3 | #+TODO: PLAN(r) | PROJ(p) 4 | #+TODO: | CNCL(c) 5 | * TODO add validation that every nixos module in modules/ has proper ~key~ 6 | * TODO Closure size tracking (with git notes?) 7 | * TODO `just lint` - remove check, convert to pre-commit hook, ??? 8 | * TODO Explain why explicit key is needed 9 | * TODO tpm2-pkcs11-esapi FTW 10 | * TODO configure treefmt / pre-commit hooks - for nix, ansible, terraform 11 | * TODO build-all - support remote-only builds 12 | --builders 'ssh://nix-cache x86_64-linux /home/binarin/.ssh/id_ed25519 16 1 big-parallel' --max-jobs 0 13 | * TODO connect desktop nix machines to proxmox backup server 14 | :PROPERTIES: 15 | :ID: aee8dfaa-6081-439c-b3ec-1df5765e3aa7 16 | :CREATED: [2025-12-01 Mon 09:38] 17 | :END: 18 | :CLOCK: 19 | CLOCK: [2025-12-01 Mon 09:38]--[2025-12-01 Mon 09:39] => 0:01 20 | :END: 21 | * TODO surface broken RTC - add backstop, early in stage-2 boot 22 | :PROPERTIES: 23 | :ID: 33c0e58f-bf97-4975-b63f-7bcfc4d2c418 24 | :CREATED: [2025-12-01 Mon 09:37] 25 | :END: 26 | :CLOCK: 27 | CLOCK: [2025-12-01 Mon 09:37]--[2025-12-01 Mon 09:38] => 0:01 28 | :END: 29 | * TODO smb-sketchup like share for valak vms 30 | :PROPERTIES: 31 | :ID: 8bde76c6-05cf-4c65-88ab-97c68d391e1f 32 | :CREATED: [2025-11-27 Thu 17:22] 33 | :END: 34 | * TODO Script to rolling upgrade all deploy_rs machines 35 | :PROPERTIES: 36 | :ID: 25507817-4383-4a0d-8234-ecc01264c6be 37 | :CREATED: [2025-12-01 Mon 19:33] 38 | :END: 39 | :CLOCK: 40 | CLOCK: [2025-12-01 Mon 19:33]--[2025-12-01 Mon 19:34] => 0:01 41 | :END: 42 | -------------------------------------------------------------------------------- /modules/deploy.nix: -------------------------------------------------------------------------------- 1 | { inputs, lib, ... }: 2 | let 3 | self = inputs.self; 4 | system = "x86_64-linux"; 5 | unmodifiedPkgs = import inputs.nixpkgs { inherit system; }; 6 | 7 | deployPkgs = import inputs.nixpkgs { 8 | inherit system; 9 | overlays = [ 10 | inputs.deploy-rs.overlays.default 11 | (_self: super: { 12 | deploy-rs = { 13 | inherit (unmodifiedPkgs) deploy-rs; 14 | lib = super.deploy-rs.lib; 15 | }; 16 | }) 17 | ]; 18 | }; 19 | 20 | # NOTE: can scrape nixosConfigurations, but computation is a bit too heavy 21 | deployableSystems = [ 22 | "forgejo" 23 | "monitor" 24 | "media" 25 | "nix-cache" 26 | "mail" 27 | "docker-on-nixos" 28 | ]; 29 | 30 | deployNixosSystem = 31 | hostName: 32 | let 33 | deployHostName = self.nixosConfigurations."${hostName}".config.networking.hostName; 34 | in 35 | { 36 | hostname = deployHostName; 37 | profiles.system = { 38 | sshUser = "root"; 39 | path = deployPkgs.deploy-rs.lib.activate.nixos self.nixosConfigurations."${hostName}"; 40 | }; 41 | }; 42 | in 43 | { 44 | flake = { 45 | options.deploy.nodes = lib.mkOption { 46 | type = with lib.types; lazyAttrsOf raw; 47 | }; 48 | config = { 49 | lib.deploy-nixos = deployPkgs.deploy-rs.lib.activate.nixos; 50 | 51 | deploy.nodes = lib.genAttrs deployableSystems deployNixosSystem; 52 | 53 | # This is highly advised, and will prevent many possible mistakes 54 | perSystem = 55 | { system, ... }: 56 | { 57 | checks = (inputs.deploy-rs.lib."${system}").deployChecks self.deploy; 58 | }; 59 | }; 60 | }; 61 | } 62 | -------------------------------------------------------------------------------- /secrets/furfur/ssh_host_ecdsa_key: -------------------------------------------------------------------------------- 1 | { 2 | "data": "ENC[AES256_GCM,data:kcX/Y6n29vICi1VAkx0SlDVpAutSHyv6/eU8Ry66s0P14DT7t8RBbrln5sLWNkMt7R5mdpbgjeMI1RODohjIcE42WCNHDICqAFINxrVG2Zoj/i/ZpKydrrzwqVZO3vSczExzhmM1iktWWc6pkIOimQqzW80Enm9oy5bhq82CoLZlc4/pvvjBNkZjMDDoA7KH9mjU8725Qhipji9J+r63yb7s9Yulu0xAIWwGJtS7EF9XtaOX7NFzjyoRx8QQCGTcuUh7ibB/QzLAyckgFvt6cj5UsqHjJuQxCzR7vnHu8L+Hhn3hR1Hqd7insMuHgwxFB7EqrOmPsROYZneuxTysAXdsELnmdGAjyw2nXXZuRTRf+EwxVBwcUsORqpyG6sSo4bv79yK3e5VVU6COmSvTwzVDJQGbjhGGQFNryrbkS4epSxV3gFv+EJG4yhw040pxX1Cce8kqxd/tGSG3vnGJ0cnhD5bDOGU0rRo2kROUgIQBKBtcAC/koUCSzYNpcOTn9JZnOPm7H+/6h8OlybWQVn4aE3yM9JHFA5pGDDJ6Y6lUIN7p0r8F445xKm7GjprEYKSIOmZQuImYGAaXjsBAzNIWY6eOdlIjTtBZbjvGLF97ybb535s0FBetSOV+swdNsZZ9nKKW7bru/Mcfl6c9X1j6DW+vQmrBCXdSJpz1pXwL,iv:GfqZsdU0UJyEHkBJ4TvUAmeZOL9av5xEBhTBzmoJAZs=,tag:T1RvS4xiqBlsQL14h4KkBA==,type:str]", 3 | "sops": { 4 | "age": [ 5 | { 6 | "recipient": "age1dvu92xmh3w23ghey4l0epcs27m89f3nswzs5twgnel36xxu42f4sfsv023", 7 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBxWXJRVlo0WUltWjRGcitl\nbXZvUysyc2YvQ1VQUGt2L1VhZjY4N3lEZUFZCnpkZGtwMzNWY1dxa2pDL2gzS1ZF\nM3NVeCtad3lxRnA2VlJjTGxNbGI3V28KLS0tIGZ3QlAvWldHMi83THJEZGI3aGFW\nOUFqUDlUWWFjaERaUDhYUzFPVXp3N28KYj7kUof7hYJqcph01Dddfyt/aEQikIsx\nMTxx0hq91fmZyINNTQr156Qj2/AcSsCSl6cKTigWxFqcAmIdhVBd6w==\n-----END AGE ENCRYPTED FILE-----\n" 8 | } 9 | ], 10 | "lastmodified": "2025-11-30T08:11:13Z", 11 | "mac": "ENC[AES256_GCM,data:27vgSHW8rIFuw9fy7CmIqHYcMkTccmr7czjnQxvi5+IXuZBtKObBw9xQZQTR8TBCcW/67bg638d+mLeUZYQ9/TaIN/Bu3rRYkYF/3SgwInMG7pdS3SXLxMiw9QReCNUBUgCDRqQ68AyP6n11//ihBR5/C1X9JBFc7jlaWY5lIwM=,iv:KkGYxk3koU14zQYwhN7gCgeQLW+N4vbRKUmrgwZizyo=,tag:jCpY+oGm9r6uHhqPfxf9PQ==,type:str]", 12 | "version": "3.11.0" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /ansible/playbooks/tailscale-exit-nodes.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: pbs-hetzner 3 | roles: 4 | - role: artis3n.tailscale 5 | vars: 6 | tailscale_args: >- 7 | --advertise-exit-node 8 | tailscale_authkey: tskey-auth-XXX-YYYYY 9 | vars: 10 | network_interfaces: 11 | tailscale0: 12 | firewalld_zone: trusted 13 | eth0: 14 | firewalld_zone: external 15 | tasks: 16 | - package: name=firewalld state=present 17 | 18 | - name: Adding interfaces to firewalld zones 19 | ansible.builtin.command: >- 20 | firewall-cmd {{ '--permanent' if item[1] else ''}} --add-interface={{ item[0].key }} --zone={{ item[0].value.firewalld_zone }} 21 | 22 | register: result 23 | changed_when: '"ALREADY_ENABLED" not in result.stderr and "ZONE_ALREADY_SET" not in result.stderr' 24 | loop: "{{ network_interfaces | dict2items | product([True, False]) | list }}" 25 | loop_control: 26 | label: "{{ item[0].key }} to {{ item[0].value.firewalld_zone }} ({{ 'permanently' if item[1] else 'runtime'}})" 27 | 28 | - name: Enable ipv6 masquerade for `external` firewalld zone 29 | ansible.posix.firewalld: 30 | zone: external 31 | rich_rule: rule family=ipv6 masquerade 32 | permanent: true 33 | immediate: true 34 | state: enabled 35 | 36 | - name: Open Proxmox Backup Server port 37 | ansible.posix.firewalld: 38 | zone: external 39 | state: enabled 40 | port: 8007/tcp 41 | permanent: true 42 | immediate: true 43 | 44 | - ansible.posix.sysctl: 45 | name: net.ipv4.ip_forward 46 | value: '1' 47 | sysctl_set: true 48 | state: absent 49 | reload: true 50 | -------------------------------------------------------------------------------- /ansible/roles/sshd/README.md: -------------------------------------------------------------------------------- 1 | SSH Daemon Configuration Role 2 | ============================= 3 | 4 | This role configures SSH daemon for security and manages SSH authorized keys and trusted CA keys. 5 | 6 | Requirements 7 | ------------ 8 | 9 | - Ansible 2.9 or higher 10 | - SSH daemon installed on target hosts 11 | - Root or sudo access to modify SSH configuration 12 | 13 | Role Variables 14 | -------------- 15 | 16 | ### SSH Configuration Paths 17 | - `sshd_config_path`: Path to main sshd_config file (default: `/etc/ssh/sshd_config`) 18 | - `sshd_config_d_path`: Path to sshd_config.d directory (default: `/etc/ssh/sshd_config.d`) 19 | - `authorized_keys_path`: Path to authorized_keys.d directory (default: `/etc/ssh/authorized_keys.d`) 20 | - `trusted_user_ca_keys_path`: Path to trusted user CA keys file (default: `/etc/ssh/trusted_user_ca_keys`) 21 | 22 | ### SSH Security Settings 23 | - `sshd_permit_root_login`: Root login setting (default: `"prohibit-password"`) 24 | - `sshd_password_authentication`: Enable password auth (default: `false`) 25 | - `sshd_kbd_interactive_authentication`: Enable keyboard interactive auth (default: `false`) 26 | - `sshd_use_pam`: Enable PAM (default: `false`) 27 | 28 | ### SSH Users and Keys 29 | - `sshd_users`: List of users to configure authorized keys for (default: `[]`) 30 | - `ssh_keys`: Dictionary of SSH key definitions (should be provided via vars_files) 31 | 32 | ### Other Settings 33 | - `sshd_include_config_d`: Include config.d directory (default: `true`) 34 | 35 | Dependencies 36 | ------------ 37 | 38 | None. 39 | 40 | Example Playbook 41 | ---------------- 42 | 43 | ```yaml 44 | - hosts: servers 45 | vars_files: 46 | - ssh-public-keys.yaml 47 | roles: 48 | - sshd 49 | ``` 50 | 51 | License 52 | ------- 53 | 54 | MIT-0 55 | 56 | Author Information 57 | ------------------ 58 | 59 | Maintained as part of nixos-config infrastructure. 60 | -------------------------------------------------------------------------------- /modules/lxc.nix: -------------------------------------------------------------------------------- 1 | { config, ... }: 2 | let 3 | flakeConfig = config; 4 | in 5 | { 6 | flake.nixosModules.lxc = 7 | { 8 | modulesPath, 9 | config, 10 | lib, 11 | pkgs, 12 | ... 13 | }: 14 | { 15 | key = "nixos-config.modules.nixos.lxc"; 16 | 17 | # Need to add to /etc/pve/lxc/XXX.conf 18 | # lxc.cgroup2.devices.allow: c 10:200 rwm 19 | # lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file 20 | imports = [ 21 | "${modulesPath}/virtualisation/proxmox-lxc.nix" 22 | ]; 23 | 24 | config = { 25 | proxmoxLXC.enable = true; 26 | 27 | proxmoxLXC.manageNetwork = true; 28 | networking.useNetworkd = true; 29 | networking.useHostResolvConf = false; 30 | networking.useDHCP = false; 31 | 32 | systemd.network.networks."40-lxc" = { 33 | matchConfig.Name = "eth0"; 34 | dns = flakeConfig.inventory.networks.home.dns; 35 | address = [ 36 | flakeConfig.inventory.ipAllocation."${config.networking.hostName}".home.primary.addressWithPrefix 37 | ]; 38 | routes = [ { Gateway = flakeConfig.inventory.networks.home.gateway; } ]; 39 | }; 40 | 41 | services.getty.autologinUser = "root"; 42 | 43 | # XXX this creates /sbin/init with /bin/sh shebang, not compatible with impermanence 44 | boot.loader.initScript.enable = lib.mkForce false; 45 | # XXX so copy it from regular lxc-container.nix for the time being 46 | system.build.installBootLoader = pkgs.writeScript "install-lxc-sbin-init.sh" '' 47 | #!${pkgs.runtimeShell} 48 | ${pkgs.coreutils}/bin/ln -fs "$1/init" /sbin/init 49 | ''; 50 | system.activationScripts.installInitScript = lib.mkForce '' 51 | ln -fs $systemConfig/init /init 52 | ''; 53 | }; 54 | }; 55 | } 56 | -------------------------------------------------------------------------------- /modules/machines/mail.nix: -------------------------------------------------------------------------------- 1 | { 2 | self, 3 | inputs, 4 | ... 5 | }: 6 | { 7 | flake.nixosConfigurations.mail = inputs.nixpkgs.lib.nixosSystem { 8 | system = "x86_64-linux"; 9 | modules = [ 10 | self.nixosModules.mail-configuration 11 | ]; 12 | 13 | }; 14 | 15 | flake.nixosModules.mail-configuration = 16 | { 17 | pkgs, 18 | ... 19 | }: 20 | { 21 | key = "nixos-config.modules.nixos.mail-configuration"; 22 | imports = [ 23 | self.nixosModules.default 24 | self.nixosModules.lxc 25 | ]; 26 | 27 | config = { 28 | networking.hostName = "mail"; 29 | system.stateVersion = "24.05"; 30 | 31 | # tailscale serve --bg --tls-terminated-tcp 1143 1143 32 | # tailscale serve --bg --tls-terminated-tcp 1025 1025 33 | # gpg --pinentry-mode loopback --quick-gen-key --passphrase '' 'Mail LXC ' 34 | 35 | environment.systemPackages = with pkgs; [ 36 | imapsync 37 | protonmail-bridge 38 | gnupg 39 | pinentry-tty 40 | pass 41 | ]; 42 | 43 | systemd.services.protonmail-bridge = { 44 | wantedBy = [ "multi-user.target" ]; 45 | serviceConfig = { 46 | Type = "simple"; 47 | Restart = "always"; 48 | User = "protonmail-bridge"; 49 | }; 50 | path = with pkgs; [ 51 | protonmail-bridge 52 | gnupg 53 | pass 54 | ]; 55 | script = '' 56 | protonmail-bridge -n 57 | ''; 58 | }; 59 | 60 | users.users.protonmail-bridge = { 61 | isNormalUser = true; 62 | group = "protonmail-bridge"; 63 | }; 64 | users.groups.protonmail-bridge = { }; 65 | 66 | nixos-config.export-metrics.enable = true; 67 | }; 68 | }; 69 | } 70 | -------------------------------------------------------------------------------- /modules/expose-local-http.nix: -------------------------------------------------------------------------------- 1 | { self, ... }: 2 | { 3 | 4 | flake.nixosModules.expose-local-http = 5 | { 6 | lib, 7 | pkgs, 8 | config, 9 | ... 10 | }: 11 | let 12 | cfg = config.services.caddy.expose-local-http; 13 | in 14 | { 15 | key = "nixos-config.modules.nixos.expose-local-http"; 16 | 17 | options.services.caddy.expose-local-http = { 18 | enable = lib.mkEnableOption "Allow exposing local services over let's encrypted https"; 19 | virtualHosts = lib.mkOption { 20 | type = lib.types.attrsOf lib.types.str; 21 | default = { }; 22 | }; 23 | }; 24 | 25 | config = lib.mkIf cfg.enable { 26 | sops.secrets.cloudflare-api-key = { 27 | sopsFile = "${config.lib.self.file' "secrets/webservers.yaml"}"; 28 | restartUnits = [ "caddy.service" ]; 29 | }; 30 | 31 | systemd.services.caddy.serviceConfig.AmbientCapabilities = "CAP_NET_ADMIN CAP_NET_BIND_SERVICE"; 32 | systemd.services.caddy.serviceConfig.LoadCredential = 33 | "cloudflare-api-token:${config.sops.secrets.cloudflare-api-key.path}"; 34 | 35 | services.caddy = { 36 | enable = true; 37 | enableReload = false; # fails to reload when new hosts are added 38 | package = self.packages."${pkgs.stdenv.hostPlatform.system}".caddy-with-cloudflare-dns; 39 | virtualHosts = 40 | with lib; 41 | flip mapAttrs cfg.virtualHosts ( 42 | _hostname: backend: { 43 | extraConfig = '' 44 | reverse_proxy ${backend} 45 | tls { 46 | dns cloudflare {file.{$CREDENTIALS_DIRECTORY}/cloudflare-api-token} 47 | resolvers 1.1.1.1 48 | } 49 | ''; 50 | } 51 | ); 52 | }; 53 | }; 54 | }; 55 | } 56 | -------------------------------------------------------------------------------- /modules/wezterm.nix: -------------------------------------------------------------------------------- 1 | { 2 | ... 3 | }: 4 | { 5 | flake.homeModules.wezterm = 6 | { 7 | lib, 8 | pkgs, 9 | config, 10 | osConfig, 11 | ... 12 | }: 13 | let 14 | propagatedConfig = pkgs.writeText "stylix-vars.lua" '' 15 | local vars = { 16 | fontName = ${lib.escapeShellArg (builtins.toString config.stylix.fonts.monospace.name)}, 17 | fontSize = ${builtins.toString config.stylix.fonts.sizes.terminal}, 18 | } 19 | return vars 20 | ''; 21 | in 22 | { 23 | key = "nixos-config.modules.home.wezterm"; 24 | 25 | config = lib.mkIf osConfig.services.graphical-desktop.enable { 26 | programs.wezterm = { 27 | enable = true; 28 | enableZshIntegration = true; 29 | }; 30 | 31 | # xdg.configFile."wezterm/wezterm.lua".source = lib.mkForce (config.lib.file.mkOutOfStoreSymlink "${config.home.homeDirectory}/personal-workspace/nixos-config/files/wezterm.lua"); 32 | xdg.configFile."wezterm/wezterm.lua".source = lib.mkForce (config.lib.self.file "wezterm.lua"); 33 | xdg.configFile."wezterm/stylix-vars.lua".source = propagatedConfig; 34 | 35 | # NOTE: Needed because default .desktop has some arguments that prevents (re)connection to an existing server 36 | xdg.dataFile."applications/org.wezfurlong.wezterm.desktop".source = 37 | pkgs.writeText "org.wezfurlong.wezterm.desktop" '' 38 | [Desktop Entry] 39 | Name=WezTerm 40 | Comment=Wez's Terminal Emulator 41 | Keywords=shell;prompt;command;commandline;cmd; 42 | Icon=org.wezfurlong.wezterm 43 | StartupWMClass=org.wezfurlong.wezterm 44 | TryExec=wezterm 45 | Exec=wezterm 46 | Type=Application 47 | Categories=System;TerminalEmulator;Utility; 48 | Terminal=false 49 | ''; 50 | }; 51 | }; 52 | } 53 | -------------------------------------------------------------------------------- /modules/monitored.nix: -------------------------------------------------------------------------------- 1 | { ... }: 2 | { 3 | flake.nixosModules.monitored = 4 | { config, lib, ... }: 5 | { 6 | key = "nixos-config.modules.nixos.monitored"; 7 | options.nixos-config.export-metrics = { 8 | enable = lib.mkOption { 9 | description = "Enabled sending metrics to centralized victoriametrics"; 10 | type = lib.types.bool; 11 | # no default, force the user to choose 12 | }; 13 | }; 14 | config = 15 | let 16 | cfg = config.nixos-config.export-metrics; 17 | in 18 | { 19 | services.prometheus.exporters.node = { 20 | enable = cfg.enable; 21 | port = 9100; 22 | extraFlags = [ 23 | "--collector.filesystem.fs-types-exclude='^(tmpfs|proc|sysfs|ramfs)'" 24 | ]; 25 | # enabledCollectors = [ 26 | # "logind" 27 | # "systemd" 28 | # ]; 29 | # disabledCollectors = [ "textfile" ]; 30 | # openFirewall = true; 31 | # firewallFilter = "-i br0 -p tcp -m tcp --dport 9100"; 32 | }; 33 | 34 | services.vmagent = { 35 | enable = cfg.enable; 36 | remoteWrite.url = "http://192.168.2.2:8428/api/v1/write"; 37 | # openFirewall = true; 38 | prometheusConfig = { 39 | global = { 40 | external_labels = { 41 | bhost = "${config.networking.hostName}"; 42 | }; 43 | }; 44 | scrape_configs = [ 45 | { 46 | job_name = "node"; 47 | scrape_interval = "10s"; 48 | static_configs = [ 49 | { 50 | targets = [ "127.0.0.1:9100" ]; 51 | } 52 | ]; 53 | } 54 | ]; 55 | }; 56 | }; 57 | }; 58 | }; 59 | } 60 | -------------------------------------------------------------------------------- /modules/public-keys.nix: -------------------------------------------------------------------------------- 1 | { 2 | self, 3 | ... 4 | }: 5 | { 6 | flake.modules.generic.public-keys = 7 | { 8 | lib, 9 | ... 10 | }: 11 | let 12 | allPublicKeys = import "${self}/inventory/public-keys.nix"; 13 | in 14 | { 15 | key = "nixos-config.modules.generic.public-keys"; 16 | 17 | config = { 18 | lib.publicKeys = { 19 | secureWithTag = 20 | tag: 21 | with lib; 22 | flip concatMap (attrValues allPublicKeys.ssh_keys) ( 23 | { 24 | public_key, 25 | secure ? false, 26 | tags ? [ ], 27 | ... 28 | }: 29 | if secure && (elem tag tags) then [ public_key ] else [ ] 30 | ); 31 | ssh.secureForUser = 32 | user: 33 | with lib; 34 | pipe allPublicKeys.ssh_keys [ 35 | (mapAttrsToList ( 36 | keyName: 37 | { 38 | public_key, 39 | secure ? false, 40 | tags ? [ ], 41 | description ? keyName, 42 | ... 43 | }: 44 | let 45 | matchTags = 46 | if user == "root" then 47 | [ "default" ] 48 | else 49 | [ 50 | "default" 51 | "user-${user}" 52 | ]; 53 | in 54 | if secure && (intersectLists matchTags tags != [ ]) then "${public_key} ${description}" else null 55 | )) 56 | (filter (x: x != null)) 57 | ]; 58 | }; 59 | }; 60 | }; 61 | 62 | flake.nixosModules.public-keys = 63 | { ... }: 64 | { 65 | key = "nixos-config.modules.nixos.public-keys"; 66 | 67 | imports = [ self.modules.generic.public-keys ]; 68 | }; 69 | } 70 | -------------------------------------------------------------------------------- /files/byte-compile.el: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env emacs --script 2 | ;; -*- mode: emacs-lisp; lexical-binding: t -*- 3 | 4 | (setf kill-emacs-hook nil) ;; some packages can populate this during load time with non-pure stuff, we have no reason to run it 5 | 6 | (let (src src-with-includes dst dst-early compilation-result dst-dir) 7 | (setf src (pop argv)) 8 | (setf dst-dir (pop argv)) 9 | 10 | (setf dst (expand-file-name "init.el" dst-dir)) 11 | (setf dst-early (expand-file-name "early-init.el" dst-dir)) 12 | 13 | (package-initialize) 14 | (require 'org) 15 | (require 'ob-tangle) 16 | (require 'ox-org) 17 | 18 | (unwind-protect 19 | (cl-letf (((symbol-function 'org-babel-effective-tangled-filename) 20 | (lambda (buffer-fn src-lang src-tfile) 21 | (ignore buffer-fn src-lang) 22 | (if (string-equal "early-init.el" src-tfile) 23 | dst-early 24 | dst)))) 25 | (setf src-with-includes (make-temp-file "emacs-config" nil ".org")) 26 | (with-current-buffer (find-file-noselect src) 27 | (org-export-to-file 'org src-with-includes)) 28 | (org-babel-tangle-file src-with-includes nil "emacs-lisp")) 29 | (when src-with-includes ;; (delete-file src-with-includes) 30 | (message "%s" src-with-includes) 31 | )) 32 | 33 | (setq make-backup-files nil) ;; we change buffers below, inhibit backups 34 | 35 | (dolist (el-name (list dst dst-early)) 36 | (setq ) 37 | (let ((buf (find-file-noselect el-name t))) 38 | (with-current-buffer buf 39 | (beginning-of-buffer) 40 | (insert ";;; -*- lexical-binding: t -*-\n") 41 | (save-buffer)) 42 | (kill-buffer buf))) 43 | 44 | (setf byte-compile-error-on-warn t) 45 | (setf compilation-result (and 46 | (byte-compile-file dst) 47 | (byte-compile-file dst-early))) 48 | 49 | (unless compilation-result 50 | (kill-emacs 1))) 51 | -------------------------------------------------------------------------------- /ansible/playbooks/pve/files/bael-sanoid.conf: -------------------------------------------------------------------------------- 1 | [bpool/BOOT/debian] 2 | use_template = host 3 | 4 | [rpool/ROOT/debian] 5 | use_template = host 6 | 7 | [rpool/home] 8 | use_template = host 9 | 10 | [rpool/home/root] 11 | use_template = host 12 | 13 | [rpool/var] 14 | use_template = host 15 | 16 | [rpool/var/lib] 17 | use_template = host 18 | 19 | [rpool/var/spool] 20 | use_template = host 21 | 22 | # [spinning-zfs/pve-data/subvol-111-disk-0] 23 | # # /media/movies 24 | # use_template = media 25 | 26 | # [spinning-zfs/pve-data/subvol-111-disk-1] 27 | # # /media/music 28 | # use_template = media 29 | 30 | # [spinning-zfs/pve-data/subvol-111-disk-2] 31 | # # /media/torrents/regular 32 | # use_template = media 33 | 34 | # # [spinning-zfs/pve-data/subvol-111-disk-3] 35 | # # # /media/torrents/incomplete 36 | 37 | # [spinning-zfs/pve-data/subvol-111-disk-4] 38 | # # /media/torrents/rare 39 | # use_template = media 40 | 41 | # [spinning-zfs/pve-data/subvol-111-disk-5] 42 | # # /media/tubearchivist 43 | # use_template = media 44 | 45 | # [spinning-zfs/pve-data/subvol-111-disk-6] 46 | # # /media/annex 47 | # use_template = media 48 | 49 | [spinning-zfs/raum-oob-backups/spinning-zfs] 50 | use_template = media 51 | autosnap = false 52 | recursive = true 53 | process_children_only = true 54 | 55 | [spinning-zfs/raum-oob-backups/rpool] 56 | use_template = host 57 | autosnap = false 58 | recursive = true 59 | process_children_only = true 60 | 61 | [spinning-zfs/raum-oob-backups/bpool] 62 | use_template = host 63 | autosnap = false 64 | recursive = yes 65 | process_children_only = true 66 | 67 | [template_media] 68 | daily = 30 69 | weekly = 8 70 | monthly = 4 71 | autosnap = yes 72 | autoprune = yes 73 | 74 | [template_host] 75 | frequent_period = 15 76 | frequently = 8 77 | hourly = 36 78 | daily = 30 79 | monthly = 11 80 | yearly = 2 81 | autosnap = yes 82 | autoprune = yes 83 | -------------------------------------------------------------------------------- /ansible/playbooks/pve/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: 3 | - pbs 4 | - pve 5 | tags: 6 | - etckeeper 7 | tasks: 8 | - import_tasks: tasks/etckeeper.yml 9 | 10 | - hosts: qdevices 11 | tags: 12 | - sshd 13 | vars_files: 14 | - ../../ssh-public-keys.yaml 15 | vars: 16 | sshd_root_principals: 17 | - qdevice 18 | roles: 19 | - sshd 20 | tasks: 21 | - name: Install corosync-qnetd 22 | ansible.builtin.apt: 23 | name: 24 | - corosync-qnetd 25 | 26 | - hosts: pve 27 | tags: 28 | - sshd 29 | vars_files: 30 | - ../../ssh-public-keys.yaml 31 | vars: 32 | sshd_root_principals: 33 | - pve 34 | roles: 35 | - sshd 36 | tasks: 37 | - import_tasks: tasks/tpm2-ssh.yml 38 | 39 | - hosts: pve 40 | tags: 41 | - monitoring 42 | vars_files: 43 | - vars/main.yml 44 | 45 | roles: 46 | - victoriametrics.cluster.vmagent 47 | - prometheus.prometheus.node_exporter 48 | - aroberts.zfs_exporter 49 | 50 | - hosts: pve 51 | vars_files: 52 | - vars/main.yml 53 | tasks: 54 | - import_tasks: tasks/no-nag.yml 55 | - import_tasks: tasks/empty-systctl-conf.yml 56 | - import_tasks: tasks/tailscale-docker.yml 57 | - import_tasks: tasks/sanoid.yml 58 | - name: Install corosync-qdevice 59 | ansible.builtin.apt: 60 | name: corosync-qdevice 61 | 62 | - hosts: pve 63 | tags: 64 | - hardware 65 | tasks: 66 | - include_tasks: tasks/debian-non-free-firmware.yml 67 | - name: Install amd firmware 68 | when: "'AuthenticAMD' in ansible_facts['processor']" 69 | ansible.builtin.apt: 70 | name: amd64-microcode 71 | - name: Install intel firmware 72 | when: "'GenuineIntel' in ansible_facts['processor']" 73 | ansible.builtin.apt: 74 | name: intel-microcode 75 | 76 | - hosts: pve_at_home 77 | vars_files: 78 | - ../../ip-allocation.yaml 79 | tasks: 80 | - import_tasks: tasks/tailscale-hosts.yml 81 | 82 | - hosts: bael 83 | tasks: 84 | - import_tasks: tasks/backup-raum-to-bael.yml 85 | -------------------------------------------------------------------------------- /files/dashboard-icons/sipeed.png.base64: -------------------------------------------------------------------------------- 1 | iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAAIGNIUk0AAHomAACAhAAA+gAAAIDo 2 | AAB1MAAA6mAAADqYAAAXcJy6UTwAAAGhUExURQAAAN0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0m 3 | Kd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0m 4 | Kd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0m 5 | Kd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0m 6 | Kd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0m 7 | Kd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0m 8 | Kd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0m 9 | Kd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKd0mKf///xMz0P4AAACJdFJO 10 | UwAABEOl3/EKe+oDfPrp8PP59qhUOU2a93cIYO+uGkcikt5wb268/l8Xu/vNJcUwevhGOvxTLuzE 11 | KYPOJkTDKAKtG0iUHtlicf2nOJs2NcsVW3N9oNcQq0L1yE/ygGct5WHQ4e4BFB8gM4hofg8Oyr4F 12 | duQvnduTx6OVhg2kPmttDJGQ0o6zyWa300bDtQAAAAFiS0dEioVod3YAAAAHdElNRQfpARMKAx3H 13 | 74vnAAACbElEQVRYw6XX+VfTQBAH8J0mTWiAhQpV8KIIVK0oaIonGqGKoAKeiCgeUG+ieB94oOKx 14 | /7V9bXols3GzOz/1vbzvp7uTNNMlBGKaHjckKq5rMSAEzKYEk6xEkwnEam6RzTPW0mwRrVU+z1ir 15 | RnSVPGM6iasBcUJrG2prjw5QYnifkps6OlObaVTAqABburoBILZ1myyw3YQdO3vASteu9aZ39fmr 16 | f4ADZHbDnr3ZfYOgJSuX9h+A4nPmKxjiAMMH4VAbs3Mwcti7cuQolodjISsYsI+fgFFvBSdPIXkC 17 | js4B2GkTxsaLPciH5QmcOfufuzBBQ/NwbpIHsORU7nzqghGevxh4bmtA3ZPIzU/PsDCgUmgewBnr 18 | 72UiQCkPMHvpckNduXoty0SA8vfD9blJJlIBwFs/3JgXygeAm97+YSErBbR3eP2TBW51ev2XBdht 19 | R20FjC5a5R6kBF/2gbvgCXDnriTgCeDcu79Uq+WMOFARCg8eVuvR4ydUHKj2of6XYD2l4gAuLFJx 20 | IIqAAxEEDiAu8ABhgQuICnyA0Wc9K261zAKgQgjA7KWZ59V6MbGKCmGAb0GjgO1CHGAvAetDBGDI 21 | e1M4XbYSQGDwlSLwOqG2BfeNZA/6yk1087Yc8HakdBvdd41vWx5gvP/wMV9fn6ZLE2/Nl+cAyc9f 22 | 3AI2otfS/rc9Cix/7cb/IATzKDD8zcHiBNa/B6cNBsyZnPwPZFohwPw4vn40jwE/Z/H8BjotEeDX 23 | KojnMWAq2EKAlQ3OsEWA37mFQP35y5v2CJDJBos7W4tA5DNKY1H1Q5fysU/54Kl89FU+fKse//8B 24 | ltE9mxtHwjAAAAAldEVYdGRhdGU6Y3JlYXRlADIwMjUtMDEtMTlUMTA6MDM6MTkrMDA6MDDlIMvP 25 | AAAAJXRFWHRkYXRlOm1vZGlmeQAyMDI1LTAxLTE5VDEwOjAxOjM4KzAwOjAwdNqvhwAAACh0RVh0 26 | ZGF0ZTp0aW1lc3RhbXAAMjAyNS0wMS0xOVQxMDowMzoyOSswMDowME3nVU8AAAAASUVORK5CYII= 27 | -------------------------------------------------------------------------------- /scripts/nixos-anywhere.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | set -x 4 | if [[ -z $1 ]]; then 5 | echo "Usage: $0 hostname" 6 | exit 1 7 | fi 8 | 9 | host=$1 10 | 11 | luks_pw_file=/tmp/deploy-luks-password-without-newline 12 | 13 | if [[ ! -f $luks_pw_file ]]; then 14 | read -s -p "LUKS password: " luks_pw 15 | echo -n "$luks_pw" > "$luks_pw_file" 16 | fi 17 | 18 | # Create a temporary directory 19 | temp=$(mktemp -d) 20 | 21 | # Function to cleanup temporary directory on exit 22 | cleanup() { 23 | rm -rf "$temp" 24 | } 25 | #trap cleanup EXIT 26 | 27 | while read -r line; do 28 | file=$(basename $line) 29 | source=secrets/$host/$file 30 | 31 | target_dir=$temp/$(dirname $line) 32 | target=$temp/$line 33 | install -d -m755 "$target_dir" 34 | sops decrypt "$source" > "$target" 35 | chmod 0600 "$target" 36 | done < <(nix eval "$(pwd)#nixosConfigurations.furfur.config.services.openssh.hostKeys" --json | jq '.[]|.path' -r) 37 | 38 | binarin_age=$(nix eval "$(pwd)#nixosConfigurations.furfur.config.home-manager.users.binarin.sops.age.keyFile" --json | jq . -r) 39 | binarin_age_dir=$(dirname $binarin_age) 40 | 41 | install -d -m700 "$temp/persist/home/binarin" 42 | install -d -m700 "$temp/local/home/binarin" 43 | 44 | install -d -m700 "$temp/$binarin_age_dir" 45 | sops decrypt "secrets/$host/user-binarin-age" > "$temp/$binarin_age" 46 | chmod 0600 "$temp/$binarin_age" 47 | 48 | binarin_uid=$(nix eval "$(pwd)#nixosConfigurations.furfur.config.users.users.binarin.uid") 49 | binarin_gid=$(nix eval "$(pwd)#nixosConfigurations.furfur.config.users.groups.binarin.gid") 50 | 51 | nix \ 52 | --builders 'ssh://nix-cache x86_64-linux /home/binarin/.ssh/id_ed25519 16 1 big-parallel' --max-jobs 0 \ 53 | run github:nix-community/nixos-anywhere -- \ 54 | --generate-hardware-config nixos-generate-config "machines/$host/hardware-configuration.nix" \ 55 | --flake "$(pwd)#$host" \ 56 | --target-host root@iso \ 57 | --disk-encryption-keys "$luks_pw_file" "$luks_pw_file" \ 58 | --extra-files "$temp" \ 59 | --chown "/persist/home/binarin" "$binarin_uid:$binarin_gid" \ 60 | --chown "/local/home/binarin" "$binarin_uid:$binarin_gid" 61 | -------------------------------------------------------------------------------- /scripts/check-module-keys.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | 4 | # Check that every module in modules/ that defines nixosModules or homeModules has a proper key 5 | 6 | MODULES_DIR="modules" 7 | EXIT_CODE=0 8 | declare -a ERRORS 9 | 10 | while IFS= read -r file; do 11 | # Find all module definitions in the file 12 | # Look for: flake.nixosModules.foo, flake.homeModules.bar, flake.modules.generic.baz 13 | 14 | # Extract nixosModules 15 | while IFS= read -r module_name; do 16 | expected_key="nixos-config.modules.nixos.$module_name" 17 | if ! grep -q "key = \"$expected_key\"" "$file"; then 18 | ERRORS+=("$file: flake.nixosModules.$module_name missing key = \"$expected_key\"") 19 | EXIT_CODE=1 20 | fi 21 | done < <(grep -oP 'flake\.nixosModules\.\K[a-zA-Z0-9_-]+' "$file" | sort -u) 22 | 23 | # Extract homeModules 24 | while IFS= read -r module_name; do 25 | expected_key="nixos-config.modules.home.$module_name" 26 | if ! grep -q "key = \"$expected_key\"" "$file"; then 27 | ERRORS+=("$file: flake.homeModules.$module_name missing key = \"$expected_key\"") 28 | EXIT_CODE=1 29 | fi 30 | done < <(grep -oP 'flake\.homeModules\.\K[a-zA-Z0-9_-]+' "$file" | sort -u) 31 | 32 | # Extract generic modules 33 | while IFS= read -r module_name; do 34 | expected_key="nixos-config.modules.generic.$module_name" 35 | if ! grep -q "key = \"$expected_key\"" "$file"; then 36 | ERRORS+=("$file: flake.modules.generic.$module_name missing key = \"$expected_key\"") 37 | EXIT_CODE=1 38 | fi 39 | done < <(grep -oP 'flake\.modules\.generic\.\K[a-zA-Z0-9_-]+' "$file" | sort -u) 40 | 41 | done < <(find "$MODULES_DIR" -name "*.nix" -type f) 42 | 43 | if [ $EXIT_CODE -ne 0 ]; then 44 | echo "ERROR: The following modules are missing proper 'key' attributes:" >&2 45 | printf ' - %s\n' "${ERRORS[@]}" >&2 46 | echo >&2 47 | echo "According to CLAUDE.md, having 'key' in every module is of utmost importance," >&2 48 | echo "as it's used for deduplication by the module system." >&2 49 | exit 1 50 | else 51 | echo "✓ All modules have proper key attributes" 52 | fi 53 | -------------------------------------------------------------------------------- /modules/programs/git.nix: -------------------------------------------------------------------------------- 1 | { 2 | ... 3 | }: 4 | { 5 | flake.homeModules.git = 6 | { 7 | pkgs, 8 | config, 9 | osConfig, 10 | ... 11 | }: 12 | { 13 | key = "nixos-config.modules.home.git"; 14 | 15 | config = { 16 | home.shellAliases = { 17 | g = "git"; 18 | }; 19 | 20 | programs.delta.enable = true; 21 | programs.delta.enableGitIntegration = true; 22 | 23 | programs.git = { 24 | enable = true; 25 | package = if osConfig.services.graphical-desktop.enable then pkgs.gitFull else pkgs.git; 26 | settings = { 27 | column.ui = "auto"; 28 | branch.sort = "-committerdate"; 29 | tag.sort = "version:refname"; 30 | diff.algorithm = "histogram"; 31 | diff.mnemonicPrefix = true; 32 | 33 | fetch.prune = true; 34 | fetch.pruneTags = true; 35 | fetch.all = true; 36 | help.autocorrect = "prompt"; 37 | 38 | core = { 39 | autocrlf = false; 40 | }; 41 | url = { 42 | "git@github.com:binarin/" = { 43 | insteadOf = "gh:"; 44 | pushInsteadOf = "gh:"; 45 | }; 46 | "git@forgejo.lynx-lizard.ts.net:binarin/" = { 47 | insteadOf = "fj:"; 48 | pushInsteadOf = "fj:"; 49 | }; 50 | }; 51 | commit.verbose = true; 52 | commit.template = "${config.lib.self.file "git-commit-template.txt"}"; 53 | rerere.enabled = true; 54 | rerere.autoupdate = true; 55 | merge.conflictstyle = "zdiff3"; 56 | 57 | "delta \"decorations\"" = { 58 | commit-decoration-style = "bold yellow box ul"; 59 | file-style = "bold yellow ul"; 60 | file-decoration-style = "none"; 61 | }; 62 | init = { 63 | defaultBranch = "master"; 64 | }; 65 | }; 66 | }; 67 | }; 68 | }; 69 | 70 | flake.nixosModules.git = 71 | { ... }: 72 | { 73 | key = "nixos-config.modules.nixos.git"; 74 | programs.git.enable = true; 75 | }; 76 | 77 | } 78 | -------------------------------------------------------------------------------- /modules/inputs-old.nix: -------------------------------------------------------------------------------- 1 | { inputs, lib, ... }: 2 | { 3 | imports = [ 4 | inputs.flake-file.flakeModules.dendritic 5 | ]; 6 | 7 | systems = [ "x86_64-linux" ]; 8 | 9 | flake-file.outputs = lib.mkForce '' 10 | inputs: inputs.flake-parts.lib.mkFlake { inherit inputs; } (inputs.import-tree ./modules) 11 | ''; 12 | 13 | flake-file.inputs = { 14 | flake-file.url = lib.mkForce "github:binarin/flake-file"; 15 | 16 | # nixpkgs.url = "path:/home/binarin/personal-workspace/nixpkgs"; 17 | nixpkgs.url = "github:nixos/nixpkgs/nixos-25.11"; 18 | nixpkgs-unstable.url = "github:nixos/nixpkgs"; 19 | 20 | import-tree.url = "github:vic/import-tree"; 21 | 22 | disko.url = "github:nix-community/disko"; 23 | disko.inputs.nixpkgs.follows = "nixpkgs-unstable"; 24 | 25 | emacs-overlay.url = "emacs-overlay"; 26 | emacs-overlay.inputs.nixpkgs.follows = "nixpkgs"; 27 | emacs-overlay.inputs.nixpkgs-stable.follows = "nixpkgs"; 28 | 29 | hyprland = { 30 | url = "https://github.com/hyprwm/Hyprland"; 31 | ref = "refs/tags/v0.51.1"; 32 | type = "git"; 33 | submodules = true; 34 | inputs.nixpkgs.follows = "nixpkgs-unstable"; 35 | inputs.pre-commit-hooks.follows = "pre-commit-hooks"; 36 | }; 37 | 38 | pre-commit-hooks.url = "github:cachix/git-hooks.nix"; 39 | 40 | waybar.url = "github:Alexays/Waybar"; 41 | waybar.inputs.nixpkgs.follows = "nixpkgs-unstable"; 42 | 43 | hyprland-contrib.url = "github:hyprwm/contrib"; 44 | hyprland-contrib.inputs.nixpkgs.follows = "nixpkgs"; 45 | 46 | nixos-wsl.url = "github:nix-community/NixOS-WSL/main"; 47 | nixos-wsl.inputs.nixpkgs.follows = "nixpkgs"; 48 | 49 | arion.url = "github:hercules-ci/arion"; 50 | arion.inputs.nixpkgs.follows = "nixpkgs"; 51 | 52 | sops-nix.url = "github:Mic92/sops-nix"; 53 | sops-nix.inputs.nixpkgs.follows = "nixpkgs"; 54 | 55 | flake-parts.url = "github:hercules-ci/flake-parts"; 56 | flake-parts.inputs.nixpkgs-lib.follows = "nixpkgs"; 57 | 58 | nix-index-database.url = "github:nix-community/nix-index-database"; 59 | nix-index-database.inputs.nixpkgs.follows = "nixpkgs"; 60 | 61 | deploy-rs.url = "github:serokell/deploy-rs"; 62 | deploy-rs.inputs.nixpkgs.follows = "nixpkgs"; 63 | }; 64 | } 65 | -------------------------------------------------------------------------------- /terraform/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | libvirt = { 4 | source = "dmacvicar/libvirt" 5 | version = "~> 0.7" 6 | } 7 | } 8 | } 9 | 10 | provider "libvirt" { 11 | uri = var.libvirt_uri 12 | } 13 | 14 | resource "libvirt_pool" "default" { 15 | name = "default" 16 | type = "dir" 17 | target { 18 | path = "/var/lib/libvirt/images" 19 | } 20 | } 21 | 22 | resource "libvirt_network" "br0" { 23 | name = "br0" 24 | mode = "bridge" 25 | bridge = "br0" 26 | autostart = true 27 | } 28 | 29 | resource "libvirt_volume" "debian_base" { 30 | name = "debian-13-genericcloud-amd64.qcow2" 31 | pool = libvirt_pool.default.name 32 | source = "https://cloud.debian.org/images/cloud/trixie/latest/debian-13-genericcloud-amd64.qcow2" 33 | format = "qcow2" 34 | } 35 | 36 | resource "libvirt_volume" "debian_vm" { 37 | name = var.vm_name 38 | base_volume_id = libvirt_volume.debian_base.id 39 | pool = libvirt_pool.default.name 40 | size = var.disk_size 41 | } 42 | 43 | resource "libvirt_cloudinit_disk" "commoninit" { 44 | name = "${var.vm_name}-commoninit.iso" 45 | pool = libvirt_pool.default.name 46 | 47 | user_data = templatefile("${path.module}/cloud-init.cfg", { 48 | ssh_authorized_keys = compact(split("\n", file("/etc/ssh/authorized_keys.d/binarin"))) 49 | }) 50 | 51 | network_config = templatefile("${path.module}/network-config.cfg", { 52 | static_ip = var.static_ip 53 | gateway = var.gateway 54 | dns_servers = join(",", var.dns_servers) 55 | }) 56 | } 57 | 58 | resource "libvirt_domain" "debian_vm" { 59 | name = var.vm_name 60 | memory = var.memory 61 | vcpu = var.vcpu 62 | autostart = true 63 | 64 | cloudinit = libvirt_cloudinit_disk.commoninit.id 65 | 66 | network_interface { 67 | network_name = var.network_name 68 | wait_for_lease = true 69 | addresses = [var.static_ip] 70 | } 71 | 72 | disk { 73 | volume_id = libvirt_volume.debian_vm.id 74 | } 75 | 76 | console { 77 | type = "pty" 78 | target_type = "serial" 79 | target_port = "0" 80 | } 81 | 82 | graphics { 83 | type = "spice" 84 | listen_type = "address" 85 | autoport = true 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /secrets/furfur/user-binarin.yaml: -------------------------------------------------------------------------------- 1 | syncthing-ui-password: ENC[AES256_GCM,data:nrcD+Y+KMXAiIQDYAcUjrwq3/2nYTdD2vXJEUQC4kmrv,iv:fRsdW4IMglfxtWprYbR2aX+d8Ih7qD8K2yaUmiZNJms=,tag:AxCHuAKar8hQPafx+E5YTA==,type:str] 2 | sops: 3 | age: 4 | - recipient: age1dvu92xmh3w23ghey4l0epcs27m89f3nswzs5twgnel36xxu42f4sfsv023 5 | enc: | 6 | -----BEGIN AGE ENCRYPTED FILE----- 7 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBMaXFKRVZ3MFlyTlBVekVn 8 | WUNTMytxOWJwL1AyREVoZG1xaWNWZ21aeEdVCk85UnNleXBIQlpYdGZiYkc5RnNP 9 | SFhld0RKMGJlZ1VTOGNnME1NN3pBM28KLS0tIHZRWkt3a3V4NlIwc0F5R2h3QzVp 10 | V2tUZDhwNUFhbjBiUTVudmdtN3hWY2MKW/desZM4fHljb6dcvC8tgnZNjzMuA1aK 11 | 4ySR4xg8bDirIsS6Ad22s9NQ6gat0MtCezipBvNLlM3woiBa+ZxSMg== 12 | -----END AGE ENCRYPTED FILE----- 13 | - recipient: age1nvy6629y23y7c38f9qk8qekqn089a5x55nklp2t3hdzl7la7xgys5rcfsy 14 | enc: | 15 | -----BEGIN AGE ENCRYPTED FILE----- 16 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAzNWszOFRQbVBQUHVTRis1 17 | VFNIZXhoWWRsYVBkMnR1SGlicXl6SW5Na2hNCk82S0NTQk9QbG15TFVySi95NGdE 18 | NnJudFFML2NGWUtPazdFbUFkcFI1T3MKLS0tIGx6WE0rZVFJaHM3L0ozZEJoSHNM 19 | MjFCRUJzdm9tMC95TmZBOW0wMExBQTAKUUPnO4xxLqL01f9BRTdE3rqWrtaNj26F 20 | AwPeZ5WWo+pg+A6hEfyYrbnKplsY9ecQqEeseE49XCeTdd+GrL2f0g== 21 | -----END AGE ENCRYPTED FILE----- 22 | lastmodified: "2025-11-30T12:38:19Z" 23 | mac: ENC[AES256_GCM,data:jyjJ5PPsk4/82SNm7rPwPV7H93XR8krJqhRPZVLUiVJjGxtk23pNW6Xp6gNkTQBHbGKkoVjvFaRX0e1EJbFDkDi1G9b0jvJwxxyA3CsLIDvvfQ8u2r6J3vYUI8t1MJ4urxcrLjM2MfML1WKQiEP0aWK+8Cvx8IPPs5OBUHS8ZyI=,iv:HfT1YPj/x8i/qi0ktct+VfNuf7BsKJEf16jQmN6pdSw=,tag:HucBXCwjm/vWyceh4dqqWA==,type:str] 24 | pgp: 25 | - created_at: "2025-12-01T13:09:32Z" 26 | enc: |- 27 | -----BEGIN PGP MESSAGE----- 28 | 29 | hF4DjUagJu2XQwUSAQdA6I2+y54I/xOKORNjG3/ZCYX1gKp7w33fo6EowPjDBlow 30 | Gz8ee8Le4BbUT50/+15IAeZHKxHoWOl91A8ux3IrUvkna03YWiKAgBvTo6UO8O1/ 31 | 0lwBIc1cyEQxCXE2i9o04e9LywV6z3uRFCVHFtg4vc9hVZWsvnse9ZSVXj0LZ/3p 32 | suVZQa37+le6yU+KnSOOMVTaF9wrW5iHzsbJHILLqdhXeh8DZfbxGgexVNRlLg== 33 | =K10T 34 | -----END PGP MESSAGE----- 35 | fp: 7668F12EF30BE2A802E1E13E6D4365C3B5E703F5 36 | unencrypted_suffix: _unencrypted 37 | version: 3.11.0 38 | -------------------------------------------------------------------------------- /secrets/mail/dovecot-passwd.bin: -------------------------------------------------------------------------------- 1 | { 2 | "data": "ENC[AES256_GCM,data:DOErDojriIkUyyfj6U8HQpBeB1mWoUBolRLn1gHD7/7xiF2ExBT3c6bJf58kuih/aq07Mw8v+cVf6jM6CtNYJdr4yEbVUSfm4Kl8T2io9nrZ/IfjeMDEiWrUYbE2CrxFNot+xpk5GWBGjpoht/dj5AExWk8C+fDzjrgQhtfDDpPk6dCZDICF,iv:F5+zxlP5TcIQYjNut4LhGipLHCDINRX2EMTShmgCQkM=,tag:aw51KS1H6CpmuoJ3jQNUTA==,type:str]", 3 | "sops": { 4 | "kms": null, 5 | "gcp_kms": null, 6 | "azure_kv": null, 7 | "hc_vault": null, 8 | "age": [ 9 | { 10 | "recipient": "age1ec9gz6f685mn8pdrz48qm4vq7qzpzcz48vgkcty9aa665evra9lq2uz3s2", 11 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBxdlJlaGh6N2xQT3F0SitS\nTVFwZnZINE5KTXE2Y3NwS3Bsd0NjZDFReWxBCkFFWFlDd1lRaWtNNmRSTmMyQ2Fy\nUE5zYVNraDd0Zy9sWXBQcGJDd3pLalEKLS0tIHhqQnEwL1Y5MlJHSm91RDgyYmdD\nRThENnp1OXNYTElNeFFZcUJCWWVIbVUKx27Hjtr/InwnD78b8cIMH3KKfdJk++N0\nrE144Dhwu8x9zX5JuUFtB0Vs1ng2nuU69XmVPP16KLFK8zBfUkf75w==\n-----END AGE ENCRYPTED FILE-----\n" 12 | }, 13 | { 14 | "recipient": "age1dvu92xmh3w23ghey4l0epcs27m89f3nswzs5twgnel36xxu42f4sfsv023", 15 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBMWkRWRGNXeXFTYUJlV1cv\ncithUWw3YitQaTh1NklFWUJKR2EvQ0VCelc0CmVQQnhiaTlKNFZwTDBiM0dsZHk3\nN2didkJVWHdRNXl2bHc0bzZUUzdQbkkKLS0tIHJ6U1JBWE1XekRWcDQ0VjFMeE5v\nSjBkRDBtbHFLUktMMm8zYlM4Z0g1M1UKk4vyKnI0BDb3IfegNLl6F09ES7h5/JF0\nL3erqshLSFQD9IQkYobV3eUq0PsHj+TvL908UUKDLZSTO3i7e9mkBA==\n-----END AGE ENCRYPTED FILE-----\n" 16 | }, 17 | { 18 | "recipient": "age14n05u2z9u9h7chvn2rmmwr9flc25790rapxtvx97lqt78uk3ce3sy3xc6s", 19 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBUUHlKd0JMQmltZlpnbXdp\nYzhSU2R0TUg5VnJGSENnckZ2T3UveTVJdlhVCmZIdmM2eWI2a1FHU1JxNWFpYnRC\ndUlCT3NkRjlmQldicmJ5S0I2YW51ZW8KLS0tIDVJY25SZDVqNTFBNlJvT3c0cDhi\ncmYvaXdZRll5V1lyS0NzUlFxQWw1aG8K9OEVoJR141TXgEzTnX30DfIZ1AVjMd9b\nr2jAjYDztSNeoImOpflQiY/TF1W7lOolHiWn0xHIS4HdvBwQcZXsYw==\n-----END AGE ENCRYPTED FILE-----\n" 20 | } 21 | ], 22 | "lastmodified": "2024-12-15T17:12:32Z", 23 | "mac": "ENC[AES256_GCM,data:1M5jV5Z8gu/b6Pn7lmvlK6pRDtf3/mYBzL6oJjfsSwkz9Ni62wIJS0VNY+qCkWYzOK4gy3hjDmFsPg/bSGdg8xT/dG+yzhwt/QZrpe2r59Xnlrdu5s2POswCdTsncab3JLzDIzWJH1shnUbHcIhutJZlnGaIHUuTglYHJsjRqTo=,iv:dG6oe5cy9DCI/6JalQ6uAe2y2dHFTjZ+N1k+2P2gJo0=,tag:XjZ+yRbiazj0WF8Xn9n6Tg==,type:str]", 24 | "pgp": null, 25 | "unencrypted_suffix": "_unencrypted", 26 | "version": "3.9.1" 27 | } 28 | } -------------------------------------------------------------------------------- /modules/niri/default.nix: -------------------------------------------------------------------------------- 1 | { self, ... }: 2 | { 3 | flake.nixosModules.niri = 4 | { 5 | pkgs, 6 | lib, 7 | config, 8 | ... 9 | }: 10 | let 11 | # wrapper script for `binPath` since the option type is `path` 12 | niriSession = lib.getExe ( 13 | pkgs.writeShellScriptBin "niriSession" '' 14 | ${lib.getExe config.programs.niri.package} --session 15 | '' 16 | ); 17 | in 18 | { 19 | key = "nixos-config.modules.nixos.niri"; 20 | 21 | imports = [ 22 | self.nixosModules.gui 23 | self.nixosModules.wayland 24 | self.nixosModules.swayidle 25 | ]; 26 | 27 | environment.systemPackages = with pkgs; [ 28 | # Things used by the default config 29 | alacritty 30 | fuzzel 31 | 32 | # niri binary itself, for RPC calls 33 | niri 34 | 35 | # automatically started if installed 36 | xwayland-satellite 37 | ]; 38 | 39 | programs.uwsm = { 40 | enable = true; 41 | waylandCompositors = { 42 | niri = { 43 | prettyName = "niri"; 44 | comment = "niri compositor managed by UWSM"; 45 | binPath = niriSession; 46 | }; 47 | }; 48 | }; 49 | }; 50 | 51 | flake.homeModules.niri = 52 | { 53 | config, 54 | pkgs, 55 | ... 56 | }: 57 | { 58 | key = "nixos-config.modules.home.niri"; 59 | 60 | imports = [ 61 | self.homeModules.wayland 62 | self.homeModules.fuzzel 63 | self.homeModules.waybar 64 | self.homeModules.swaync 65 | self.homeModules.wl-kbptr 66 | self.homeModules.swayidle 67 | ]; 68 | 69 | config = { 70 | home.packages = [ 71 | self.packages."${pkgs.stdenv.hostPlatform.system}".sshmenu 72 | ]; 73 | 74 | xdg.configFile."niri/config.kdl".source = 75 | config.lib.file.mkOutOfStoreSymlink "${config.home.homeDirectory}/personal-workspace/nixos-config/modules/niri/config.kdl"; 76 | 77 | # XXX 78 | # home.activation.niri-layout-binds = lib.hm.dag.entryAfter ["writeBoundary"] '' 79 | # run touch ~/.config/niri/layout-binds.kdl 80 | # ''; 81 | 82 | xdg.portal.extraPortals = [ 83 | pkgs.kdePackages.kwallet 84 | pkgs.kdePackages.xdg-desktop-portal-kde 85 | ]; 86 | 87 | }; 88 | }; 89 | } 90 | -------------------------------------------------------------------------------- /secrets/ishamael/secrets.yaml: -------------------------------------------------------------------------------- 1 | tailscale-auth: ENC[AES256_GCM,data:awnoG1JU34ftiGICmKvgPYYjJwDPVWPfGmCzyMQNVj2Dvk8p9xemOI2FIpZ4jkd1lNFhBqM7SggEiX7fP8I=,iv:mLY7MS8FjOSdfAYiXJh/DCGBs9U3n1UIbf9FLlI/I/8=,tag:8WfI9Ft93lpcXCER4elwhw==,type:str] 2 | sops: 3 | age: 4 | - recipient: age1ec9gz6f685mn8pdrz48qm4vq7qzpzcz48vgkcty9aa665evra9lq2uz3s2 5 | enc: | 6 | -----BEGIN AGE ENCRYPTED FILE----- 7 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBKRGczZ0piVXBRdjRkYUxM 8 | QXNaRmVnVkZQcks5citQVlNpUWNXRThIQ2prCkFnUkUrUmxYOGhCeTRJK1NZakxa 9 | a0R4ZUxBUU1mc1hIL3F1MHNTRnNPNFEKLS0tIEhVVklQaGJ6Z0UwNElxT1llUnJm 10 | cjdoTnhTcG9GZGdCNit0M1hsemtUc2MKeoo6XfBTXKk/wIu0HUjDh628lhrmyYRV 11 | xzjFHwP4Jap4mGnBH1GJuSdlE/0fySzBF20gmbs89BgnRqamSlU/fw== 12 | -----END AGE ENCRYPTED FILE----- 13 | - recipient: age1qk6g50qsla4mn9qm8zc9nhf0vwz37wyvlhajann8ct9shkxg0g7s5yuqn5 14 | enc: | 15 | -----BEGIN AGE ENCRYPTED FILE----- 16 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBaOTlpTmVySjBlYy9aWlVa 17 | WHg4UnF5M25ob1RXRDk2MnZ2RkZSUHZ2bEMwCk43SmVlQjEzeklFbVRDd2ZCd3Jn 18 | UThyejl5cHY1cURtQUkwTE5tOGRxRUEKLS0tIG1XZGExYUZpN2JscUJiOThQczJF 19 | bnJqaVZPTzNsU29MVTRGVVRURS9ETjAKkfNmso+hiGsF2rPHl9Y5PgkonsLM/oZh 20 | A4yg7fAcDt9BxDMzf62u2xgBvDuk6kHcg93KYuDLiq7IIy+KIn6p8Q== 21 | -----END AGE ENCRYPTED FILE----- 22 | - recipient: age1qud3t3zq28d0p3z42gj0vr5yamqcgcd4sh0uvfdg2g24qwkknqds4356qd 23 | enc: | 24 | -----BEGIN AGE ENCRYPTED FILE----- 25 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBzdk0yL055MEIvUUJDQVl4 26 | QVF6TFIybEtWYVAySXlQSmw5UzRFazROY2d3CmJtSWZnWGJKY1lhRkEvK1RCdHRq 27 | MitmNUR2YnpGdFF0b3llUDBDVWxwWGMKLS0tIE5vSVcvQkpTRFVBSkIzQjZTS2JG 28 | ZE5hVWFjMkVUZHZMUTUzdGtIS0t4Z0EKBNB6DrOfP/CjHLuXfo/ArCQaC1pcQcbR 29 | E5p1lnwBlKnT5Ji0i/aBZdyqZEca8a2PTTbDN9jJLb9bZ3AbjLbRJg== 30 | -----END AGE ENCRYPTED FILE----- 31 | lastmodified: "2025-10-27T18:26:59Z" 32 | mac: ENC[AES256_GCM,data:2FTb6zpAmvRbozxxPywEKS2bb7mAMmveWldy8BeMEjisZqkvgwnmuSQW+Dmyjd/T7PzksAuQlFcczztbjVonV4PNWGa6O7fzcKzORgodxojuxvZU79us3vPNviJqeDEvCzkCF81qSa6btuUlxz4o50H7bNJqNDaRqMRRZUc2B1U=,iv:0sLB/xqp18bMKi+bTB++CEPVQOZzL49MGoGpUcHDnVw=,tag:qWai+BFciVVO8BiTh36Sug==,type:str] 33 | unencrypted_suffix: _unencrypted 34 | version: 3.11.0 35 | -------------------------------------------------------------------------------- /.beads/config.yaml: -------------------------------------------------------------------------------- 1 | # Beads Configuration File 2 | # This file configures default behavior for all bd commands in this repository 3 | # All settings can also be set via environment variables (BD_* prefix) 4 | # or overridden with command-line flags 5 | 6 | # Issue prefix for this repository (used by bd init) 7 | # If not set, bd init will auto-detect from directory name 8 | # Example: issue-prefix: "myproject" creates issues like "myproject-1", "myproject-2", etc. 9 | # issue-prefix: "" 10 | 11 | # Use no-db mode: load from JSONL, no SQLite, write back after each command 12 | # When true, bd will use .beads/issues.jsonl as the source of truth 13 | # instead of SQLite database 14 | # no-db: false 15 | 16 | # Disable daemon for RPC communication (forces direct database access) 17 | # no-daemon: false 18 | 19 | # Disable auto-flush of database to JSONL after mutations 20 | # no-auto-flush: false 21 | 22 | # Disable auto-import from JSONL when it's newer than database 23 | # no-auto-import: false 24 | 25 | # Enable JSON output by default 26 | # json: false 27 | 28 | # Default actor for audit trails (overridden by BD_ACTOR or --actor) 29 | # actor: "" 30 | 31 | # Path to database (overridden by BEADS_DB or --db) 32 | # db: "" 33 | 34 | # Auto-start daemon if not running (can also use BEADS_AUTO_START_DAEMON) 35 | # auto-start-daemon: true 36 | 37 | # Debounce interval for auto-flush (can also use BEADS_FLUSH_DEBOUNCE) 38 | # flush-debounce: "5s" 39 | 40 | # Git branch for beads commits (bd sync will commit to this branch) 41 | # IMPORTANT: Set this for team projects so all clones use the same sync branch. 42 | # This setting persists across clones (unlike database config which is gitignored). 43 | # Can also use BEADS_SYNC_BRANCH env var for local override. 44 | # If not set, bd sync will require you to run 'bd config set sync.branch '. 45 | sync-branch: "beads-sync" 46 | # Multi-repo configuration (experimental - bd-307) 47 | # Allows hydrating from multiple repositories and routing writes to the correct JSONL 48 | # repos: 49 | # primary: "." # Primary repo (where this database lives) 50 | # additional: # Additional repos to hydrate from (read-only) 51 | # - ~/beads-planning # Personal planning repo 52 | # - ~/work-planning # Work planning repo 53 | 54 | # Integration settings (access with 'bd config get/set') 55 | # These are stored in the database, not in this file: 56 | # - jira.url 57 | # - jira.project 58 | # - linear.url 59 | # - linear.api-key 60 | # - github.org 61 | # - github.repo 62 | -------------------------------------------------------------------------------- /.beads/README.md: -------------------------------------------------------------------------------- 1 | # Beads - AI-Native Issue Tracking 2 | 3 | Welcome to Beads! This repository uses **Beads** for issue tracking - a modern, AI-native tool designed to live directly in your codebase alongside your code. 4 | 5 | ## What is Beads? 6 | 7 | Beads is issue tracking that lives in your repo, making it perfect for AI coding agents and developers who want their issues close to their code. No web UI required - everything works through the CLI and integrates seamlessly with git. 8 | 9 | **Learn more:** [github.com/steveyegge/beads](https://github.com/steveyegge/beads) 10 | 11 | ## Quick Start 12 | 13 | ### Essential Commands 14 | 15 | ```bash 16 | # Create new issues 17 | bd create "Add user authentication" 18 | 19 | # View all issues 20 | bd list 21 | 22 | # View issue details 23 | bd show 24 | 25 | # Update issue status 26 | bd update --status in-progress 27 | bd update --status done 28 | 29 | # Sync with git remote 30 | bd sync 31 | ``` 32 | 33 | ### Working with Issues 34 | 35 | Issues in Beads are: 36 | 37 | - **Git-native**: Stored in `.beads/issues.jsonl` and synced like code 38 | - **AI-friendly**: CLI-first design works perfectly with AI coding agents 39 | - **Branch-aware**: Issues can follow your branch workflow 40 | - **Always in sync**: Auto-syncs with your commits 41 | 42 | ## Why Beads? 43 | 44 | ✨ **AI-Native Design** 45 | 46 | - Built specifically for AI-assisted development workflows 47 | - CLI-first interface works seamlessly with AI coding agents 48 | - No context switching to web UIs 49 | 50 | 🚀 **Developer Focused** 51 | 52 | - Issues live in your repo, right next to your code 53 | - Works offline, syncs when you push 54 | - Fast, lightweight, and stays out of your way 55 | 56 | 🔧 **Git Integration** 57 | 58 | - Automatic sync with git commits 59 | - Branch-aware issue tracking 60 | - Intelligent JSONL merge resolution 61 | 62 | ## Get Started with Beads 63 | 64 | Try Beads in your own projects: 65 | 66 | ```bash 67 | # Install Beads 68 | curl -sSL https://raw.githubusercontent.com/steveyegge/beads/main/scripts/install.sh | bash 69 | 70 | # Initialize in your repo 71 | bd init 72 | 73 | # Create your first issue 74 | bd create "Try out Beads" 75 | ``` 76 | 77 | ## Learn More 78 | 79 | - **Documentation**: [github.com/steveyegge/beads/docs](https://github.com/steveyegge/beads/tree/main/docs) 80 | - **Quick Start Guide**: Run `bd quickstart` 81 | - **Examples**: [github.com/steveyegge/beads/examples](https://github.com/steveyegge/beads/tree/main/examples) 82 | 83 | --- 84 | 85 | _Beads: Issue tracking that moves at the speed of thought_ ⚡ 86 | -------------------------------------------------------------------------------- /machines/demandred/disko.nix: -------------------------------------------------------------------------------- 1 | { 2 | disko.devices = { 3 | disk = { 4 | root = { 5 | type = "disk"; 6 | device = "/dev/disk/by-id/ata-Samsung_SSD_870_EVO_1TB_S75CNX0XC06201Y"; 7 | content = { 8 | type = "gpt"; 9 | partitions = { 10 | ESP = { 11 | size = "1G"; 12 | type = "EF00"; 13 | content = { 14 | type = "filesystem"; 15 | format = "vfat"; 16 | mountpoint = "/boot"; 17 | mountOptions = [ "umask=0077" ]; 18 | }; 19 | }; 20 | demandred-luks-lvm = { 21 | size = "100%"; 22 | content = { 23 | type = "luks"; 24 | name = "demandred-lvm"; 25 | passwordFile = "/tmp/luks.pass"; 26 | settings = { 27 | allowDiscards = true; 28 | }; 29 | content = { 30 | type = "lvm_pv"; 31 | vg = "main"; 32 | }; 33 | }; 34 | }; 35 | }; 36 | }; 37 | }; 38 | }; 39 | lvm_vg = { 40 | main = { 41 | type = "lvm_vg"; 42 | lvs = { 43 | all = { 44 | size = "100%"; 45 | content = { 46 | type = "btrfs"; 47 | subvolumes = { 48 | "/root" = { 49 | mountpoint = "/"; 50 | mountOptions = [ "compress=zstd" ]; 51 | }; 52 | "/nix" = { 53 | mountpoint = "/nix"; 54 | mountOptions = [ 55 | "compress=zstd" 56 | "noatime" 57 | ]; 58 | }; 59 | "/persist/all" = { 60 | mountpoint = "/persist"; 61 | mountOptions = [ "compress=zstd" ]; 62 | }; 63 | "/local/all" = { 64 | mountpoint = "/local"; 65 | mountOptions = [ "compress=zstd" ]; 66 | }; 67 | "/var/lib/docker" = { 68 | mountpoint = "/var/lib/docker"; 69 | mountOptions = [ "compress=zstd" ]; 70 | }; 71 | }; 72 | }; 73 | }; 74 | swap = { 75 | size = "17G"; 76 | content = { 77 | type = "swap"; 78 | resumeDevice = true; 79 | }; 80 | }; 81 | }; 82 | }; 83 | }; 84 | }; 85 | } 86 | -------------------------------------------------------------------------------- /lib/networks-lookup.nix: -------------------------------------------------------------------------------- 1 | { lib, self, ... }: 2 | let 3 | readRawInventory = 4 | let 5 | dir = builtins.readDir "${self}/inventory/networks"; 6 | regularNixFiles = lib.attrNames ( 7 | lib.filterAttrs (n: v: v == "regular" && lib.hasSuffix ".nix" n) dir 8 | ); 9 | val = lib.map ( 10 | fn: 11 | let 12 | netName = lib.removeSuffix ".nix" fn; 13 | in 14 | lib.nameValuePair netName (import "${self}/inventory/networks/${fn}") 15 | ) regularNixFiles; 16 | in 17 | lib.listToAttrs val; 18 | 19 | buildHostLookupTable = 20 | rawInventory: 21 | let 22 | perNetwork = lib.attrValues (lib.mapAttrs mkReverseLookupTable rawInventory); 23 | in 24 | lib.zipAttrsWith (_hostName: networkTags: lib.foldr (a: b: a // b) { } networkTags) perNetwork; 25 | 26 | mkReverseLookupTable = 27 | netName: 28 | { ipam, ... }: 29 | let 30 | hostLookup = lib.zipAttrsWith (_: mergeTagAssignments) (allAssignmentsForNetwork ipam); 31 | in 32 | lib.mapAttrs (_hostName: tags: { "${netName}" = tags; }) hostLookup; 33 | 34 | mergeTagAssignments = lib.zipAttrsWith ( 35 | name: values: 36 | if builtins.length values == 1 then 37 | builtins.head values 38 | else 39 | throw "Tag '${name}' conflict ${builtins.toJSON values}" 40 | ); 41 | 42 | allAssignmentsForNetwork = 43 | ipam: 44 | lib.map ( 45 | { name, value }: 46 | let 47 | ip = name; 48 | allocationTarget = expandIpAllocationTarget value; 49 | in 50 | assignIp allocationTarget ip 51 | ) (lib.attrsToList ipam); 52 | 53 | expandIpAllocationTarget = 54 | hostNameWithTags: 55 | if lib.isString hostNameWithTags then 56 | [ 57 | hostNameWithTags 58 | "primary" 59 | ] 60 | else 61 | hostNameWithTags; 62 | 63 | normalizeIpam = lib.mapAttrs (_k: v: expandIpAllocationTarget v); 64 | 65 | taggedHostnames = 66 | { domain, ... }: 67 | hostnameWithTags: 68 | with lib; 69 | let 70 | hostname = head hostnameWithTags; 71 | tags = tail hostnameWithTags; 72 | in 73 | map ( 74 | tag: if tag == "primary" then "${hostname}.${domain}" else "${hostname}-${tag}.${domain}" 75 | ) tags; 76 | 77 | assignIp = 78 | ipAllocationTarget: ip: 79 | let 80 | hostName = lib.head ipAllocationTarget; 81 | tags = lib.tail ipAllocationTarget; 82 | in 83 | { 84 | "${hostName}" = lib.genAttrs tags (_tag: { 85 | _type = "leaf"; 86 | address = ip; 87 | }); 88 | }; 89 | in 90 | { 91 | inherit 92 | readRawInventory 93 | buildHostLookupTable 94 | normalizeIpam 95 | taggedHostnames 96 | ; 97 | } 98 | -------------------------------------------------------------------------------- /secrets/furfur/secrets.yaml: -------------------------------------------------------------------------------- 1 | binarin_password_hash: ENC[AES256_GCM,data:DYHCS/cdwEo1mxPUlMBSux8lavhxPE+SPHV4wR4AJV4pYMLtC+cPlX7IwiimVlKEPuecxqegqUQoHx6MUL+Hy9I/7iH3z+V5TA==,iv:n/gexK9sJdpK1VCJvd7t1HnfOcrGudIbYRtsSW/sFVI=,tag:0NwLJjrqgpbKbT4oAC4IEQ==,type:str] 2 | root_password_hash: ENC[AES256_GCM,data:QnATUBhw3F0LlQy/kwWN73v7r//KhKcXKJhJhUM/NUO38Ve0a4W7C563I2qKJekhtx7+AK0E0/Yaqmxkj8dbb5aRsZBE8GrDMA==,iv:oCNjA/XF6O9u97kgbejCEk9PEiiXOGhzgQcz9uUioMQ=,tag:QmFlc3aVfalISMKwa5YdHw==,type:str] 3 | agares_password: ENC[AES256_GCM,data:rQ2wxiwT96Zayi3gZeJYuHOs3LjaQsq2ecRzLh1MueZUUHBVJ7cCtc5SogxPu4cO,iv:cVp4R0yRgNYyT/YKvLMVkvngSg4lq3kdax77dXvDpsE=,tag:FM5Gr6aHdw5iKFdrQSaPIA==,type:str] 4 | sops: 5 | age: 6 | - recipient: age1dvu92xmh3w23ghey4l0epcs27m89f3nswzs5twgnel36xxu42f4sfsv023 7 | enc: | 8 | -----BEGIN AGE ENCRYPTED FILE----- 9 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBadHRSaWFKSkpmWW5qcUtS 10 | Mkhqa2JXVFdSR3IxcXFrcyt1M1RWRlBHb21RCksvMC94eVJPR2s4cnlHVFVCbDM5 11 | RXFEd1NhRVdxeThtZFZxcW5jRDNxWXcKLS0tIDFrKy9hcEJ5cWtHYk1YVUNtY3Fm 12 | SGtrTjU0ckVNak5pZytZU3RjNzBURUUKrxNV0fNFOPlYvvxmnRi60ZzWtGdvD8MS 13 | ZlMfTPcIwDAaibCOPxZcHQkHau78NZW8Mc6zEpF1PIj/lt93BZdVkA== 14 | -----END AGE ENCRYPTED FILE----- 15 | - recipient: age14uq5t9y9sapf54enpahql6ncfqvttrwwxk459vtkegu4slk9ef7s6pp2uw 16 | enc: | 17 | -----BEGIN AGE ENCRYPTED FILE----- 18 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBVWnUxWHVrRnQ0bnVpWGtZ 19 | Rksza2YzWEdkUlZFcGlrY2Q4dzlSYjhmYng4Cm84aFVDZlB6VTBiT2czdW42cUww 20 | TGd2aTF6TFVRbjVWTHZCcFZmM3FuNWcKLS0tIE5RRXJXazRhQ0x6QVpGQ0IvbFly 21 | dnNhL1hScittM21YK1pGdGIrTy9WRk0KOQ/7dsX+Sr7SuzfyxMIAvqZlqMMExcQ4 22 | F1ZyjarOWZ/rmfZrNuJFysiXzOl8Rlo7tkY1aqI1XU1P174XgBu0Mg== 23 | -----END AGE ENCRYPTED FILE----- 24 | lastmodified: "2025-11-30T12:38:11Z" 25 | mac: ENC[AES256_GCM,data:LCD0ZtxmBzKbi3HixeCYX7HJJuIoopbzF67XpE/mdhQUEmMqUWwCW4P3U0Qu4C0PhjIXY+8cSJFTocw6+GFq7GyK+VI9W+A7dHVjNRqYFMK1xYRxCUaD4aXu/3CXbLwgJyj+lZk+VfjyMYIgXBa7gTNiwE2OzRpwi335INjKgCg=,iv:pJDsD5CZTCwQeuyZkFxKAbZZveENpRhlPPdddpbYYsU=,tag:LGOl6OaSG2rei7+ABAuZUA==,type:str] 26 | pgp: 27 | - created_at: "2025-12-01T13:09:32Z" 28 | enc: |- 29 | -----BEGIN PGP MESSAGE----- 30 | 31 | hF4DjUagJu2XQwUSAQdAknlF020bCZMH8w4SJs0InbJ3v+XCL3hWxwphkqnH8g0w 32 | eWaqRbPxREeoX/kRUia+DvS36vgTI5i4fgeNXCIHCGwU2ik2HbQRV6ur9DmfUUON 33 | 0l4BPkKL0duPR40+3r4H69Q1ujrd9fdx5n6CuY9RTa3UKrO5/FVk8n4bdxTyY6pL 34 | /FI9YQUUqEb76uMxm6xgJbf2NL5BbTk3DiR1gLDYMVFqPtICTxAfV5aXg+BmYM6F 35 | =WkUy 36 | -----END PGP MESSAGE----- 37 | fp: 7668F12EF30BE2A802E1E13E6D4365C3B5E703F5 38 | unencrypted_suffix: _unencrypted 39 | version: 3.11.0 40 | -------------------------------------------------------------------------------- /modules/binarin/hypridle.nix: -------------------------------------------------------------------------------- 1 | { ... }: 2 | { 3 | flake.nixosModules.hypridle = 4 | { ... }: 5 | { 6 | key = "nixos-config.modules.nixos.hypridle"; 7 | programs.hyprlock.enable = true; 8 | }; 9 | 10 | flake.homeModules.hypridle = 11 | { config, ... }: 12 | { 13 | key = "nixos-config.modules.home.hypridle"; 14 | services.hypridle = { 15 | enable = true; 16 | settings = { 17 | general = { 18 | lock_cmd = "pidof hyprlock || hyprlock"; # avoid starting multiple hyprlock instances. 19 | before_sleep_cmd = "loginctl lock-session"; # lock before suspend. 20 | after_sleep_cmd = "hyprctl dispatch dpms on"; # to avoid having to press a key twice to turn on the display. 21 | }; 22 | listener = [ 23 | { 24 | timeout = 150; # 2.5min. 25 | on-timeout = "brightnessctl-all 10"; # set monitor backlight to minimum, avoid 0 on OLED monitor. 26 | on-resume = "brightnessctl-all 100"; # monitor backlight restore. 27 | } 28 | 29 | { 30 | timeout = 300; # 5min 31 | on-timeout = "loginctl lock-session"; # lock screen when timeout has passed 32 | } 33 | 34 | { 35 | timeout = "330"; # 5.5min 36 | on-timeout = "hyprctl dispatch dpms off"; # screen off when timeout has passed 37 | on-resume = "hyprctl dispatch dpms on"; # screen on when activity is detected after timeout has fired. 38 | } 39 | ]; 40 | }; 41 | }; 42 | 43 | programs.hyprlock = { 44 | enable = true; 45 | settings = { 46 | general = { 47 | disable_loading_bar = true; 48 | grace = 30; 49 | hide_cursor = true; 50 | ignore_empty_input = true; 51 | }; 52 | 53 | background = [ 54 | { 55 | path = "screenshot"; 56 | blur_passes = 3; 57 | blur_size = 8; 58 | } 59 | ]; 60 | 61 | input-field = with config.lib.stylix.colors; [ 62 | { 63 | size = "200, 50"; 64 | position = "0, -80"; 65 | monitor = ""; 66 | dots_center = true; 67 | fade_on_empty = false; 68 | outer_color = "rgb(${base03})"; 69 | inner_color = "rgb(${base00})"; 70 | font_color = "rgb(${base05})"; 71 | fail_color = "rgb(${base08})"; 72 | check_color = "rgb(${base0A})"; 73 | outline_thickness = 5; 74 | placeholder_text = ''Password...''; 75 | shadow_passes = 2; 76 | } 77 | ]; 78 | }; 79 | }; 80 | }; 81 | } 82 | -------------------------------------------------------------------------------- /modules/xdg-autostart.nix: -------------------------------------------------------------------------------- 1 | { ... }: 2 | { 3 | flake.homeModules.xdg-autostart = 4 | { lib, config, ... }: 5 | let 6 | cfg = config.xdg.autostart.override; 7 | optsModule = 8 | with lib; 9 | with types; 10 | submodule { 11 | options = { 12 | hidden = mkOption { 13 | description = "No service will be generated if set to true (Hidden= entry)"; 14 | type = nullOr bool; 15 | default = null; 16 | }; 17 | x-systemd-skip = mkOption { 18 | description = "No service will be generated if set to true (X-systemd-skip= entry)"; 19 | type = nullOr bool; 20 | default = null; 21 | }; 22 | notShownIn = mkOption { 23 | description = "ExecCondition= using systemd-xdg-autostart-condition (NotShowIn= entry)"; 24 | type = nullOr (listOf str); 25 | default = null; 26 | }; 27 | onlyShownIn = mkOption { 28 | description = "ExecCondition= using systemd-xdg-autostart-condition (OnlyShowIn= entry)"; 29 | type = nullOr (listOf str); 30 | default = null; 31 | }; 32 | }; 33 | }; 34 | in 35 | { 36 | key = "nixos-config.modules.home.xdg-autostart"; 37 | options = { 38 | xdg.autostart.override = lib.mkOption { 39 | type = with lib.types; attrsOf optsModule; 40 | default = { }; 41 | }; 42 | }; 43 | config = lib.mkMerge [ 44 | { 45 | assertions = lib.mapAttrsToList (name: options: { 46 | assertion = !(options.notShownIn != null && options.onlyShownIn != null); 47 | message = "xdg.autostart.override.${name}: Cannot specify both notShownIn and onlyShownIn for the same desktop entry"; 48 | }) cfg; 49 | 50 | xdg.configFile = 51 | with lib; 52 | flip mapAttrs' cfg ( 53 | name: options: 54 | nameValuePair "autostart/${name}.desktop" { 55 | text = '' 56 | [Desktop Entry] 57 | '' 58 | + optionalString (options.hidden != null) "Hidden=${if options.hidden then "true" else "false"}\n" 59 | + 60 | optionalString (options.x-systemd-skip != null) 61 | "X-systemd-skip=${if options.x-systemd-skip then "true" else "false"}\n" 62 | + optionalString ( 63 | options.notShownIn != null 64 | ) "NotShowIn=${concatStringsSep ";" options.notShownIn}\n" 65 | + optionalString ( 66 | options.onlyShownIn != null 67 | ) "OnlyShowIn=${concatStringsSep ";" options.onlyShownIn}\n"; 68 | } 69 | ); 70 | } 71 | ]; 72 | }; 73 | } 74 | -------------------------------------------------------------------------------- /modules/programs/linkwarden.nix: -------------------------------------------------------------------------------- 1 | { ... }: 2 | { 3 | flake.nixosModules.linkwarden = 4 | { config, ... }: 5 | { 6 | key = "nixos-config.modules.nixos.linkwarden"; 7 | config = { 8 | sops.secrets."linkwarden/nextauth-secret" = { }; 9 | sops.secrets."linkwarden/postgres-password" = { }; 10 | 11 | services.caddy.virtualHosts."linkwarden.binarin.info".extraConfig = '' 12 | reverse_proxy http://127.0.0.1:3000 13 | import letsencrypt 14 | ''; 15 | 16 | sops.templates."linkwarden-env".content = '' 17 | NEXTAUTH_URL=http://localhost:3000/api/v1/auth 18 | NEXTAUTH_SECRET=${config.sops.placeholder."linkwarden/nextauth-secret"} 19 | POSTGRES_PASSWORD=${config.sops.placeholder."linkwarden/postgres-password"} 20 | NEXT_PUBLIC_DISABLE_REGISTRATION=true 21 | DISABLE_NEW_SSO_USERS=true 22 | ''; 23 | 24 | sops.templates."linkwarden-database-url-env".content = '' 25 | DATABASE_URL="postgresql://postgres:${ 26 | config.sops.placeholder."linkwarden/postgres-password" 27 | }@postgres:5432/postgres" 28 | ''; 29 | 30 | virtualisation.arion.projects.linkwarden = { 31 | serviceName = "linkwarden-docker-compose"; 32 | settings = { 33 | services = { 34 | postgres.service = { 35 | image = "postgres:16-alpine"; 36 | env_file = [ 37 | config.sops.templates."linkwarden-env".path 38 | ]; 39 | restart = "unless-stopped"; 40 | volumes = [ 41 | "/var/lib/linkwarden/postgres-data:/var/lib/postgresql/data" 42 | ]; 43 | }; 44 | linkwarden.service = { 45 | env_file = [ 46 | config.sops.templates."linkwarden-env".path 47 | config.sops.templates."linkwarden-database-url-env".path 48 | ]; 49 | restart = "unless-stopped"; 50 | image = "ghcr.io/linkwarden/linkwarden:v2.13.1"; 51 | ports = [ "3000:3000" ]; 52 | volumes = [ 53 | "/var/lib/linkwarden/linkwarden-data:/data/data" 54 | ]; 55 | depends_on = [ 56 | "postgres" 57 | "meilisearch" 58 | ]; 59 | }; 60 | meilisearch.service = { 61 | image = "getmeili/meilisearch:v1.12.8"; 62 | restart = "unless-stopped"; 63 | env_file = [ 64 | config.sops.templates."linkwarden-env".path 65 | ]; 66 | volumes = [ 67 | "/var/lib/linkwarden/meilisearch-data:/meili_data" 68 | ]; 69 | }; 70 | }; 71 | }; 72 | }; 73 | }; 74 | }; 75 | } 76 | -------------------------------------------------------------------------------- /secrets/demandred/secrets.yaml: -------------------------------------------------------------------------------- 1 | tailscale-auth: ENC[AES256_GCM,data:nd6omS4=,iv:5TeUcddgTgimXdBnzlvYgPZSrnO265Zn/NLouw8HuWQ=,tag:sWTm3x4Ghf9LVrfZzPMErw==,type:str] 2 | sops: 3 | age: 4 | - recipient: age1ec9gz6f685mn8pdrz48qm4vq7qzpzcz48vgkcty9aa665evra9lq2uz3s2 5 | enc: | 6 | -----BEGIN AGE ENCRYPTED FILE----- 7 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBUak81b3VQRVZmWVI3WDVm 8 | Zk9WKzQ4UW4wSWZiaE5UWWJ5SkVWMjZMTFJNClNkR24yOGt5eHdPcGdYVTJ4UDBm 9 | SFF0VXFBSzZ3Yy96WmQrVi9BVlRYdTgKLS0tIDl1K2czQW5vT0Y3WEszMys3elRn 10 | aFJuZm1oNkFIRHo1RHBrRUpHNEhlSjAKQsz0ve4A/fbNAvg2xi1/MxlHYg5E0xg+ 11 | HwuOnZ3XBlZJ5hGMVW7EyI1aFp6zTMVJccY1A5zmslLKa4vY7D+uKg== 12 | -----END AGE ENCRYPTED FILE----- 13 | - recipient: age1dvu92xmh3w23ghey4l0epcs27m89f3nswzs5twgnel36xxu42f4sfsv023 14 | enc: | 15 | -----BEGIN AGE ENCRYPTED FILE----- 16 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBVYjZ6UFBwRnoxSURvekJ6 17 | Y29QUCtKdGx5cit2VHhYL3UrdWpxckRpclNnCjdwTlRLL2c3VDNCQ09mRHNrWVhQ 18 | bzJEaHlEQmk1MnN3RTgrU1hlTTJ0alkKLS0tIFU0VDJmcG1IS3dIclBYU1ZUcmV3 19 | S0pDTHp0b1NZbTJtZ3g4UG1sd1J0QVEKUn6Cn43bQAj1GO21xseyubK5YjYbtwTp 20 | 88icLweYd+lyR7l489T1Ymef65voFA8PzrVlvzkEIUUBapCW6Rpnyg== 21 | -----END AGE ENCRYPTED FILE----- 22 | - recipient: age1flg86f4c6xd2jss983nml4vvqexxyxavm22rp4ve3h6mywc9kdesnppy7j 23 | enc: | 24 | -----BEGIN AGE ENCRYPTED FILE----- 25 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBONVVMOHl0d1EzMGprQTQ4 26 | eHlUTDRwOEVVd3o2ZHVGaDFwd1ZjNFNjQm5vCk1jeUdsZU1KV0lwZWJydmFuTkNw 27 | UlFNOGN3M0VPTUMyUmE2Nm1Gbjd2TmcKLS0tIGJ6bWw1amZhdjVrYUF1Y3FTeDIx 28 | cnYyV2NwN2tqaHhSZjhUUnlVWWlzQjQKuZKw3w6pwk8IdS715JP8QL1fZ0i4TuRm 29 | Y0rerQbDTwI63tv0m06y+bZ7Bg18BkCxYSrdj3wyK9jcADVMURleGg== 30 | -----END AGE ENCRYPTED FILE----- 31 | lastmodified: "2025-02-05T09:48:30Z" 32 | mac: ENC[AES256_GCM,data:jGLNiAtSpai7iDu8J3slMKHo9fC1YaCqaHbsk4omml3Eoga7No+zHX6FjX+00uAMJD9ItF/ofRrBnDum9OEAVBMGKOr/07cD3BNgf/2QIoj+WoANU7Pv/LpkJ4S+GHDsavQwftpuCqoRCoiIWHjp59k1jP+tWvnbMLoqAUvJV+Q=,iv:5bMPUf/zR1L67KiAeUwOh1QVZgHHcbV3H5WlWEy1EaI=,tag:UjmWupDAVpTxf9Nfg+c0Gw==,type:str] 33 | pgp: 34 | - created_at: "2025-12-01T13:09:32Z" 35 | enc: |- 36 | -----BEGIN PGP MESSAGE----- 37 | 38 | hF4DjUagJu2XQwUSAQdATRkZnuKbK8vn9Lu4GFpPo2r7q/+PteBQGFnCWf3hxgYw 39 | Umbw6RlCJZ5hhGIx2dhczS+oE1/cLMxuLcA5PiYrasArYQtgMSwdtOzrk0+DRI5M 40 | 0l4ByXf/1sqC7HnGkoxfXWsN7DfCRA9KpeaSW7TcLO3lqaApzVLHyEH22aAdPsVD 41 | wY2GQdkdC6wItW61fyTFjUQV6Pm91OYfcd0MTRqzbM+sXE1GxQ/57LXNpDPUMlmz 42 | =t6/v 43 | -----END PGP MESSAGE----- 44 | fp: 7668F12EF30BE2A802E1E13E6D4365C3B5E703F5 45 | unencrypted_suffix: _unencrypted 46 | version: 3.9.4 47 | -------------------------------------------------------------------------------- /secrets/demandred/user-binarin.yaml: -------------------------------------------------------------------------------- 1 | syncthing-ui-password: ENC[AES256_GCM,data:VCH/St2i+5DY6Nubzl3Rjfe5aEbaEi+Up5UVzWRWDH4=,iv:ooHkcuv2mPM5/0uNGXQbxl6qn/SYR4ayTjMWgfLe32I=,tag:AuvSfvIOcjJbcvrweXtsEg==,type:str] 2 | sops: 3 | age: 4 | - recipient: age1ec9gz6f685mn8pdrz48qm4vq7qzpzcz48vgkcty9aa665evra9lq2uz3s2 5 | enc: | 6 | -----BEGIN AGE ENCRYPTED FILE----- 7 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBYR3FNc0dnWGxlVmlna0c3 8 | NkcwRWI1YXZ2NnJDYlRQRXlrY3RZemZYVnlJCjFkd1hZV2lXK2dlemdPeGZTa1c4 9 | bTYrdmxVdk1ySDFiV2JtZXRBWTJQUEUKLS0tIG54cXQ4VXVVZmJ2MlB1OG8vMFhn 10 | RFdXcW1OSHFUc25xUjloelFhK2ZCNUkK3zaAXOFUa/h9ec4K6Ve+mzQywRgYAtoZ 11 | f5DCkQqZUI0xN8KqngqhPUW4DxAnGiXltKgQzoUh/DxlBR7LMynfzw== 12 | -----END AGE ENCRYPTED FILE----- 13 | - recipient: age1dvu92xmh3w23ghey4l0epcs27m89f3nswzs5twgnel36xxu42f4sfsv023 14 | enc: | 15 | -----BEGIN AGE ENCRYPTED FILE----- 16 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBuM0tQbjE5Y05ubC92N2l0 17 | UTFsTksyRmllTVhKYWRDMjNaQjRaVWNrYmxNCjZLV2R2VzdLZDhCWnlTeE5nYTVT 18 | SXcvZTRpYUtMUHF4SlA4dTlxK2c0WEEKLS0tIFFVN2hHQ1ZiSi8rVi9XMlpYSEpJ 19 | UTJXOUlHaGVKNzBvR21ubmxpdFphUFUKdB/IdOfkDMDpJZLbLIw0QIBQ+eCS/9ye 20 | disiwcecRHhWvIDMuNH0lHp0uifQUtYEpXRD+iNK+PJF6ykF19VSvA== 21 | -----END AGE ENCRYPTED FILE----- 22 | - recipient: age1flg86f4c6xd2jss983nml4vvqexxyxavm22rp4ve3h6mywc9kdesnppy7j 23 | enc: | 24 | -----BEGIN AGE ENCRYPTED FILE----- 25 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBzMHBoTE9aR0NDTFIxZDg2 26 | d1g5QzRLNUxKMUt5ZGk0L0FBZmU5eDZuWVZVClF4c3BOdEZSQUhWbkNkSDUySmtO 27 | YjQ2Y1dzNUQ0cnAyMHg4czJSbkEzQTAKLS0tIHROeDB0bi9zMHBpTDBJQXlTOTFj 28 | VUtGT1ZJMzQ4OVZFMzcwZm1GbUpGRVEKWCkomCV5iOitc+DxiIC6bzv5PhOpxbnj 29 | QXxbLcuxoZBw2NMBQxMeRYzpoKMP2KjvGia0Jxd4jmxjLFZed2mKlw== 30 | -----END AGE ENCRYPTED FILE----- 31 | lastmodified: "2025-11-13T07:00:21Z" 32 | mac: ENC[AES256_GCM,data:Ke4GEo1QJO4tIEjdzDw9ZsGAz3k2qnOLoeTAjgwf5SvVqqJ8gHnpF2GLVTWEnypFYzJx4PSHekA09wCMW0N4JsN0DkHVljl79RNLIl54Slj5UD0PWK4lKNWwp/r/WtIFC/mEr3qqL5j5IL0aNtphYQzoHaWgxTrPi9ogfnKmXXw=,iv:GAcKGxzmzIz92kS+jqVROckT3PJRc+ikPJKIUGDYVSM=,tag:6b2fqnVA0wUEGF469kN7Sw==,type:str] 33 | pgp: 34 | - created_at: "2025-12-01T13:09:32Z" 35 | enc: |- 36 | -----BEGIN PGP MESSAGE----- 37 | 38 | hF4DjUagJu2XQwUSAQdAJVXfaBMZS2tBJ23DvjTW7hO684Xj24mVT7IuUeMbPhgw 39 | w5DvdUIjBQAKowdRM4NwmZ0xLHJFm/03c8i3cE+AKYzbXVihQctbDIM0SV7OBb/N 40 | 0l4BnwzQHS7dDBR1jSVLO7hsf2t91hNilwwEyljDiCd3PF9OV7sM4CXCwcj93O/O 41 | F+e+Fc+3pirqLVcLMGDDgg2jjsd0/PP3RHTPLHHSkHOHKOUpjOABAMXQTwAL1d+I 42 | =6YGp 43 | -----END PGP MESSAGE----- 44 | fp: 7668F12EF30BE2A802E1E13E6D4365C3B5E703F5 45 | unencrypted_suffix: _unencrypted 46 | version: 3.11.0 47 | -------------------------------------------------------------------------------- /secrets/ishamael/user-binarin.yaml: -------------------------------------------------------------------------------- 1 | syncthing-ui-password: ENC[AES256_GCM,data:VCH/St2i+5DY6Nubzl3Rjfe5aEbaEi+Up5UVzWRWDH4=,iv:ooHkcuv2mPM5/0uNGXQbxl6qn/SYR4ayTjMWgfLe32I=,tag:AuvSfvIOcjJbcvrweXtsEg==,type:str] 2 | sops: 3 | age: 4 | - recipient: age1ec9gz6f685mn8pdrz48qm4vq7qzpzcz48vgkcty9aa665evra9lq2uz3s2 5 | enc: | 6 | -----BEGIN AGE ENCRYPTED FILE----- 7 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBramZJSCtWY0N5bHF1Z1NB 8 | aWJrRFBtUkF3SSthSnZlUGxpWW1BRDZKNlNnCit0eGdCeXNKNE1UVzlicHIySEpo 9 | NUN3N0EwbWFKTGk1eUV3VkdQQTB6SVkKLS0tICtpNGxvVVZuWEJmblJKdFN0T3B6 10 | WHRQOWNoOE0zbzVtVys4RUgvY3plWVkKwMvvbh7FD3Oa7IvTPGY7WmA/oizio2bY 11 | GT3hBYAKiiBx8+Zl2vRWx8Y2/3GxS2afrYGZaqjjVLkqPY46hiDNWw== 12 | -----END AGE ENCRYPTED FILE----- 13 | - recipient: age1qk6g50qsla4mn9qm8zc9nhf0vwz37wyvlhajann8ct9shkxg0g7s5yuqn5 14 | enc: | 15 | -----BEGIN AGE ENCRYPTED FILE----- 16 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBNN1J5Z0c2NjFic1BFRHdR 17 | dUd2VmdhNmc1bVlqd3RSTG9DcXdJZkVzemg0CmZTRmdVYUhQaGFsSEdUZldQVlFy 18 | L0srUERpUkJCZ1UvdUVMOGR3VURyK0kKLS0tIDBIS29wZFgzalpGQXBMcURlbmhR 19 | VG1RUDZFYmpDOURLZlBZQTMyVmhZOTAKmn1YVWNTYn8n5ivlUmfAzaCLGtGaZHbu 20 | au+5vx/fWlJ5CGJBVFFex67g1aBXFuwErjAMD+5rm+yszXKDLjr4fg== 21 | -----END AGE ENCRYPTED FILE----- 22 | - recipient: age1qud3t3zq28d0p3z42gj0vr5yamqcgcd4sh0uvfdg2g24qwkknqds4356qd 23 | enc: | 24 | -----BEGIN AGE ENCRYPTED FILE----- 25 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBDUks5alhOQzZpeDJndlRy 26 | RDFJbjNqQS9KU2c2RTI4aFBvZHRFVFJxUVdZClpZbEpqK2dsM25sRmVNUjBFWW45 27 | Rkk4c1ArdlM5MzJhQ1NjcHlxUWoyNlkKLS0tIGxHcmNyKzc2cDBwMkRZaXBJSm9N 28 | d1lyMTBXSko5cUlPaS93cENGYUc2bncKTBwnEshL3HIeCnPq1iscU9F+x+YFHeva 29 | K96O2nSswHGqCnq+avEWoej7vGrymaWkP7RYmm8Fo8EHLk/wKmWeJA== 30 | -----END AGE ENCRYPTED FILE----- 31 | lastmodified: "2025-11-13T07:00:21Z" 32 | mac: ENC[AES256_GCM,data:Ke4GEo1QJO4tIEjdzDw9ZsGAz3k2qnOLoeTAjgwf5SvVqqJ8gHnpF2GLVTWEnypFYzJx4PSHekA09wCMW0N4JsN0DkHVljl79RNLIl54Slj5UD0PWK4lKNWwp/r/WtIFC/mEr3qqL5j5IL0aNtphYQzoHaWgxTrPi9ogfnKmXXw=,iv:GAcKGxzmzIz92kS+jqVROckT3PJRc+ikPJKIUGDYVSM=,tag:6b2fqnVA0wUEGF469kN7Sw==,type:str] 33 | pgp: 34 | - created_at: "2025-12-01T13:09:32Z" 35 | enc: |- 36 | -----BEGIN PGP MESSAGE----- 37 | 38 | hF4DjUagJu2XQwUSAQdAUFVzXBopLUPLf038pBTNM6gMIYzO8SonIM6UImU/em4w 39 | hn2TWVupVugaqgQGyq5xhiL/YSZb0UoPRlX+OaiL6+Wj8j5xhRi2l8eTRo3k6oMb 40 | 0l4B/0gd6rnXPb8aZPNeV+1EnjuvTjYMQZkpS9nUtKT1WFm8tNdtsxXbA2FEmXcO 41 | 4KO0bH7uaRDu5xGBS+OFVKIYqiHXH2d6e4nnwEyHtikpSULJpgx+WyHoEo5Xww2b 42 | =4rkW 43 | -----END PGP MESSAGE----- 44 | fp: 7668F12EF30BE2A802E1E13E6D4365C3B5E703F5 45 | unencrypted_suffix: _unencrypted 46 | version: 3.11.0 47 | -------------------------------------------------------------------------------- /modules/binarin/workstation.nix: -------------------------------------------------------------------------------- 1 | { self, ... }: 2 | { 3 | flake.nixosModules.binarin-workstation = 4 | { ... }: 5 | { 6 | key = "nixos-config.modules.nixos.binarin-workstation"; 7 | 8 | imports = [ 9 | self.nixosModules.binarin-baseline 10 | self.nixosModules.home-manager 11 | self.nixosModules.impermanence 12 | self.nixosModules.stylix 13 | self.nixosModules.gnupg 14 | self.nixosModules.emacs 15 | self.nixosModules.binarin-nix-dev 16 | ]; 17 | 18 | users.users.binarin.extraGroups = [ 19 | "dialout" 20 | "docker" 21 | "i2c" 22 | "libvirtd" 23 | "lxd" 24 | "networkmanager" 25 | "plugdev" # QMK 26 | "transmission" 27 | "tss" # for TPM2 28 | "users" 29 | "vboxusers" 30 | "video" 31 | "wheel" 32 | "wireshark" 33 | ]; 34 | 35 | hardware.keyboard.qmk.enable = true; 36 | 37 | home-manager.users.binarin = self.homeModules.binarin-workstation; 38 | }; 39 | 40 | flake.homeModules.binarin-workstation = 41 | { 42 | lib, 43 | osConfig, 44 | pkgs, 45 | ... 46 | }: 47 | { 48 | key = "nixos-config.modules.home.binarin-workstation"; 49 | 50 | imports = [ 51 | self.homeModules.impermanence 52 | self.homeModules.git 53 | self.homeModules.emacs 54 | self.homeModules.claude-code 55 | self.homeModules.direnv 56 | self.homeModules.interactive-cli 57 | self.homeModules.sops 58 | self.homeModules.gnupg 59 | ] 60 | ++ (lib.optionals osConfig.services.graphical-desktop.enable [ 61 | self.homeModules.niri 62 | # self.homeModules.hyprland 63 | self.homeModules.wezterm 64 | self.homeModules.foot 65 | self.homeModules.fonts 66 | self.homeModules.syncthing 67 | self.homeModules.firefox 68 | self.homeModules.binarin-nix-dev 69 | ]); 70 | 71 | programs.git = { 72 | settings.user = { 73 | name = "Alexey Lebedeff"; 74 | email = "binarin@binarin.info"; 75 | }; 76 | }; 77 | 78 | home.packages = with pkgs; [ 79 | gopass 80 | dos2unix 81 | sox 82 | ]; 83 | 84 | impermanence.local-files = [ 85 | ]; 86 | 87 | impermanence.local-directories = [ 88 | ".cache/nixos-config" 89 | ".cache/nix" 90 | ".local/state/home-manager" 91 | ".local/state/nix" 92 | ]; 93 | 94 | impermanence.persist-files = [ 95 | ]; 96 | 97 | impermanence.persist-directories = [ 98 | "personal-workspace" 99 | "org" 100 | "annex" 101 | "finance" 102 | ".ssh" 103 | ".local/share/gopass" 104 | ".config/gopass" 105 | ".config/qmk" 106 | ]; 107 | }; 108 | } 109 | -------------------------------------------------------------------------------- /files/wezterm.lua: -------------------------------------------------------------------------------- 1 | local wezterm = require 'wezterm' 2 | 3 | local stylix = require 'stylix-vars' 4 | 5 | local config = wezterm.config_builder() 6 | local act = wezterm.action 7 | 8 | config.font = wezterm.font(stylix.fontName) 9 | config.font_size = stylix.fontSize 10 | 11 | config.unix_domains = { 12 | { 13 | name = 'unix', 14 | }, 15 | } 16 | 17 | config.default_gui_startup_args = { 'connect', 'unix' } 18 | 19 | -- Show which key table is active in the status area 20 | wezterm.on('update-right-status', function(window, pane) 21 | local name = window:active_key_table() 22 | if name then 23 | name = 'TABLE: ' .. name 24 | end 25 | window:set_right_status(name or '') 26 | end) 27 | 28 | config.keys = { 29 | { key = 'Enter', mods = 'ALT', action = act.DisableDefaultAssignment }, 30 | { key = 'l', mods = 'ALT', action = act.ShowLauncher }, 31 | { 32 | key = 'o', 33 | mods = 'CTRL', 34 | action = act.ActivateKeyTable { 35 | name = "ctrl_o", 36 | timeout_milliseconds = 2000, 37 | }, 38 | }, 39 | } 40 | 41 | config.key_tables = { 42 | ctrl_o = { 43 | { key = 'o', 44 | action = act.SendKey { 45 | key = 'o', 46 | mods = 'CTRL', 47 | }, 48 | }, 49 | { key = 'o', 50 | mods = 'CTRL', 51 | action = act.ActivateLastTab, 52 | }, 53 | { key = 'n', action = wezterm.action.ActivateTabRelative(1) }, 54 | { key = 'p', action = wezterm.action.ActivateTabRelative(-1) }, 55 | { key = 'c', action = act.SpawnTab 'CurrentPaneDomain' }, 56 | { key = '-', action = act.SplitVertical { domain = 'CurrentPaneDomain' } }, 57 | { key = '|', mods = 'SHIFT', action = act.SplitHorizontal { domain = 'CurrentPaneDomain' } }, 58 | { key = 'LeftArrow', action = act.ActivatePaneDirection "Left" }, 59 | { key = 'RightArrow', action = act.ActivatePaneDirection "Right" }, 60 | { key = 'DownArrow', action = act.ActivatePaneDirection "Down" }, 61 | { key = 'UpArrow', action = act.ActivatePaneDirection "Up" }, 62 | }, 63 | } 64 | 65 | for tab_no=1,9 do 66 | table.insert(config.key_tables.ctrl_o, { key = tostring(tab_no), action = act.ActivateTab(tab_no - 1) }) 67 | end 68 | 69 | 70 | -- config.color_scheme = 'Zenburn' 71 | config.colors = { 72 | background = "#000000", 73 | foreground = "#faebd7", 74 | ansi = { 75 | "#000000", 76 | "#cd0000", 77 | "#00cd00", 78 | "#cdcd00", 79 | "#0000cd", 80 | "#cd00cd", 81 | "#00cdcd", 82 | "#faebd7", 83 | }, 84 | brights = { 85 | "#404040", 86 | "#ff0000", 87 | "#00ff00", 88 | "#ffff00", 89 | "#0000ff", 90 | "#ff00ff", 91 | "#00ffff", 92 | "#ffffff", 93 | } 94 | } 95 | 96 | config.window_frame = { 97 | font_size = 11, 98 | -- active_titlebar_bg = '#00ff00', 99 | -- inactive_titlebar_bg = '', 100 | } 101 | 102 | return config 103 | -------------------------------------------------------------------------------- /modules/initrd-ssh.nix: -------------------------------------------------------------------------------- 1 | { self, ... }: 2 | { 3 | flake.nixosModules.initrd-ssh = 4 | { config, lib, ... }: 5 | { 6 | key = "nixos-config.modules.nixos.initrd-ssh"; 7 | boot.initrd.systemd.contents."/etc/ssh/ssh_host_ed25519_key.jwe".source = 8 | "${self}/secrets/qdevice/ssh_host_ed25519_key.jwe"; 9 | boot.initrd.systemd.contents."/etc/ssh/ssh_host_rsa_key.jwe".source = 10 | "${self}/secrets/qdevice/ssh_host_rsa_key.jwe"; 11 | boot.initrd.systemd.contents."/etc/ssh/trusted_user_ca_keys".text = lib.concatStringsSep "\n" ( 12 | config.lib.publicKeys.secureWithTag "user-ca" 13 | ); 14 | 15 | boot.initrd.systemd.enable = true; 16 | boot.initrd.systemd.network.enable = true; 17 | # boot.initrd.systemd.emergencyAccess = true; 18 | # boot.initrd.systemd.initrdBin = with pkgs; [ iproute2 gnugrep procps gnutar ]; 19 | 20 | # boot.kernelParams = [ 21 | # "ip=${config.inventory.ipAllocation."${config.networking.hostName}".home.primary.address}::${config.inventory.networks.home.gateway}:255.255.255.0::enp2s0:off" 22 | # "nameserver=${lib.head config.inventory.networks.home.dns}" 23 | # ]; 24 | 25 | boot.initrd.systemd.services.initrd-decrypt-ssh-host-keys = { 26 | description = "Decrypts SSH host keys to use in initrd"; 27 | requiredBy = [ "sshd.service" ]; 28 | requires = [ 29 | "dev-tpmrm0.device" 30 | ]; 31 | after = [ 32 | "dev-tpmrm0.device" 33 | ]; 34 | before = [ 35 | "sshd.service" 36 | "shutdown.target" 37 | ]; 38 | conflicts = [ "shutdown.target" ]; 39 | unitConfig.DefaultDependencies = false; 40 | 41 | script = '' 42 | set -x 43 | clevis decrypt < /etc/ssh/ssh_host_rsa_key.jwe > /etc/ssh/ssh_host_rsa_key || true 44 | # clevis returns non-zero even on success 45 | if [[ ! -s /etc/ssh/ssh_host_rsa_key ]]; then 46 | echo "Failed to decrypt /etc/ssh/ssh_host_rsa_key.jwe" 47 | exit 1 48 | fi 49 | chmod 0600 /etc/ssh/ssh_host_rsa_key 50 | 51 | clevis decrypt < /etc/ssh/ssh_host_ed25519_key.jwe > /etc/ssh/ssh_host_ed25519_key || true 52 | # clevis returns non-zero even on success 53 | if [[ ! -s /etc/ssh/ssh_host_ed25519_key ]]; then 54 | echo "Failed to decrypt /etc/ssh/ssh_host_ed25519_key.jwe" 55 | exit 1 56 | fi 57 | chmod 0600 /etc/ssh/ssh_host_ed25519_key 58 | ''; 59 | 60 | serviceConfig = { 61 | Type = "oneshot"; 62 | RemainAfterExit = true; 63 | }; 64 | }; 65 | 66 | boot.initrd.network.ssh.extraConfig = '' 67 | Hostkey /etc/ssh/ssh_host_rsa_key 68 | Hostkey /etc/ssh/ssh_host_ed25519_key 69 | TrustedUserCaKeys /etc/ssh/trusted_user_ca_keys 70 | ''; 71 | 72 | boot.initrd.network.ssh.enable = true; 73 | boot.initrd.network.ssh.ignoreEmptyHostKeys = true; 74 | }; 75 | } 76 | -------------------------------------------------------------------------------- /secrets/nix-cache/secrets.yaml: -------------------------------------------------------------------------------- 1 | tailscale-auth: ENC[AES256_GCM,data:ZVjLwFgPEIMTVxEgJsxZ0JNPKpuZqlOHF0+CN2Ceoy3Eqf93LYAv1bvV9IHhFiq5DvhD+tD0TdrjXFBp4Q==,iv:NcmzID0LxnVNcX41JRq+tUymJlyhxeoUirTmvqmRwhc=,tag:t7Z2rzfQH49lUrSUe++TZg==,type:str] 2 | nixos-config-runner-token: ENC[AES256_GCM,data:iiZtzxnAtfUrNse55KYzhbDjQWXDEIfi/Iw4U/VxI6Kk7U3v/lDZhQ==,iv:bLisTpzvPyV1spxCh4MpsUkXbT3Nih2BYq2ZRkIJLHQ=,tag:sdx1uBjyKbrgp/mTNdKQ2A==,type:str] 3 | sops: 4 | age: 5 | - recipient: age1ec9gz6f685mn8pdrz48qm4vq7qzpzcz48vgkcty9aa665evra9lq2uz3s2 6 | enc: | 7 | -----BEGIN AGE ENCRYPTED FILE----- 8 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBneXdUM01CQk5oNUdxb0Ey 9 | VzRrMDM3TWp1QnpGYnVTWk5TMFRVSFZHOFYwCm45WkVteEpnZUZqbU5FZFZBZk5Y 10 | Wm5VVXU4NE1YSnF4UFk1ME1ZUnFPdVEKLS0tIHNTeXJ3SllXNmsyenI5d1hjK0pX 11 | ZnUrUHlJWTkwNzJyelFlZEVPUTRHVzAKi/8hmknZkbR3FVK4rChZBf8YgNG5368/ 12 | dsr0ELvvi3T0G/1/knKZDM1OJs14d0gHU7N3Vsw2poimUYIgT3XCsw== 13 | -----END AGE ENCRYPTED FILE----- 14 | - recipient: age1dvu92xmh3w23ghey4l0epcs27m89f3nswzs5twgnel36xxu42f4sfsv023 15 | enc: | 16 | -----BEGIN AGE ENCRYPTED FILE----- 17 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB2LzVnL2hTRTJmY1F5Qkl4 18 | clNWbWlCa2NBdVJGbWxRa21MTXYxUWNoQ0ZRCnpHUGZ4RFMwUkxLbFRNYjJ5Zlhr 19 | QXJXOFcxeTZIUUZlSS9GTmMrTU1JQUUKLS0tIFNGRVErZkttZ1FaekR3T1hGOVlO 20 | NXFaZDVPMU81U3hJRWswOGZVaUM1bDgKhbFZ6T5kGC9z/8UjBMeEqYxlEO/P/Auq 21 | Ba95SJX8YJy+xSZSm2jdGg5xXtpf+VJgItKXegM5DraP3kDbBZoVag== 22 | -----END AGE ENCRYPTED FILE----- 23 | - recipient: age1a4lwvtxlnz8vzhqva8cwvrvkumjdzeau6rmrwg0xu7tseyxe33zs5sw04d 24 | enc: | 25 | -----BEGIN AGE ENCRYPTED FILE----- 26 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAzTDY1bmZFMjl1ODJnRTlk 27 | ZUNobkZPMStwK0NtMit4eG9FR3gyRS91RUhnCjRvSmFlZlh6azArYXpjY0xzL3Ux 28 | ZUNqSm45TGdHK2l3dy94UE4xV1BVVkkKLS0tIDhlTkkxaG9IWlNXNFNRU3hiUVdU 29 | WkZXSTEyL08wRzZQVFVpSWVYY0ZFdTAKdE45piyqub8TrrP8cA5fC/Zl6RpFFx0i 30 | cbKoNsS+0OXmSuq5XWwpht6b0rJYiRV90J83EXAUudBjQOv5938Z0A== 31 | -----END AGE ENCRYPTED FILE----- 32 | lastmodified: "2025-01-16T18:44:19Z" 33 | mac: ENC[AES256_GCM,data:tgLUCp1Ox9IW7VDZ7ij6JdryOYwHLyuPLNGPHcDswFIkFVVEglM9fk74ho1RjDePjShsSgiQpZTHNBY4Pwb6DoUI30GlqKskMhRCL8u6XIEW4Sa5ffR/RNXikmvUmmDel0K2MuqBMYO60XY3qqyfWhTq85VRcREXbgaBPGt65MM=,iv:O2DAmaCs1Y3TVwUOoJkPoaNdLMpSePm3MK1HMiDV02M=,tag:c+zhg9VWax+QsHhCrwbX3g==,type:str] 34 | pgp: 35 | - created_at: "2025-12-01T13:09:32Z" 36 | enc: |- 37 | -----BEGIN PGP MESSAGE----- 38 | 39 | hF4DjUagJu2XQwUSAQdA41JuUosk0McUW5apsco7MjVHBj07W3h8fHNPKNLVfE4w 40 | zZ6/NpjT01qowIWWvDEdt9b6GSwe+XKvrljwxfFHLYBQKHqwId8yGs+rBsA/6J8T 41 | 0l4BElomJwa/Ytp6He07/4Pw1/Vih8qfqBqlGd2f+cJhEadLv2p2megy6iAvRt6s 42 | p1ivSevj6nEPLFNqri5rLPMh/rmK3+CZ2/VayeC9tR+zWS9H9ik4d+VAbMLKORzs 43 | =Doqh 44 | -----END PGP MESSAGE----- 45 | fp: 7668F12EF30BE2A802E1E13E6D4365C3B5E703F5 46 | unencrypted_suffix: _unencrypted 47 | version: 3.9.2 48 | -------------------------------------------------------------------------------- /modules/machines/demandred.nix: -------------------------------------------------------------------------------- 1 | { 2 | self, 3 | inputs, 4 | ... 5 | }: 6 | { 7 | flake.nixosConfigurations.demandred = inputs.nixpkgs.lib.nixosSystem { 8 | system = "x86_64-linux"; 9 | modules = [ 10 | self.nixosModules.demandred-configuration 11 | ]; 12 | }; 13 | 14 | flake.nixosModules.demandred-configuration = 15 | { 16 | lib, 17 | pkgs, 18 | modulesPath, 19 | ... 20 | }: 21 | { 22 | key = "nixos-config.modules.nixos.demandred-configuration"; 23 | imports = [ 24 | self.nixosModules.baseline 25 | 26 | (modulesPath + "/installer/scan/not-detected.nix") 27 | inputs.disko.nixosModules.default 28 | "${self}/machines/demandred/disko.nix" 29 | 30 | self.nixosModules.default 31 | self.nixosModules.binarin-workstation 32 | self.nixosModules.nix 33 | self.nixosModules.kanata 34 | self.nixosModules.niri 35 | self.nixosModules.bluetooth 36 | self.nixosModules.firefox 37 | self.nixosModules.inventory-legacy 38 | self.nixosModules.impure-nix-setup 39 | self.nixosModules.large-console-fonts 40 | ]; 41 | config = { 42 | nixos-config.export-metrics.enable = false; 43 | networking.hostName = "demandred"; 44 | 45 | impermanence.enable = true; 46 | 47 | system.stateVersion = "24.11"; 48 | 49 | boot.initrd.availableKernelModules = [ 50 | "xhci_pci" 51 | "ehci_pci" 52 | "ahci" 53 | "usb_storage" 54 | "sd_mod" 55 | "sr_mod" 56 | "sdhci_pci" 57 | ]; 58 | boot.initrd.kernelModules = [ "dm-snapshot" ]; 59 | boot.kernelModules = [ "kvm-intel" ]; 60 | boot.extraModulePackages = [ ]; 61 | 62 | boot.initrd.systemd.enable = true; 63 | boot.loader.systemd-boot.enable = true; 64 | boot.loader.efi.canTouchEfiVariables = true; 65 | 66 | services.tailscale.authKeyFile = lib.mkForce null; 67 | boot.initrd.luks.devices.demandred-lvm.crypttabExtraOpts = [ 68 | "fido2-device=auto" 69 | "token-timeout=10s" 70 | ]; 71 | 72 | fileSystems."/persist".neededForBoot = true; 73 | fileSystems."/local".neededForBoot = true; 74 | 75 | networking.networkmanager.enable = true; 76 | networking.useDHCP = lib.mkDefault true; 77 | 78 | hardware.cpu.intel.updateMicrocode = lib.mkDefault true; 79 | 80 | users.users.root.initialHashedPassword = "$7$CU..../....2tYl/rrPqgcDE/0wbfkSR/$BDDtkNKdAi/yfv3P7ETmpoCKBxfHdiRIM8B4K8nFuB3"; 81 | users.users.binarin.initialHashedPassword = "$7$CU..../....w.WruOOmFL2KKwVMMMysm1$OxbByS3HBVRsOdmYlqBtpivURr1QWVBVf87M1gXAEQC"; 82 | 83 | services.kanata.keyboards.all.devices = [ 84 | "/dev/input/by-path/platform-i8042-serio-0-event-kbd" 85 | ]; 86 | 87 | environment.systemPackages = with pkgs; [ 88 | distrobox 89 | ]; 90 | 91 | virtualisation.docker = { 92 | enable = true; 93 | storageDriver = "btrfs"; 94 | }; 95 | 96 | services.displayManager.defaultSession = lib.mkForce "niri-uwsm"; 97 | }; 98 | }; 99 | } 100 | -------------------------------------------------------------------------------- /secrets/forgejo/secrets.yaml: -------------------------------------------------------------------------------- 1 | tailscale-auth: ENC[AES256_GCM,data:e2vX6Hl6QECdmpkCR8b7gAM6XVOhxcrb7Ku58dCmxAojKlmwIV82SzxrnN2G7qNveTbq039q5mAMMahtpio=,iv:Jeprze2tKGKmhwsIXM9+v+OOqKAEjoP7TekxfPD44cY=,tag:Zs4SKzoTXaV8d+iB+SyAsQ==,type:str] 2 | smtp2go-password: ENC[AES256_GCM,data:CxQRwScbKMz6NWZMTu5e/w==,iv:+Thcfl+gHcmf0B3wnGwJhNpZXd9VTVpkVDQyZAuFiks=,tag:xx7uZ6coo4CtejXZYtuQaA==,type:str] 3 | smtp2go-username: ENC[AES256_GCM,data:rUsmI0DV/mTpdfUItEs0zPOH6eehniS1AB8=,iv:dbPv5WhMdVMJU3G56zR0KLao8oI6z3BJcucRz/cUfig=,tag:yrjq0a+ZgqXDNnqje9X91Q==,type:str] 4 | sops: 5 | age: 6 | - recipient: age1ec9gz6f685mn8pdrz48qm4vq7qzpzcz48vgkcty9aa665evra9lq2uz3s2 7 | enc: | 8 | -----BEGIN AGE ENCRYPTED FILE----- 9 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBubURqWllvVEFSM05xcnBW 10 | b1VNQmgzR2lTTVJLUUdveGNVaVdFaEt2U240CnVlOUxiZ05aWGU2UEdTY2FnUVpK 11 | TDVnOHJ0QXY5bW5hUDhqRVhueVRzRmMKLS0tIHZubmxVb2dWcGQ4TFduVVByblZu 12 | allVTUM2c0Z2Mm9ZSmxyQ08wQWxXVlkK8B+3KLnRILxfIofqTY3eMwxw5eEoTD0b 13 | IWZxj4ltp7MJv52KKvrYBVxqJdH6QXZJsCUKD3RTkY5yY2HqB+zamA== 14 | -----END AGE ENCRYPTED FILE----- 15 | - recipient: age1dvu92xmh3w23ghey4l0epcs27m89f3nswzs5twgnel36xxu42f4sfsv023 16 | enc: | 17 | -----BEGIN AGE ENCRYPTED FILE----- 18 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA0SU05dGNOZFJIUWVPTWov 19 | VHllM0NxMGNic0NVeElnNmd4Ni9zTkJEZ1d3ClNvZ0hIejM2Y2gyRE5oODcrdHVR 20 | SVlwVCtNbWsrMFpVWFRobnZKejRlTXcKLS0tIHNKNkZzQ0hHKzQ2VFNEdmRZUlZx 21 | anBIa1Z0YWI3NVBGZS8wK3dXQ3VzTVEKNQ7L8FFW2wNfPQOWDxOXGDZoLuG8onJT 22 | SQ8hVnBki1YvoJC6uSGf9B68bBaU4ULBwfSoK2UvyckP/5hecN/7OQ== 23 | -----END AGE ENCRYPTED FILE----- 24 | - recipient: age17jnj7kvw0wn0nfk02wll05yhe8xnu86u489tfks6hgnd4gfs4f2qy9chtv 25 | enc: | 26 | -----BEGIN AGE ENCRYPTED FILE----- 27 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBZY0FvZWVNR3NsRVNiUGJI 28 | dllBQnY1bkdtNm5Rb2kyRGg0QlNuZzhyUTNZCkswK1A0ckVUNDNwSWZBTWxTbi8y 29 | Wkp5eTZpUnNZY3p4d1NRTlFtUHpUbVEKLS0tIHp3akRBREFaZHVacGxHdDFBYURX 30 | L1poMmRsek52aHBpRUFsMDlnLyt0M00K27iDPeQGB6vMlCY9oTLvwvv+42BqtzIO 31 | dsTJbhtfRL8dSdMpcogS+1CPxX4PiBTIpO375Si8p2K+tHTGFzde5g== 32 | -----END AGE ENCRYPTED FILE----- 33 | lastmodified: "2024-10-25T10:38:17Z" 34 | mac: ENC[AES256_GCM,data:YTJZfgHFDB3dAJLhWGoPyNg3ihh9l7TX2zdlYxhBzaNBdzkukptBwofvwbSKoYd5QLk+gMdaCUV1FYnsFAwHin+2HxLNBTpC/dxHiNf4r/ScuFh7ha2HZrSNlPtbKlVy41GqH95esFYnnKEFs7+F6hXnoN3c8Bx1fdez528C+08=,iv:Y2ImQ8bCoYfJbT3z9hIPOvrz2ocITLzURjNDPnvct44=,tag:14N+VscghjJU/c2pW1zyFQ==,type:str] 35 | pgp: 36 | - created_at: "2025-12-01T13:09:32Z" 37 | enc: |- 38 | -----BEGIN PGP MESSAGE----- 39 | 40 | hF4DjUagJu2XQwUSAQdA5CYgsd4Ngjs8m2xeuzBt/PZTUrI9/RqjxIjwWO1ekAYw 41 | m23euaE5ygmBkthJaZ8Zxx1NL+a6W6UqnoGknKuSPD0CA9EeQnKIcgEdwCm81ZT9 42 | 0lwBL9kI38a/aXtDcywhHUHNayOrJXB37HjnlmgOtnokYXKrSbS0CJ89HSQxasiN 43 | uyV/l8dD+QpudNT4VglXCnjHpvFK/tnDw+QEziCZnxSuitzqeGENkIy1FQBQhg== 44 | =YPUw 45 | -----END PGP MESSAGE----- 46 | fp: 7668F12EF30BE2A802E1E13E6D4365C3B5E703F5 47 | unencrypted_suffix: _unencrypted 48 | version: 3.8.1 49 | -------------------------------------------------------------------------------- /ansible/roles/sshd/tasks/main.yml: -------------------------------------------------------------------------------- 1 | #SPDX-License-Identifier: MIT-0 2 | --- 3 | - name: Creates centrally-managed authorized_keys.d directory 4 | file: 5 | path: "{{ authorized_keys_path }}" 6 | state: directory 7 | 8 | - name: Creates centrally-managed authorized_principals.d directory 9 | file: 10 | path: "{{ authorized_principals_path }}" 11 | state: directory 12 | 13 | - name: Provision authorized_keys for users 14 | template: 15 | src: authorized_keys.j2 16 | dest: "{{ authorized_keys_path }}/{{ username }}" 17 | loop: "{{ ['root'] + sshd_users }}" 18 | loop_control: 19 | loop_var: username 20 | 21 | - name: Provision authorized_principals for root 22 | template: 23 | src: authorized_principals.j2 24 | dest: "{{ authorized_principals_path }}/root" 25 | vars: 26 | ssh_principals: "{{ sshd_root_principals }}" 27 | 28 | - name: Ensure that /etc/ssh/sshd_config.d/*.conf is sourced by sshd 29 | when: sshd_include_config_d 30 | lineinfile: 31 | path: "{{ sshd_config_path }}" 32 | line: 'Include {{ sshd_config_d_path }}/*.conf' 33 | insertbefore: BOF 34 | notify: restart sshd 35 | 36 | - name: Create /etc/ssh/trusted_user_ca_keys 37 | template: 38 | src: trusted_user_ca_keys.j2 39 | dest: "{{ trusted_user_ca_keys_path }}" 40 | owner: root 41 | group: root 42 | mode: "0644" 43 | 44 | - name: Create /etc/ssh/sshd_config.d/10-trusted_user_ca_keys.conf 45 | copy: 46 | content: "TrustedUserCaKeys {{ trusted_user_ca_keys_path }}\n" 47 | dest: "{{ sshd_config_d_path }}/10-trusted_user_ca_keys.conf" 48 | force: yes 49 | notify: restart sshd 50 | 51 | - name: Only allow system-defined authorized_keys 52 | notify: restart sshd 53 | lineinfile: 54 | path: "{{ sshd_config_path }}" 55 | line: "AuthorizedKeysFile {{ authorized_keys_path }}/%u" 56 | regex: '^\s*#*\s*AuthorizedKeysFile' 57 | 58 | - name: Only allow system-defined authorized_principals 59 | notify: restart sshd 60 | lineinfile: 61 | path: "{{ sshd_config_path }}" 62 | line: "AuthorizedPrincipalsFile {{ authorized_principals_path }}/%u" 63 | regex: '^\s*#*\s*AuthorizedPrincipalsFile' 64 | 65 | - name: Disable password-based authentication 66 | notify: restart sshd 67 | block: 68 | - name: Set PermitRootLogin 69 | lineinfile: 70 | path: "{{ sshd_config_path }}" 71 | line: "PermitRootLogin {{ sshd_permit_root_login }}" 72 | regex: '^\s*#*\s*PermitRootLogin' 73 | 74 | - name: Disable PasswordAuthentication 75 | lineinfile: 76 | path: "{{ sshd_config_path }}" 77 | line: "PasswordAuthentication {{ 'yes' if sshd_password_authentication else 'no' }}" 78 | regex: '^\s*#*\s*PasswordAuthentication\s+(yes|no)' 79 | 80 | - name: Disable KbdInteractiveAuthentication 81 | lineinfile: 82 | path: "{{ sshd_config_path }}" 83 | line: "KbdInteractiveAuthentication {{ 'yes' if sshd_kbd_interactive_authentication else 'no' }}" 84 | regex: '^\s*#*\s*KbdInteractiveAuthentication\s+(yes|no)' 85 | 86 | - name: Configure UsePAM 87 | lineinfile: 88 | # Or change /etc/pam.d/sshd not to include 'common-auth'/'pam_unix' 89 | # usermod -p '*' username is needed if the user doesn't have a password 90 | path: "{{ sshd_config_path }}" 91 | line: "UsePAM {{ 'yes' if sshd_use_pam else 'no' }}" 92 | regex: '^\s*#*\s*UsePAM' 93 | 94 | 95 | -------------------------------------------------------------------------------- /secrets/monitor/secrets.yaml: -------------------------------------------------------------------------------- 1 | tailscale-auth: ENC[AES256_GCM,data:gv5kc9izHf94kMuqEMmPPgREa4s6pof0aTxVpBcFMjjdIaHdLOMetaEhv55+mexGs0U4DCtQpQAa9uXUoQ==,iv:ZkB+Zbmu6om9zMt/0MBWbS+FakLxblwwWBLKFkh+QXs=,tag:8Rvf9r79+VR4USxuSPsY6g==,type:str] 2 | grafana: 3 | admin-username: ENC[AES256_GCM,data:UnEWL8s=,iv:eOPuZskXyoC67YpzXGF2G6S6oZQ4aTfr62QTjhyIBn0=,tag:KMaVCQ4zGUDCs0go8DiDig==,type:str] 4 | admin-password: ENC[AES256_GCM,data:ZToMHCMR5c9n0raStOSvcPIyxXuDfb4OcA==,iv:XQWBG0agIS51BhHJ7BQIwAcyznaBlPTFAdeCx0Eqww0=,tag:anQms5d8OXrvnvShXQN2NA==,type:str] 5 | secret-key: ENC[AES256_GCM,data:dJ3lIzfTMPAYPMdST06aA//07XK21kWV1jf4Eb7ioTqngBbbKWg2Ig==,iv:9EQAUhymzEbIZwgFmK3IoG0tHWPqcVHIWgwxGHF/Wsg=,tag:jSNjxo0nFt8eXwJgngJoWQ==,type:str] 6 | sops: 7 | age: 8 | - recipient: age1ec9gz6f685mn8pdrz48qm4vq7qzpzcz48vgkcty9aa665evra9lq2uz3s2 9 | enc: | 10 | -----BEGIN AGE ENCRYPTED FILE----- 11 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBGNGF0dEtjOVhuVys4T0s4 12 | d21DemJiNkhMVHcrSVh1SlZ3ZGRMNUhKL1hzCmdndjJZU2FGK0pqVVh1VFVGdUx1 13 | UUVaMDljTXNiWVVlM3Q1SW5xUWJsdkEKLS0tIHpyUlA0ZDBMMHFubzhUWk40WUpl 14 | dEJ0MUo1bkdseGtQTmtiRzR4MTJXaTgKPdZ2KcwZD+87xELJTkuZ+j3k2uCKW9X2 15 | MQlqxG5Z0hohIg8ynsoy8syEWxXsaCFw1qRUi6Kga6xVMqHAQXrMOQ== 16 | -----END AGE ENCRYPTED FILE----- 17 | - recipient: age1dvu92xmh3w23ghey4l0epcs27m89f3nswzs5twgnel36xxu42f4sfsv023 18 | enc: | 19 | -----BEGIN AGE ENCRYPTED FILE----- 20 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSByaXB5ZXpaOXFkSHo0aGlr 21 | V2NUK1FUSnZHUEF6SkpmUTVGZXJXcDl2ZFhvCmIrdDFIdmNvQ1AvbjdENkk5Ri9H 22 | ZHlzTGRmelUrWkxvby9RU0RYSHJVVzAKLS0tIDF3TXZLbFlhRkpLNnU1cy9QK1V2 23 | MC9sM3JzNW9TaDJsRmpURnQ3dGVVeHcKYoTdYahPeUBhqTV674ZehrQSOF5JBctH 24 | rhoXcyWdZANEek9mvJmqUcZPCgj+EV2x1gcRjyKZwAk5gfaHJlcCSg== 25 | -----END AGE ENCRYPTED FILE----- 26 | - recipient: age1y74a28z6tpym2c0wpqnhwaegqlns8z3t286wxjhms86vxdttnvws2lev3r 27 | enc: | 28 | -----BEGIN AGE ENCRYPTED FILE----- 29 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBhZGlSV2IvSEhsL3ZKekFz 30 | Zk1QczNvNnF4dkdqaUQvSHRDV0Q0NGlvWWcwClo3RkYwMVRUaEdkS1FQVVdibTJ4 31 | ZHBLWVZrcTFnaEVicTVUVjltOXJWVDAKLS0tIEpQTzZXazlib1FrbkFZNHVLSi9T 32 | enBFZ0VrUUd6UDdBQmFOOTdqelV0MW8KuoJLQVQLJ5D2xRMYNXNiTGVOoDtb2otf 33 | 3RvH6YJeRuaXR8oRnBboettfay+eZPNKU07COLSQs9EpwwaMcDh8pQ== 34 | -----END AGE ENCRYPTED FILE----- 35 | lastmodified: "2024-10-24T10:51:47Z" 36 | mac: ENC[AES256_GCM,data:Wgb0r4MKfIYrR37qHBkkzBl1DGBVCJ3sGyqk8c3AFQJdMvFvd1v04SQ/chrKbcoWGfjp1134+jtTPWTCzF7KUwVNLLPS7sLMGIRLA/lLyNOIMiKA01pu/UbWciZiYbz9d9Y3Wetlz7JqpBWq4K6po8TpKI2IH4D1AUnOQP/iepQ=,iv:aV54n4xfYDpoRbqjkQUfKGZ/8OyV0tf2bT+XWQBx5bE=,tag:LDv2+MEtWtPT3G5/kJxw1A==,type:str] 37 | pgp: 38 | - created_at: "2025-12-01T13:09:32Z" 39 | enc: |- 40 | -----BEGIN PGP MESSAGE----- 41 | 42 | hF4DjUagJu2XQwUSAQdAjEvF3d//Q3nywTMfrnGKPQrTFWaXBFd1BVQZ1w24aS8w 43 | A0GVY54FATjUgskdDWqnVAISssw/xTx1SAoSoe3xc6IDpSSJ0HfbnxcZ43dHJAxk 44 | 0lEBDuqiLOGEKW+n49rgH0kTEYaa3z2i/t665E36Rc2jtlFJRyZM6Dz6E6iHirPG 45 | 3JuzQLUqZA6iEt6AwGXmci7rqaNGLKpCCVPG1CLG8usam7Y= 46 | =LTXQ 47 | -----END PGP MESSAGE----- 48 | fp: 7668F12EF30BE2A802E1E13E6D4365C3B5E703F5 49 | unencrypted_suffix: _unencrypted 50 | version: 3.8.1 51 | -------------------------------------------------------------------------------- /machines/qdevice/disko.nix: -------------------------------------------------------------------------------- 1 | { lib, ... }: 2 | { 3 | disko.devices = { 4 | disk = { 5 | main = { 6 | device = "/dev/disk/by-id/nvme-WD_Red_SN700_1000GB_241759800883"; 7 | type = "disk"; 8 | content = { 9 | type = "gpt"; 10 | partitions = { 11 | ESP = { 12 | type = "EF00"; 13 | size = "1G"; 14 | content = { 15 | type = "filesystem"; 16 | format = "vfat"; 17 | mountpoint = "/boot"; 18 | mountOptions = [ 19 | "umask=0077" 20 | "nofail" 21 | ]; 22 | }; 23 | }; 24 | luks = { 25 | size = "100%"; 26 | content = { 27 | type = "luks"; 28 | name = "luks1"; 29 | passwordFile = lib.mkForce "/tmp/password-without-newline"; 30 | settings = { 31 | allowDiscards = true; 32 | }; 33 | content = { 34 | type = "zfs"; 35 | pool = "rpool"; 36 | }; 37 | }; 38 | }; 39 | }; 40 | }; 41 | }; 42 | }; 43 | zpool = { 44 | rpool = { 45 | type = "zpool"; 46 | options = { 47 | ashift = "12"; 48 | autotrim = "on"; 49 | }; 50 | 51 | rootFsOptions = { 52 | mountpoint = "/"; 53 | acltype = "posixacl"; 54 | xattr = "sa"; 55 | dnodesize = "auto"; 56 | compression = "lz4"; 57 | normalization = "formD"; 58 | relatime = "on"; 59 | canmount = "off"; 60 | }; 61 | 62 | datasets = { 63 | "ROOT" = { 64 | type = "zfs_fs"; 65 | options = { 66 | canmount = "off"; 67 | mountpoint = "none"; 68 | }; 69 | }; 70 | "ROOT/nixos" = { 71 | type = "zfs_fs"; 72 | options = { 73 | canmount = "noauto"; 74 | mountpoint = "/"; 75 | }; 76 | mountpoint = "/"; 77 | postCreateHook = "zfs list -t snapshot -H -o name | grep -E '^rpool/ROOT/nixos@blank$' || zfs snapshot rpool/ROOT/nixos@blank"; 78 | }; 79 | "var" = { 80 | type = "zfs_fs"; 81 | options = { 82 | canmount = "off"; 83 | }; 84 | }; 85 | "var/lib" = { 86 | type = "zfs_fs"; 87 | options = { 88 | canmount = "off"; 89 | }; 90 | }; 91 | "var/lib/docker" = { 92 | type = "zfs_fs"; 93 | options = { 94 | "com.sun:auto-snapshot" = "false"; 95 | }; 96 | mountpoint = "/var/lib/docker"; 97 | }; 98 | "var/log" = { 99 | type = "zfs_fs"; 100 | mountpoint = "/var/log"; 101 | }; 102 | "nix" = { 103 | type = "zfs_fs"; 104 | mountpoint = "/nix"; 105 | }; 106 | "persist" = { 107 | type = "zfs_fs"; 108 | mountpoint = "/persist"; 109 | }; 110 | "local" = { 111 | type = "zfs_fs"; 112 | mountpoint = "/local"; 113 | }; 114 | }; 115 | }; 116 | }; 117 | }; 118 | } 119 | -------------------------------------------------------------------------------- /machines/furfur/disko.nix: -------------------------------------------------------------------------------- 1 | { lib, ... }: 2 | { 3 | disko.devices = { 4 | disk = { 5 | main = { 6 | device = "/dev/disk/by-id/nvme-HFM001TD3GX013N-SKhynix_FSB3N697910303Q6C"; 7 | type = "disk"; 8 | content = { 9 | type = "gpt"; 10 | partitions = { 11 | ESP = { 12 | type = "EF00"; 13 | size = "1G"; 14 | content = { 15 | type = "filesystem"; 16 | format = "vfat"; 17 | mountpoint = "/boot"; 18 | mountOptions = [ 19 | "umask=0077" 20 | "nofail" 21 | ]; 22 | }; 23 | }; 24 | 25 | luks = { 26 | size = "100%"; 27 | content = { 28 | type = "luks"; 29 | name = "luks1"; 30 | passwordFile = lib.mkForce "/tmp/deploy-luks-password-without-newline"; 31 | settings = { 32 | allowDiscards = true; 33 | }; 34 | content = { 35 | type = "zfs"; 36 | pool = "rpool"; 37 | }; 38 | }; 39 | }; 40 | }; 41 | }; 42 | }; 43 | }; 44 | 45 | zpool = { 46 | rpool = { 47 | type = "zpool"; 48 | options = { 49 | ashift = "12"; 50 | autotrim = "on"; 51 | }; 52 | 53 | rootFsOptions = { 54 | mountpoint = "/"; 55 | acltype = "posixacl"; 56 | xattr = "sa"; 57 | dnodesize = "auto"; 58 | compression = "lz4"; 59 | normalization = "formD"; 60 | relatime = "on"; 61 | canmount = "off"; 62 | }; 63 | 64 | datasets = { 65 | "ROOT" = { 66 | type = "zfs_fs"; 67 | options = { 68 | canmount = "off"; 69 | mountpoint = "none"; 70 | }; 71 | }; 72 | "ROOT/nixos" = { 73 | type = "zfs_fs"; 74 | options = { 75 | canmount = "noauto"; 76 | mountpoint = "/"; 77 | }; 78 | mountpoint = "/"; 79 | postCreateHook = "zfs list -t snapshot -H -o name | grep -E '^rpool/ROOT/nixos@blank$' || zfs snapshot rpool/ROOT/nixos@blank"; 80 | }; 81 | "var" = { 82 | type = "zfs_fs"; 83 | options = { 84 | canmount = "off"; 85 | }; 86 | }; 87 | "var/lib" = { 88 | type = "zfs_fs"; 89 | options = { 90 | canmount = "off"; 91 | }; 92 | }; 93 | "var/lib/docker" = { 94 | type = "zfs_fs"; 95 | options = { 96 | "com.sun:auto-snapshot" = "false"; 97 | }; 98 | mountpoint = "/var/lib/docker"; 99 | }; 100 | "var/log" = { 101 | type = "zfs_fs"; 102 | mountpoint = "/var/log"; 103 | }; 104 | "nix" = { 105 | type = "zfs_fs"; 106 | mountpoint = "/nix"; 107 | }; 108 | "persist" = { 109 | type = "zfs_fs"; 110 | mountpoint = "/persist"; 111 | }; 112 | "local" = { 113 | type = "zfs_fs"; 114 | mountpoint = "/local"; 115 | }; 116 | }; 117 | }; 118 | }; 119 | }; 120 | } 121 | -------------------------------------------------------------------------------- /secrets/mail/secrets.yaml: -------------------------------------------------------------------------------- 1 | tailscale-auth: ENC[AES256_GCM,data:vhx247SYXyfLNUNklBHNiGM7Jlc11goi48ACDyHznPinqg7dTcPllYfA4pn50/p9pQqO+2GBTWjyYlrqfQ==,iv:wks/hm7Zf7HlFiP8dCo+MujA0DES0vciNciFLgq2d3Q=,tag:wmfgYxOv/R+bIrjmbpeqcg==,type:str] 2 | mail-gpg-password: ENC[AES256_GCM,data:RKDq2LQ8G1/Y5/qM0+gdDJiIFAttZ4wvHDlmYrpg41B49rPDQ3sqrPJFgWTOK/IeFcepFXtLezgP,iv:6hRDSS39Lz2SJoqtxQ6IqDw1/3ViKLCUrP4yZ4DfEWA=,tag:H+ZJDG1FKJ6FDh87zZ/g3w==,type:str] 3 | imapsync: 4 | host1-password: ENC[AES256_GCM,data:SIpj+V5SiyN3XZ6GUguhA8WcplnUhQ==,iv:YpfQXCuOfCfPFGxDlT2dOUsnuqb6UnLL0oAGas7NZW0=,tag:GaRC45+dsYm+VB2gXM0L/g==,type:str] 5 | host2-password: ENC[AES256_GCM,data:wXyJcSgA3IRj5mQ6Al4ZXLA8rncA9qXXRWASAkm9NqnUmiHxdYEA+jubFNi6ikq9ghE=,iv:utq8QkxZkRo/CLuxJtPYspO/ew/9Pk6Iqg0grLlm+JI=,tag:XkbCqefiA5RDL6UxArt2TA==,type:str] 6 | sops: 7 | age: 8 | - recipient: age1ec9gz6f685mn8pdrz48qm4vq7qzpzcz48vgkcty9aa665evra9lq2uz3s2 9 | enc: | 10 | -----BEGIN AGE ENCRYPTED FILE----- 11 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBYY01MQ2E3MWNvUnFJSnBn 12 | Qlk4cVg2d0VQRkRzZjRIS0c2MmE1WlNhN1ZRCjM5dzFPaWlOczFjMWVFYnZoM211 13 | MmdKSjMvWlRmMlRmdFBVS0xBekFNRE0KLS0tIE5QbDFCd0k5Tm1OTitmWWVJWGZP 14 | REdjcm9LcEdZcE5neUQxZCt5cW1TVk0KS+mjZtR3PRjapo6lP5unX5UqW6FxP6TA 15 | BxE3SMwK8WIH2P4JadOdEi12ctPDAWMmHlXSVuZiYhJ/9C7lRhU4Bg== 16 | -----END AGE ENCRYPTED FILE----- 17 | - recipient: age1dvu92xmh3w23ghey4l0epcs27m89f3nswzs5twgnel36xxu42f4sfsv023 18 | enc: | 19 | -----BEGIN AGE ENCRYPTED FILE----- 20 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBvNy9sV202Z1d0TDY4Sisw 21 | RlU3Q2NzY0ZIdWZjY3RSNjdBckNtOFFkWWlVCmhrL1lnNlpzREw1cWtUbWZMdHIy 22 | S20zVWs2OXNpd25iMnNjNGNheDZuTVkKLS0tIEN6UWQwaUlSVU5HbHdGcFRXeVE4 23 | SFZWakhaaGNzRThBclFseFQzdnIvNGcKRks7tGyXhM6GQ/XX5puZZZPKCgHLgoh4 24 | E9jlGGhjIse6xCLUI5rKXnHIVaLBCxSyZ7HQ2SZrH1uSLjmyJpH3zg== 25 | -----END AGE ENCRYPTED FILE----- 26 | - recipient: age14n05u2z9u9h7chvn2rmmwr9flc25790rapxtvx97lqt78uk3ce3sy3xc6s 27 | enc: | 28 | -----BEGIN AGE ENCRYPTED FILE----- 29 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAxL1NFamMxWThreWoxanF5 30 | Qi9vRkxVSzFsV1FUV3p4OU4wSGpXaG1KbEFBCmNEa091NkZUTVFxWHAzL2lXWWxz 31 | SHlJTFFFMGVZd29mcGJqVVEzbzVhZFEKLS0tIFlrMTMyUXkxdGNuNGl4c1FITDE1 32 | dEp5eGJuU3BpLzBUVkRUSk5kWmVBc3MKIBiphSAvO5oAabvWR2Ty8hPK1wPRtN/f 33 | FfDsyBXTbbnZbeoQjbNGp174v7Y1FrTvdSwF1eP8zAlg/TcFp2gliA== 34 | -----END AGE ENCRYPTED FILE----- 35 | lastmodified: "2024-12-15T19:08:30Z" 36 | mac: ENC[AES256_GCM,data:xFpB0b/e9g716b/XT2RSPXpltFHtu8fSsNR2s6jQfuWyxQxNMp8xlcIJzwTWqYajYCLMel4d7rPj+HmgiwDxpmcbmMPxOynm93GVcOhmej/re+cIK8blFP425lqip8puMb0e+45X4k1us84pU9XQxDbc+YwQs6YJBXNpr6NHCu4=,iv:q3dHX8eJZ+sBenFbGvkLVWhWYIpOTWZKscp/IhDdZvw=,tag:S7D4trbz1tI8Rtu4mQxmsw==,type:str] 37 | pgp: 38 | - created_at: "2025-12-01T13:09:32Z" 39 | enc: |- 40 | -----BEGIN PGP MESSAGE----- 41 | 42 | hF4DjUagJu2XQwUSAQdA3neStkCXx+m2rOr2x61wyy2srf5LJKdwJvHAuJ2FWCcw 43 | JYw8RMcw1YTfVTqnREs7ZWhM8ckcXBCnaxM7FnteQ5Ye85SC8d76W0h/rojOLcYf 44 | 0lwBxQc3mBlGu4F3fOkwfAaPevdBpGEMjYXwfhBIHUpDJfwqz+u95YfBeIcoqrxs 45 | MS5jvLckRJHZ8nAVjZ+UESVV0smmbq6KQ288oXI/vvvIxlRR1pDOKWd2obQuxA== 46 | =XdI8 47 | -----END PGP MESSAGE----- 48 | fp: 7668F12EF30BE2A802E1E13E6D4365C3B5E703F5 49 | unencrypted_suffix: _unencrypted 50 | version: 3.9.1 51 | --------------------------------------------------------------------------------