├── .envrc ├── config ├── modules │ ├── ui │ │ ├── eww │ │ │ ├── eww.yuck │ │ │ ├── images │ │ │ │ └── music.png │ │ │ ├── default.nix │ │ │ ├── close_dashboard │ │ │ ├── open_dashboard │ │ │ ├── scripts │ │ │ │ ├── mails │ │ │ │ ├── open_links │ │ │ │ ├── open_folders │ │ │ │ ├── open_apps │ │ │ │ ├── sys_info │ │ │ │ ├── music_info │ │ │ │ └── weather_info │ │ │ ├── launch_dashboard │ │ │ └── eww.scss │ │ ├── dmenu │ │ │ ├── emoji-prompt.sh │ │ │ ├── default.nix │ │ │ ├── kill-window-prompt.sh │ │ │ ├── exit-prompt.sh │ │ │ ├── screenshot-prompt.sh │ │ │ └── home.nix │ │ ├── git │ │ │ ├── default.nix │ │ │ ├── work-profile │ │ │ └── home.nix │ │ ├── st │ │ │ ├── default.nix │ │ │ ├── home.nix │ │ │ ├── 0001-Adds-Fira-Code-Font.patch │ │ │ ├── st-scrollback-mouse-20220127-2c5edf2.diff │ │ │ ├── 0003-Change-Font-Size-Shortcuts.patch │ │ │ ├── 0002-Adds-Sanity-Inc-Tomorrow-Eighties-Theme.patch │ │ │ ├── st-scrollback-mouse-altscreen-20220127-2c5edf2.diff │ │ │ └── st-blinking_cursor-20230819-3a6d6d7.diff │ │ ├── direnv │ │ │ ├── default.nix │ │ │ └── home.nix │ │ ├── dunst │ │ │ ├── default.nix │ │ │ └── home.nix │ │ ├── emacs │ │ │ ├── default.nix │ │ │ └── home.nix │ │ ├── starship │ │ │ ├── default.nix │ │ │ └── home.nix │ │ ├── zsh │ │ │ ├── default.nix │ │ │ └── home.nix │ │ ├── opengl │ │ │ └── default.nix │ │ ├── xserver │ │ │ ├── wallpapers │ │ │ │ ├── Vaporwave.jpg │ │ │ │ ├── Big_Sur_Simple.png │ │ │ │ └── Yosemite-Color-Block.png │ │ │ └── default.nix │ │ ├── fonts │ │ │ └── default.nix │ │ ├── audio │ │ │ └── default.nix │ │ ├── picom │ │ │ └── default.nix │ │ ├── ntfy │ │ │ └── home.nix │ │ └── kmonad │ │ │ ├── default.nix │ │ │ └── home.nix │ ├── system │ │ ├── users │ │ │ └── default.nix │ │ ├── nix-direnv │ │ │ └── default.nix │ │ ├── powertop │ │ │ └── default.nix │ │ ├── timezone │ │ │ └── default.nix │ │ ├── devices │ │ │ ├── bluetooth │ │ │ │ └── default.nix │ │ │ ├── touchpad │ │ │ │ └── default.nix │ │ │ └── udisk │ │ │ │ └── default.nix │ │ ├── nix-binary-caches │ │ │ ├── nix-linter.nix │ │ │ └── default.nix │ │ ├── systemd-boot │ │ │ └── default.nix │ │ └── nixpkgs │ │ │ └── default.nix │ ├── security │ │ ├── process-information-hiding │ │ │ └── default.nix │ │ ├── sshd │ │ │ ├── public-keys.nix │ │ │ └── default.nix │ │ └── gpg │ │ │ └── default.nix │ ├── packages │ │ ├── development.nix │ │ ├── pc-cli-tools.nix │ │ ├── server-cli-tools.nix │ │ ├── gui-applications.nix │ │ └── default.nix │ ├── services │ │ ├── virtualisation │ │ │ ├── virtualbox │ │ │ │ └── default.nix │ │ │ └── libvirt │ │ │ │ └── default.nix │ │ ├── tailscale │ │ │ └── default.nix │ │ ├── postgresql │ │ │ └── default.nix │ │ └── syncthing │ │ │ └── default.nix │ └── data │ │ └── session-vars │ │ └── default.nix ├── machines │ ├── servers │ │ ├── gnostic-ascension │ │ │ ├── public │ │ │ │ └── index.html │ │ │ ├── hardware.nix │ │ │ ├── default.nix │ │ │ ├── disk-config2.nix │ │ │ └── disk-config.nix │ │ ├── test-vm │ │ │ ├── default.nix │ │ │ ├── hardware.nix │ │ │ └── disk-config.nix │ │ ├── accompaniment-of-shadows │ │ │ ├── sabnzbd.nix │ │ │ ├── redis.nix │ │ │ ├── tt-rss.nix │ │ │ ├── tailscale.nix │ │ │ ├── lubelogger.nix │ │ │ ├── filebrowser.nix │ │ │ ├── observability.nix │ │ │ ├── paperless-ngx.nix │ │ │ ├── homebox.nix │ │ │ ├── hardware.nix │ │ │ ├── default.nix │ │ │ ├── planka.nix │ │ │ ├── postgresql.nix │ │ │ ├── servarr.nix │ │ │ └── coredns.nix │ │ ├── sower │ │ │ ├── navidrome.nix │ │ │ ├── hoogle.nix │ │ │ ├── podgrab.nix │ │ │ ├── mpd.nix │ │ │ ├── soulseek.nix │ │ │ ├── default.nix │ │ │ ├── kmonad.nix │ │ │ ├── jellyfin.nix │ │ │ ├── immich.nix │ │ │ ├── hardware.nix │ │ │ ├── home-assistant.nix │ │ │ └── tubearchivist.nix │ │ ├── apollyon │ │ │ ├── default.nix │ │ │ ├── hardware.nix │ │ │ └── wireguard.nix │ │ └── void-warren │ │ │ ├── default.nix │ │ │ └── disk-config.nix │ └── personal-computers │ │ ├── lorean │ │ ├── arduino.nix │ │ ├── immich-sdcard-sync.nix │ │ ├── wireguard.nix │ │ ├── hardware.nix │ │ ├── zfs.nix │ │ ├── default.nix │ │ ├── kmonad.nix │ │ └── disk-config.nix │ │ ├── voice-of-evening │ │ ├── arduino.nix │ │ ├── hardware.nix │ │ ├── default.nix │ │ ├── zfs.nix │ │ ├── kmonad.nix │ │ └── disk-config.nix │ │ └── nightshade │ │ ├── home.nix │ │ └── kmonad.nix └── profiles │ ├── physical-machine │ └── default.nix │ ├── pc │ ├── default.nix │ └── openai-codex.nix │ └── virtual-machine │ └── default.nix ├── .gitignore ├── installer ├── templates │ ├── pc-default.nix │ ├── server-default.nix │ ├── server-disk-config.nix │ └── pc-disk-config.nix ├── connect.sh ├── default.nix ├── configuration.nix └── install-server.sh ├── MACHINES.md ├── .gitmodules ├── modules ├── nixos │ ├── detect-online.sh │ ├── network-interfaces.nix │ ├── primary-user.nix │ ├── s3fs.nix │ └── qbittorrent.nix ├── default.nix └── home-manager │ └── primary-user.nix ├── .sops.yaml ├── CLAUDE.md ├── README.md └── PROVISIONING.md /.envrc: -------------------------------------------------------------------------------- 1 | use flake 2 | 3 | export PATH="$HOME/.local/bin:$PATH" 4 | 5 | -------------------------------------------------------------------------------- /config/modules/ui/eww/eww.yuck: -------------------------------------------------------------------------------- 1 | (include "/home/solomon/eww/eww.yuck") 2 | -------------------------------------------------------------------------------- /config/machines/servers/gnostic-ascension/public/index.html: -------------------------------------------------------------------------------- 1 | W E L C O M E 2 | -------------------------------------------------------------------------------- /config/modules/system/users/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | 3 | { 4 | users.mutableUsers = false; 5 | } 6 | -------------------------------------------------------------------------------- /config/modules/system/nix-direnv/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | 3 | { 4 | programs.direnv.enable = true; 5 | } 6 | -------------------------------------------------------------------------------- /config/modules/system/powertop/default.nix: -------------------------------------------------------------------------------- 1 | { ... }: 2 | 3 | { 4 | powerManagement.powertop.enable = true; 5 | } 6 | -------------------------------------------------------------------------------- /config/modules/system/timezone/default.nix: -------------------------------------------------------------------------------- 1 | { ... }: 2 | 3 | { 4 | time.timeZone = "America/Los_Angeles"; 5 | } 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/.projectile 2 | **/.direnv 3 | /result 4 | /.direnv/ 5 | /installer/result 6 | /.projectile-cache.eld 7 | -------------------------------------------------------------------------------- /config/modules/ui/dmenu/emoji-prompt.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | OPTIONS= 4 | 5 | echo "$OPTIONS" | dmenu | xclip -sel clip 6 | -------------------------------------------------------------------------------- /config/modules/security/process-information-hiding/default.nix: -------------------------------------------------------------------------------- 1 | { ... }: 2 | 3 | { 4 | security.hideProcessInformation = true; 5 | } 6 | -------------------------------------------------------------------------------- /config/modules/ui/eww/images/music.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solomon-b/nixos-config/HEAD/config/modules/ui/eww/images/music.png -------------------------------------------------------------------------------- /config/modules/ui/git/default.nix: -------------------------------------------------------------------------------- 1 | { ... }: 2 | 3 | { 4 | primary-user = { 5 | home-manager.imports = [ ./home.nix ]; 6 | }; 7 | } 8 | -------------------------------------------------------------------------------- /config/modules/ui/st/default.nix: -------------------------------------------------------------------------------- 1 | { ... }: 2 | 3 | { 4 | primary-user = { 5 | home-manager.imports = [ ./home.nix ]; 6 | }; 7 | } 8 | -------------------------------------------------------------------------------- /config/modules/ui/direnv/default.nix: -------------------------------------------------------------------------------- 1 | { ... }: 2 | 3 | { 4 | primary-user = { 5 | home-manager.imports = [ ./home.nix ]; 6 | }; 7 | } 8 | -------------------------------------------------------------------------------- /config/modules/ui/dunst/default.nix: -------------------------------------------------------------------------------- 1 | { ... }: 2 | 3 | { 4 | primary-user = { 5 | home-manager.imports = [ ./home.nix ]; 6 | }; 7 | } 8 | -------------------------------------------------------------------------------- /config/modules/ui/emacs/default.nix: -------------------------------------------------------------------------------- 1 | { ... }: 2 | 3 | { 4 | primary-user = { 5 | home-manager.imports = [ ./home.nix ]; 6 | }; 7 | } 8 | -------------------------------------------------------------------------------- /config/modules/ui/starship/default.nix: -------------------------------------------------------------------------------- 1 | { ... }: 2 | 3 | { 4 | primary-user = { 5 | home-manager.imports = [ ./home.nix ]; 6 | }; 7 | } 8 | -------------------------------------------------------------------------------- /config/modules/ui/zsh/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | 3 | { 4 | primary-user = { 5 | home-manager.imports = [ ./home.nix ]; 6 | }; 7 | } 8 | -------------------------------------------------------------------------------- /config/modules/ui/dmenu/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | 3 | { 4 | primary-user = { 5 | home-manager.imports = [ ./home.nix ]; 6 | }; 7 | } 8 | -------------------------------------------------------------------------------- /config/modules/ui/direnv/home.nix: -------------------------------------------------------------------------------- 1 | { ... }: 2 | 3 | { 4 | programs.direnv = { 5 | enable = true; 6 | enableZshIntegration = true; 7 | }; 8 | } 9 | -------------------------------------------------------------------------------- /config/modules/ui/opengl/default.nix: -------------------------------------------------------------------------------- 1 | { ... }: 2 | 3 | { 4 | hardware.graphics = { 5 | enable = true; 6 | enable32Bit = true; 7 | }; 8 | } 9 | -------------------------------------------------------------------------------- /config/modules/ui/xserver/wallpapers/Vaporwave.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solomon-b/nixos-config/HEAD/config/modules/ui/xserver/wallpapers/Vaporwave.jpg -------------------------------------------------------------------------------- /config/modules/system/devices/bluetooth/default.nix: -------------------------------------------------------------------------------- 1 | { ... }: 2 | 3 | { 4 | hardware.bluetooth.enable = true; 5 | services.blueman.enable = true; 6 | } 7 | -------------------------------------------------------------------------------- /config/modules/ui/xserver/wallpapers/Big_Sur_Simple.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solomon-b/nixos-config/HEAD/config/modules/ui/xserver/wallpapers/Big_Sur_Simple.png -------------------------------------------------------------------------------- /config/modules/ui/eww/default.nix: -------------------------------------------------------------------------------- 1 | { ... }: 2 | 3 | { 4 | primary-user.home-manager.programs.eww = { 5 | enable = true; 6 | configDir = ./.; 7 | }; 8 | } 9 | -------------------------------------------------------------------------------- /config/modules/ui/eww/close_dashboard: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ## Files and cmd 4 | CFG="$HOME/.config/eww" 5 | EWW=`which eww` 6 | 7 | ${EWW} --config "$CFG" close-all 8 | -------------------------------------------------------------------------------- /config/modules/ui/git/work-profile: -------------------------------------------------------------------------------- 1 | [user] 2 | name = "Solomon Bothwell" 3 | email = "solomon.bothwell@bitnomial.com" 4 | signingKey = "~/.ssh/id_ed25519_bitnomial.pub"; 5 | -------------------------------------------------------------------------------- /config/modules/ui/xserver/wallpapers/Yosemite-Color-Block.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solomon-b/nixos-config/HEAD/config/modules/ui/xserver/wallpapers/Yosemite-Color-Block.png -------------------------------------------------------------------------------- /config/modules/packages/development.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | 3 | let 4 | packages = import ./default.nix { inherit pkgs; }; 5 | in 6 | { 7 | home.packages = packages.development; 8 | } -------------------------------------------------------------------------------- /config/modules/packages/pc-cli-tools.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | 3 | let 4 | packages = import ./default.nix { inherit pkgs; }; 5 | in 6 | { 7 | home.packages = packages.pc-cli-tools; 8 | } -------------------------------------------------------------------------------- /config/modules/packages/server-cli-tools.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | let 3 | packages = import ./default.nix { inherit pkgs; }; 4 | in 5 | { 6 | home.packages = packages.server-cli-tools; 7 | } -------------------------------------------------------------------------------- /config/modules/packages/gui-applications.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | 3 | let 4 | packages = import ./default.nix { inherit pkgs; }; 5 | in 6 | { 7 | home.packages = packages.gui-applications; 8 | } -------------------------------------------------------------------------------- /config/modules/ui/fonts/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | 3 | { 4 | fonts.packages = with pkgs; [ 5 | fira-code 6 | iosevka 7 | material-design-icons 8 | noto-fonts-emoji 9 | ]; 10 | } 11 | -------------------------------------------------------------------------------- /config/modules/services/virtualisation/virtualbox/default.nix: -------------------------------------------------------------------------------- 1 | { config, ... }: 2 | 3 | { 4 | virtualisation.virtualbox.host.enable = true; 5 | users.extraGroups.vboxusers.members = [ config.primary-user.name ]; 6 | } 7 | -------------------------------------------------------------------------------- /config/modules/ui/emacs/home.nix: -------------------------------------------------------------------------------- 1 | { config, pkgs, ... }: 2 | 3 | { 4 | programs.emacs = { 5 | enable = true; 6 | package = pkgs.emacs; 7 | extraPackages = epkgs: with epkgs; [ vterm ]; 8 | }; 9 | } 10 | 11 | -------------------------------------------------------------------------------- /installer/templates/pc-default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | 3 | { 4 | imports = [ 5 | ./hardware.nix 6 | ./disk-config.nix 7 | 8 | ../../../profiles/pc 9 | ]; 10 | 11 | networking.hostName = "{{MACHINE_NAME}}"; 12 | } -------------------------------------------------------------------------------- /config/modules/ui/eww/open_dashboard: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ## Files and cmd 4 | CFG="$HOME/.config/eww" 5 | EWW=`which eww` 6 | 7 | ## Open widgets 8 | run_eww() { 9 | ${EWW} --config "$CFG" open dashboard 10 | } 11 | 12 | run_eww 13 | -------------------------------------------------------------------------------- /installer/templates/server-default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | 3 | { 4 | imports = [ 5 | ./hardware.nix 6 | ./disk-config.nix 7 | 8 | ../../../profiles/virtual-machine 9 | ]; 10 | 11 | networking.hostName = "{{MACHINE_NAME}}"; 12 | } -------------------------------------------------------------------------------- /config/machines/servers/test-vm/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | 3 | { 4 | imports = [ 5 | ./hardware.nix 6 | ./disk-config.nix 7 | 8 | ../../../profiles/virtual-machine 9 | ]; 10 | 11 | networking.hostName = "test-vm"; 12 | } 13 | -------------------------------------------------------------------------------- /config/modules/system/nix-binary-caches/nix-linter.nix: -------------------------------------------------------------------------------- 1 | { ... }: 2 | 3 | { 4 | nix = { 5 | binaryCaches = [ "https://nix-linter.cachix.org" ]; 6 | binaryCachePublicKeys = [ "nix-linter.cachix.org-1:BdTne5LEHQfIoJh4RsoVdgvqfObpyHO5L0SCjXFShlE=" ]; 7 | }; 8 | } 9 | -------------------------------------------------------------------------------- /config/modules/ui/eww/scripts/mails: -------------------------------------------------------------------------------- 1 | #!/bin/python 2 | 3 | import imaplib 4 | obj = imaplib.IMAP4_SSL('imap.gmail.com',993) 5 | obj.login('username@gmail.com','PASSWORD') # write your email and password 6 | obj.select() 7 | print(len(obj.search(None, 'UnSeen')[1][0].split())) 8 | -------------------------------------------------------------------------------- /config/modules/services/virtualisation/libvirt/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | 3 | { 4 | virtualisation.libvirtd.enable = true; 5 | programs.dconf.enable = true; 6 | 7 | primary-user.extraGroups = [ "libvirtd" ]; 8 | environment.systemPackages = [ pkgs.virt-manager ]; 9 | } 10 | -------------------------------------------------------------------------------- /MACHINES.md: -------------------------------------------------------------------------------- 1 | - accompaniment-of-shadows 2 | nginx reverse proxy, docker orchestration, postgres, redis and DNS server 3 | 4 | - apollyon 5 | qBittorent 6 | 7 | - sower 8 | media streaming 9 | - immich 10 | - jellyfin 11 | - podgrab 12 | - tubearchivist 13 | - navidrome 14 | -------------------------------------------------------------------------------- /config/modules/services/tailscale/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | 3 | { 4 | services.tailscale.enable = true; 5 | networking.firewall = { 6 | allowedUDPPorts = [ 41641 ]; 7 | checkReversePath = "loose"; 8 | }; 9 | environment.systemPackages = [ pkgs.tailscale ]; 10 | } 11 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "flakes/xmonad-solomon/xmonad-contrib"] 2 | path = flakes/xmonad-solomon/xmonad-contrib 3 | url = git@github.com:solomon-b/xmonad-contrib.git 4 | [submodule "flakes/xmonad-solomon/xmonad"] 5 | path = flakes/xmonad-solomon/xmonad 6 | url = git@github.com:solomon-b/xmonad.git 7 | -------------------------------------------------------------------------------- /config/modules/system/systemd-boot/default.nix: -------------------------------------------------------------------------------- 1 | { lib, ... }: 2 | 3 | { 4 | boot.loader = { 5 | timeout = lib.mkDefault 1; 6 | systemd-boot = { 7 | enable = lib.mkDefault true; 8 | editor = false; 9 | }; 10 | efi.canTouchEfiVariables = lib.mkDefault true; 11 | }; 12 | } 13 | -------------------------------------------------------------------------------- /installer/connect.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | SSID=$(gum input --placeholder "enter ssid..") 4 | PSK=$(gum input --placeholder "enter password..") 5 | echo "Choose Interface:" 6 | INTERFACE=$(basename -a /sys/class/net/* | gum choose) 7 | 8 | wpa_passphrase -B -i "${INTERFACE}" -c <(wpa_passphrase "$SSID" "$PSK") 9 | -------------------------------------------------------------------------------- /config/modules/system/devices/touchpad/default.nix: -------------------------------------------------------------------------------- 1 | { ... }: 2 | 3 | { 4 | services.libinput = { 5 | enable = true; 6 | touchpad = { 7 | disableWhileTyping = true; 8 | naturalScrolling = false; 9 | tapping = false; 10 | clickMethod = "clickfinger"; 11 | }; 12 | }; 13 | } 14 | -------------------------------------------------------------------------------- /config/modules/ui/dmenu/kill-window-prompt.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | WIN_ID=$(xdotool getwindowfocus) 4 | WIN_NAME=$(xprop -id "$WIN_ID" WM_NAME | cut -d '"' -f 2) 5 | 6 | PROMPT="Kill window: $WIN_NAME" 7 | echo "$PROMPT" | dmenu -l 1 -nb '#2d2d2d' -nf '#cccccc' -sb '#333333' -sf '#cccccc' >/dev/null && xkill -id "$WIN_ID" 8 | -------------------------------------------------------------------------------- /config/modules/services/postgresql/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | 3 | { 4 | services.postgresql = { 5 | enable = true; 6 | package = pkgs.postgresql; 7 | extraPlugins = [ pkgs.postgresql.pkgs.postgis ]; 8 | settings = { 9 | shared_preload_libraries = "pg_stat_statements"; 10 | }; 11 | }; 12 | } 13 | -------------------------------------------------------------------------------- /config/machines/servers/accompaniment-of-shadows/sabnzbd.nix: -------------------------------------------------------------------------------- 1 | { config, ... }: 2 | 3 | { 4 | services.sabnzbd = { 5 | enable = true; 6 | }; 7 | 8 | services.nginx.virtualHosts = { 9 | "sabnzbd.service.home.arpa" = { 10 | locations."/" = { 11 | proxyPass = "http://localhost:8080"; 12 | }; 13 | }; 14 | }; 15 | } 16 | -------------------------------------------------------------------------------- /config/modules/ui/starship/home.nix: -------------------------------------------------------------------------------- 1 | { config, ... }: 2 | 3 | { 4 | programs.starship = { 5 | enable = true; 6 | enableBashIntegration = true; 7 | enableZshIntegration = true; 8 | settings = { 9 | add_newline = true; 10 | character = { 11 | success_symbol = "➜"; 12 | error_symbol = "✗"; 13 | }; 14 | }; 15 | }; 16 | } 17 | -------------------------------------------------------------------------------- /modules/nixos/detect-online.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # https://unix.stackexchange.com/questions/216919/how-can-i-make-my-user-services-wait-till-the-network-is-online/249609#249609 3 | host="${1:-8.8.8.8}" 4 | 5 | pingcheck() { 6 | ping -n -c 1 -w 5 $1 >/dev/null 2>&1 7 | } 8 | 9 | # Do you want a timeout ? 10 | while :; do 11 | pingcheck ${host} && exit 0 12 | sleep 10 13 | done 14 | -------------------------------------------------------------------------------- /config/modules/ui/audio/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | 3 | { 4 | primary-user.extraGroups = [ "audio" ]; 5 | 6 | security.rtkit.enable = true; 7 | 8 | services.pipewire = { 9 | enable = true; 10 | alsa.enable = true; 11 | alsa.support32Bit = true; 12 | pulse.enable = true; 13 | 14 | }; 15 | 16 | environment.systemPackages = [ 17 | pkgs.pulseaudio 18 | ]; 19 | } 20 | -------------------------------------------------------------------------------- /modules/default.nix: -------------------------------------------------------------------------------- 1 | { ... }: 2 | 3 | { 4 | imports = [ 5 | ./nixos/network-interfaces.nix 6 | ./nixos/primary-user.nix 7 | ./nixos/qbittorrent.nix 8 | ./nixos/s3fs.nix 9 | 10 | # ./nixos/powerpanel.nix # Ask Connor 11 | # ./nixos/preLVMTempMount.nix # Is this temp fs related? 12 | # ./nixos/secure.nix # Is this for disk encryption? 13 | # ./nixos/sudo-cmds.nix 14 | ]; 15 | } 16 | -------------------------------------------------------------------------------- /config/machines/servers/sower/navidrome.nix: -------------------------------------------------------------------------------- 1 | { ... }: 2 | 3 | { 4 | services.navidrome = { 5 | enable = true; 6 | settings = 7 | { 8 | Address = "127.0.0.1"; 9 | Port = 4533; 10 | MusicFolder = "/mnt/media/Music"; 11 | }; 12 | }; 13 | 14 | services.nginx.virtualHosts."navidrome.service.home.arpa" = { 15 | locations."/".proxyPass = "http://localhost:4533"; 16 | }; 17 | } 18 | -------------------------------------------------------------------------------- /config/machines/servers/sower/hoogle.nix: -------------------------------------------------------------------------------- 1 | { ... }: 2 | 3 | { 4 | services.hoogle = { 5 | enable = true; 6 | port = 8083; 7 | 8 | # TODO: enable all packages 9 | packages = hp: [ 10 | hp.xmonad 11 | hp.xmonad-contrib 12 | hp.lens 13 | hp.comonad 14 | ]; 15 | }; 16 | 17 | services.nginx.virtualHosts."hoogle.sower" = { 18 | locations."/".proxyPass = "http://localhost:8083"; 19 | }; 20 | } 21 | -------------------------------------------------------------------------------- /config/modules/security/sshd/public-keys.nix: -------------------------------------------------------------------------------- 1 | [ 2 | "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHedhPWMgsGFQS7niiFlgkCty/0yS68tVP0pm4x4PQLp solomon@nightshade" 3 | "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILVTeNwDsHZX06k+o+fz1wmI8h3q2ks+5C7Mv5ADXo+o solomon@lorean" 4 | "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKK3kMxgeA9ivLG/A81PNKhRJx32r7dzFnl+SZNhBc9K solomon@sower" 5 | "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJHWnMYdY2wfu05WThiGKlNK8aCX3HmNyQds8MOoSM+v solomon@voice-of-evening" 6 | ] 7 | -------------------------------------------------------------------------------- /config/modules/ui/dmenu/exit-prompt.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | OPTIONS=$(cat <<'EOF' 4 | 1: Logout | loginctl terminate-session "$XDG_SESSION_ID" 5 | 2: Shutdown | systemctl poweroff 6 | 3: Reboot | systemctl reboot 7 | EOF 8 | ) 9 | 10 | CHOICE=$(echo "$OPTIONS" | cut -d'|' -f1 | dmenu -l 10 -nb '#2d2d2d' -nf '#cccccc' -sb '#333333' -sf '#cccccc' | sed 's/ *$//') 11 | [ -z "$CHOICE" ] && exit 0 12 | 13 | CMD=$(echo "$OPTIONS" | grep -F "$CHOICE" | cut -d'|' -f2- | head -n 1) 14 | [ -n "$CMD" ] && eval "$CMD" 15 | -------------------------------------------------------------------------------- /config/machines/servers/accompaniment-of-shadows/redis.nix: -------------------------------------------------------------------------------- 1 | # Redis Service 2 | { config, pkgs, lib, ... }: 3 | 4 | { 5 | # Redis for Immich 6 | services.redis.servers.immich = { 7 | enable = true; 8 | openFirewall = true; 9 | port = 6379; 10 | requirePassFile = config.sops.secrets.redis-immich-password.path; 11 | bind = null; # Allow connections from all interfaces 12 | }; 13 | 14 | networking.firewall.allowedTCPPorts = [ 6379 ]; 15 | 16 | sops.secrets.redis-immich-password = { }; 17 | } 18 | -------------------------------------------------------------------------------- /config/modules/system/nix-binary-caches/default.nix: -------------------------------------------------------------------------------- 1 | { ... }: 2 | 3 | { 4 | nix.settings = { 5 | substituters = [ 6 | "https://cache.nixos.org/" 7 | "https://nix-linter.cachix.org" 8 | "https://iohk.cachix.org" 9 | ]; 10 | trusted-public-keys = [ 11 | "nix-linter.cachix.org-1:BdTne5LEHQfIoJh4RsoVdgvqfObpyHO5L0SCjXFShlE=" 12 | "hydra.iohk.io:f/Ea+s+dFdN+3Y/G+FDgSq+a5NEWhJGzdjvKNGv0/EQ=" 13 | "iohk.cachix.org-1:DpRUyj7h7V830dp/i6Nti+NEO2/nhblbov/8MW7Rqoo=" 14 | ]; 15 | }; 16 | } 17 | -------------------------------------------------------------------------------- /config/modules/security/gpg/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | 3 | { 4 | programs.gnupg.agent = { 5 | enable = true; 6 | #enableSSHSupport = true; 7 | }; 8 | 9 | services.udev.packages = [ pkgs.yubikey-personalization ]; 10 | 11 | services.pcscd.enable = true; 12 | 13 | environment.systemPackages = [ 14 | pkgs.passage 15 | pkgs.age-plugin-yubikey 16 | pkgs.rage 17 | pkgs.yubikey-manager 18 | #pkgs.yubikey-manager-qt 19 | ]; 20 | 21 | environment.variables = { 22 | PASSAGE_AGE = "rage"; 23 | }; 24 | } 25 | -------------------------------------------------------------------------------- /config/modules/system/nixpkgs/default.nix: -------------------------------------------------------------------------------- 1 | { config, lib, inputs, ... }: 2 | 3 | { 4 | nix = { 5 | gc = { 6 | automatic = true; 7 | options = "--delete-older-than 14d"; 8 | dates = "03:15"; 9 | }; 10 | 11 | nixPath = lib.mkForce [ 12 | "unstable=${inputs.unstable}" 13 | "nixpkgs=${inputs.nixpkgs}" 14 | ]; 15 | 16 | settings.auto-optimise-store = true; 17 | 18 | registry = { 19 | nixpkgs.flake = inputs.nixpkgs; 20 | unstable.flake = inputs.unstable; 21 | }; 22 | }; 23 | } 24 | -------------------------------------------------------------------------------- /config/machines/servers/sower/podgrab.nix: -------------------------------------------------------------------------------- 1 | { ... }: 2 | 3 | 4 | { 5 | virtualisation.oci-containers.containers.podgrab = { 6 | image = "akhilrex/podgrab:latest"; 7 | ports = [ "8081:8080" ]; 8 | volumes = [ 9 | "/mnt/media/Podcasts:/assets" 10 | ]; 11 | environment = { 12 | CHECK_FREQUENCY = "240"; 13 | }; 14 | }; 15 | 16 | services.nginx.virtualHosts."podgrab.service" = { 17 | locations."/".proxyPass = "http://localhost:8081"; 18 | }; 19 | 20 | networking.firewall.allowedTCPPorts = [ 8081 ]; 21 | } 22 | -------------------------------------------------------------------------------- /config/modules/system/devices/udisk/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | 3 | { 4 | services.udisks2 = { 5 | enable = true; 6 | mountOnMedia = true; 7 | }; 8 | 9 | services.dbus.enable = true; 10 | 11 | primary-user.home-manager = { 12 | services.udiskie = { 13 | enable = true; 14 | }; 15 | 16 | # https://github.com/nix-community/home-manager/issues/2064 17 | systemd.user.targets.tray = { 18 | Unit = { 19 | Description = "Home Manager System Tray"; 20 | Requires = [ "graphical-session-pre.target" ]; 21 | }; 22 | }; 23 | }; 24 | } 25 | -------------------------------------------------------------------------------- /config/machines/personal-computers/lorean/arduino.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | 3 | { 4 | environment.systemPackages = [ 5 | pkgs.arduino 6 | pkgs.arduino-cli 7 | ]; 8 | 9 | # Arduino Giga R1 UDEV Rule 10 | # https://github.com/arduino/ArduinoCore-mbed/blob/main/post_install.sh 11 | services.udev.extraRules = '' 12 | SUBSYSTEMS=="usb", ATTRS{idVendor}=="2e8a", MODE:="0666" 13 | SUBSYSTEMS=="usb", ATTRS{idVendor}=="2341", MODE:="0666" 14 | SUBSYSTEMS=="usb", ATTRS{idVendor}=="1fc9", MODE:="0666" 15 | SUBSYSTEMS=="usb", ATTRS{idVendor}=="0525", MODE:="0666" 16 | ''; 17 | 18 | } 19 | -------------------------------------------------------------------------------- /config/machines/personal-computers/voice-of-evening/arduino.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | 3 | { 4 | environment.systemPackages = [ 5 | pkgs.arduino 6 | pkgs.arduino-cli 7 | ]; 8 | 9 | # Arduino Giga R1 UDEV Rule 10 | # https://github.com/arduino/ArduinoCore-mbed/blob/main/post_install.sh 11 | services.udev.extraRules = '' 12 | SUBSYSTEMS=="usb", ATTRS{idVendor}=="2e8a", MODE:="0666" 13 | SUBSYSTEMS=="usb", ATTRS{idVendor}=="2341", MODE:="0666" 14 | SUBSYSTEMS=="usb", ATTRS{idVendor}=="1fc9", MODE:="0666" 15 | SUBSYSTEMS=="usb", ATTRS{idVendor}=="0525", MODE:="0666" 16 | ''; 17 | 18 | } 19 | -------------------------------------------------------------------------------- /config/machines/personal-computers/lorean/immich-sdcard-sync.nix: -------------------------------------------------------------------------------- 1 | { config, ... }: 2 | 3 | { 4 | sops.secrets = { 5 | immich-server-url-file = { }; 6 | immich-server-username-file = { }; 7 | immich-server-password-file = { }; 8 | }; 9 | immich-sdcard-sync = { 10 | enable = true; 11 | immichServerUrlFile = "${config.sops.secrets.immich-server-url-file.path}"; 12 | immichUsernameFile = "${config.sops.secrets.immich-server-username-file.path}"; 13 | immichPasswordFile = "${config.sops.secrets.immich-server-password-file.path}"; 14 | sdCardSerials = "Generic-_SD_MMC_20120501030900000-0:0"; 15 | }; 16 | } 17 | -------------------------------------------------------------------------------- /modules/nixos/network-interfaces.nix: -------------------------------------------------------------------------------- 1 | { lib, ... }: 2 | 3 | { 4 | options.interfaces = lib.mkOption { 5 | description = "The list of networking interface names."; 6 | 7 | type = lib.types.submodule { 8 | options = { 9 | wifi = lib.mkOption { 10 | default = null; 11 | type = lib.types.nullOr lib.types.str; 12 | description = "The wifi interface."; 13 | }; 14 | eth = lib.mkOption { 15 | default = null; 16 | type = lib.types.nullOr lib.types.str; 17 | description = "The ethernet interface."; 18 | }; 19 | }; 20 | }; 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /config/machines/servers/accompaniment-of-shadows/tt-rss.nix: -------------------------------------------------------------------------------- 1 | # NOTE: Run the update script to initialize the database: 2 | # sudo -u tt_rss /nix/store/0ch7n28h724dpvkc30z18jgy21s9p49b-php-with-extensions-8.0.22/bin/php /var/lib/tt-rss/www/update.php --update-schema 3 | { config, ... }: 4 | 5 | { 6 | services.tt-rss = { 7 | enable = true; 8 | selfUrlPath = "http://tt-rss.service.home.arpa"; 9 | database = { 10 | host = "localhost"; 11 | port = 5432; 12 | passwordFile = config.sops.secrets.tt_rss-password.path; 13 | }; 14 | singleUserMode = true; 15 | }; 16 | 17 | sops.secrets.tt_rss-password = { }; 18 | } 19 | -------------------------------------------------------------------------------- /config/modules/ui/dmenu/screenshot-prompt.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | OPTIONS=$(cat < 3 | Date: Thu, 4 Jan 2024 13:15:32 -0800 4 | Subject: [PATCH 1/3] Adds Fira Code Font 5 | 6 | https://github.com/tonsky/FiraCode 7 | --- 8 | config.def.h | 2 +- 9 | 1 file changed, 1 insertion(+), 1 deletion(-) 10 | 11 | diff --git a/config.def.h b/config.def.h 12 | index 91ab8ca..52b2ec3 100644 13 | --- a/config.def.h 14 | +++ b/config.def.h 15 | @@ -5,7 +5,7 @@ 16 | * 17 | * font: see http://freedesktop.org/software/fontconfig/fontconfig-user.html 18 | */ 19 | -static char *font = "Liberation Mono:pixelsize=12:antialias=true:autohint=true"; 20 | +static char *font = "Fira Code,Fira Code Light:style=Regular:pixelsize=14:antialias=true:autohint=true"; 21 | static int borderpx = 2; 22 | 23 | /* 24 | -- 25 | 2.42.0 26 | 27 | -------------------------------------------------------------------------------- /config/machines/servers/apollyon/default.nix: -------------------------------------------------------------------------------- 1 | # qBittorrent 2 | { pkgs, ... }: 3 | 4 | { 5 | imports = [ 6 | ./hardware.nix 7 | ./wireguard.nix 8 | 9 | ../../../profiles/virtual-machine 10 | ]; 11 | 12 | networking.hostName = "apollyon"; 13 | 14 | services.qBittorrent = { 15 | enable = true; 16 | openFirewall = true; 17 | webUIAddress.port = 8081; 18 | }; 19 | 20 | fileSystems."/mnt/media" = { 21 | device = "192.168.5.6:/mnt/tank/Media"; 22 | fsType = "nfs"; 23 | options = [ 24 | "defaults" # → rw,suid,dev,exec,auto,nouser,async 25 | "vers=3" # force NFSv3 26 | "proto=tcp" # use TCP transport 27 | "intr" # allow signals (Ctrl-C) to interrupt 28 | "timeo=30" # initial timeout = 3 s (30 deciseconds) 29 | "retrans=3" # retry only 3 times (~9 s total) 30 | "_netdev" # wait for network before mounting 31 | ]; 32 | }; 33 | } 34 | -------------------------------------------------------------------------------- /config/modules/security/sshd/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, lib, config, ... }: 2 | { 3 | services.openssh = { 4 | enable = true; 5 | settings.PasswordAuthentication = true; 6 | extraConfig = "PermitUserEnvironment yes"; 7 | }; 8 | 9 | programs.ssh.startAgent = true; 10 | 11 | services.sshd.enable = true; 12 | 13 | security.pam = { 14 | sshAgentAuth.enable = true; 15 | services.sudo.sshAgentAuth = true; 16 | }; 17 | 18 | primary-user.openssh.authorizedKeys.keys = import ./public-keys.nix; 19 | 20 | users.users.root.openssh.authorizedKeys.keys = [ 21 | "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHedhPWMgsGFQS7niiFlgkCty/0yS68tVP0pm4x4PQLp solomon@nightshade" 22 | "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILVTeNwDsHZX06k+o+fz1wmI8h3q2ks+5C7Mv5ADXo+o solomon@lorean" 23 | "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJHWnMYdY2wfu05WThiGKlNK8aCX3HmNyQds8MOoSM+v solomon@voice-of-evening" 24 | ]; 25 | } 26 | -------------------------------------------------------------------------------- /config/modules/ui/eww/scripts/open_links: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ## Open links in firefox 4 | FILE="$HOME/.cache/eww_launch.dashboard" 5 | CFG="$HOME/.config/eww/dashboard" 6 | EWW=`which eww` 7 | cmd="firefox --new-tab" 8 | 9 | close_dash() { 10 | ${EWW} --config "$CFG" close \ 11 | background profile system clock uptime music github \ 12 | reddit twitter youtube weather apps mail logout sleep reboot poweroff folders 13 | rm -rf "$FILE" 14 | } 15 | 16 | if [[ "$1" == "--mail" ]]; then 17 | close_dash && ${cmd} "https://mail.google.com" 18 | 19 | elif [[ "$1" == "--gh" ]]; then 20 | close_dash && ${cmd} "https://github.com" 21 | 22 | elif [[ "$1" == "--rd" ]]; then 23 | close_dash && ${cmd} "https://reddit.com" 24 | 25 | elif [[ "$1" == "--tw" ]]; then 26 | close_dash && ${cmd} "https://twitter.com" 27 | 28 | elif [[ "$1" == "--yt" ]]; then 29 | close_dash && ${cmd} "https://youtube.com" 30 | 31 | fi 32 | -------------------------------------------------------------------------------- /config/modules/ui/ntfy/home.nix: -------------------------------------------------------------------------------- 1 | { config, pkgs, ... }: 2 | 3 | let 4 | notifyScript = pkgs.writeShellScript "ntfy-notify" '' 5 | ${pkgs.libnotify}/bin/notify-send "$title" "$message" 6 | ''; 7 | in 8 | { 9 | home.packages = with pkgs; [ ntfy-sh ]; 10 | 11 | systemd.user.services.ntfy-subscribe = { 12 | Unit = { 13 | Description = "Ntfy notification subscriber for homelab alerts"; 14 | After = [ "network-online.target" "sops-nix.service" ]; 15 | }; 16 | Service = { 17 | Type = "simple"; 18 | Environment = "DISPLAY=:0"; 19 | ExecStart = ''${pkgs.bash}/bin/bash -c '${pkgs.ntfy-sh}/bin/ntfy subscribe https://ntfy.sh/$(cat ${config.sops.secrets.ntfy-topic.path}) ${notifyScript}' ''; 20 | Restart = "on-failure"; 21 | RestartSec = "10s"; 22 | }; 23 | Install = { 24 | WantedBy = [ "default.target" ]; 25 | }; 26 | }; 27 | 28 | sops.secrets.ntfy-topic = { }; 29 | } 30 | -------------------------------------------------------------------------------- /config/machines/servers/accompaniment-of-shadows/lubelogger.nix: -------------------------------------------------------------------------------- 1 | { ... }: 2 | 3 | { 4 | fileSystems."/mnt/lubelogger" = { 5 | device = "192.168.5.6:/mnt/tank/app-data/lubelogger"; 6 | fsType = "nfs"; 7 | options = [ 8 | "defaults" # → rw,suid,dev,exec,auto,nouser,async 9 | "vers=3" # force NFSv3 10 | "proto=tcp" # use TCP transport 11 | "intr" # allow signals (Ctrl-C) to interrupt 12 | "timeo=30" # initial timeout = 3 s (30 deciseconds) 13 | "retrans=3" # retry only 3 times (~9 s total) 14 | "_netdev" # wait for network before mounting 15 | ]; 16 | }; 17 | 18 | services.lubelogger = { 19 | enable = true; 20 | port = 5000; 21 | dataDir = "lubelogger"; 22 | }; 23 | 24 | services.nginx.virtualHosts."lubelogger.service.home.arpa" = { 25 | locations."/" = { 26 | proxyPass = "http://localhost:5000"; 27 | proxyWebsockets = true; 28 | }; 29 | }; 30 | } 31 | -------------------------------------------------------------------------------- /config/modules/ui/eww/scripts/open_folders: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ## Open folders in thunar 4 | FILE="$HOME/.cache/eww_launch.dashboard" 5 | CFG="$HOME/.config/eww/dashboard" 6 | EWW=`which eww` 7 | 8 | close_dash() { 9 | ${EWW} --config "$CFG" close \ 10 | background profile system clock uptime music github \ 11 | reddit twitter youtube weather apps mail logout sleep reboot poweroff folders 12 | rm -rf "$FILE" 13 | } 14 | 15 | if [[ "$1" == "--dl" ]]; then 16 | close_dash && thunar ~/Downloads & 17 | 18 | elif [[ "$1" == "--docs" ]]; then 19 | close_dash && thunar ~/Documents & 20 | 21 | elif [[ "$1" == "--music" ]]; then 22 | close_dash && thunar ~/Music & 23 | 24 | elif [[ "$1" == "--pics" ]]; then 25 | close_dash && thunar ~/Pictures & 26 | 27 | elif [[ "$1" == "--cfg" ]]; then 28 | close_dash && thunar ~/.config & 29 | 30 | elif [[ "$1" == "--local" ]]; then 31 | close_dash && thunar ~/.local/share & 32 | 33 | fi 34 | -------------------------------------------------------------------------------- /config/machines/servers/accompaniment-of-shadows/filebrowser.nix: -------------------------------------------------------------------------------- 1 | { config, ... }: 2 | 3 | { 4 | virtualisation.oci-containers.containers.filebrowser = { 5 | image = "filebrowser/filebrowser"; 6 | ports = [ "8081:80" ]; 7 | volumes = [ 8 | "/mnt/storage:/srv" 9 | "/srv/filebrowser:/database" 10 | ]; 11 | }; 12 | 13 | fileSystems."/mnt/storage" = { 14 | device = "192.168.5.6:/mnt/tank/solomon"; 15 | fsType = "nfs"; 16 | options = [ 17 | "defaults" # → rw,suid,dev,exec,auto,nouser,async 18 | "vers=3" # force NFSv3 19 | "proto=tcp" # use TCP transport 20 | "intr" # allow signals (Ctrl-C) to interrupt 21 | "timeo=30" # initial timeout = 3 s (30 deciseconds) 22 | "retrans=3" # retry only 3 times (~9 s total) 23 | "_netdev" # wait for network before mounting 24 | ]; 25 | }; 26 | 27 | services.nginx.virtualHosts."filebrowser.service" = { 28 | locations."/".proxyPass = "http://localhost:8081"; 29 | }; 30 | } 31 | -------------------------------------------------------------------------------- /config/modules/ui/st/st-scrollback-mouse-20220127-2c5edf2.diff: -------------------------------------------------------------------------------- 1 | From b5d3351a21442a842e01e8c0317603b6890b379c Mon Sep 17 00:00:00 2001 2 | From: asparagii 3 | Date: Thu, 27 Jan 2022 15:44:02 +0100 4 | Subject: [PATCH] st-scrollback-mouse 5 | 6 | --- 7 | config.def.h | 2 ++ 8 | 1 file changed, 2 insertions(+) 9 | 10 | diff --git a/config.def.h b/config.def.h 11 | index e3b469b..c217315 100644 12 | --- a/config.def.h 13 | +++ b/config.def.h 14 | @@ -176,6 +176,8 @@ static uint forcemousemod = ShiftMask; 15 | */ 16 | static MouseShortcut mshortcuts[] = { 17 | /* mask button function argument release */ 18 | + { ShiftMask, Button4, kscrollup, {.i = 1} }, 19 | + { ShiftMask, Button5, kscrolldown, {.i = 1} }, 20 | { XK_ANY_MOD, Button2, selpaste, {.i = 0}, 1 }, 21 | { ShiftMask, Button4, ttysend, {.s = "\033[5;2~"} }, 22 | { XK_ANY_MOD, Button4, ttysend, {.s = "\031"} }, 23 | -- 24 | 2.34.1 25 | -------------------------------------------------------------------------------- /config/machines/servers/gnostic-ascension/hardware.nix: -------------------------------------------------------------------------------- 1 | { config, lib, pkgs, modulesPath, ... }: 2 | 3 | { 4 | imports = [ 5 | (modulesPath + "/installer/scan/not-detected.nix") 6 | (modulesPath + "/profiles/qemu-guest.nix") 7 | ./disk-config.nix 8 | ]; 9 | 10 | boot.initrd.availableKernelModules = [ "ata_piix" "uhci_hcd" "virtio_pci" "virtio_scsi" "virtio_blk" ]; 11 | # boot.initrd.kernelModules = [ ]; 12 | boot.kernelModules = [ "kvm-intel" ]; 13 | boot.kernelPackages = config.boot.zfs.package.latestCompatibleLinuxPackages; 14 | # boot.extraModulePackages = [ ]; 15 | 16 | boot.loader.efi.canTouchEfiVariables = false; 17 | boot.loader.systemd-boot.enable = false; 18 | boot.loader.grub = { 19 | efiSupport = true; 20 | 21 | efiInstallAsRemovable = true; 22 | }; 23 | 24 | # fileSystems = { 25 | # "/".neededForBoot = true; 26 | # "/var/log".neededForBoot = true; 27 | # }; 28 | 29 | # networking.useDHCP = lib.mkDefault true; 30 | 31 | nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; 32 | } 33 | -------------------------------------------------------------------------------- /config/machines/personal-computers/lorean/hardware.nix: -------------------------------------------------------------------------------- 1 | { config, lib, pkgs, modulesPath, ... }: 2 | 3 | { 4 | imports = [ 5 | (modulesPath + "/installer/scan/not-detected.nix") 6 | ./disk-config.nix 7 | ]; 8 | 9 | boot.initrd.availableKernelModules = [ "xhci_pci" "ahci" "usb_storage" "usbhid" "sd_mod" ]; 10 | boot.initrd.kernelModules = [ "dm-snapshot" "vfat" "nls_cp437" "nls_iso8859-1" "usbhid" ]; 11 | 12 | boot.initrd.luks.devices.CRYPT = { 13 | allowDiscards = true; 14 | fallbackToPassword = true; 15 | }; 16 | 17 | boot.kernelModules = [ "nfs" ]; 18 | boot.supportedFilesystems = [ "nfs" "ntfs" ]; 19 | boot.extraModulePackages = [ ]; 20 | 21 | fileSystems = { 22 | "/".neededForBoot = true; 23 | "/var/log".neededForBoot = true; 24 | }; 25 | 26 | powerManagement.cpuFreqGovernor = lib.mkDefault "powersave"; 27 | 28 | zramSwap.enable = true; 29 | 30 | hardware.nvidia.open = lib.mkForce false; 31 | 32 | nix.settings = { 33 | build-cores = 2; 34 | max-jobs = lib.mkDefault 4; 35 | }; 36 | } 37 | -------------------------------------------------------------------------------- /config/machines/servers/test-vm/hardware.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 | { config, lib, pkgs, modulesPath, ... }: 5 | 6 | { 7 | imports = [ ]; 8 | 9 | boot.initrd.availableKernelModules = [ "ahci" "xhci_pci" "sd_mod" "sr_mod" ]; 10 | boot.initrd.kernelModules = [ ]; 11 | boot.kernelModules = [ ]; 12 | boot.extraModulePackages = [ ]; 13 | 14 | # Enables DHCP on each ethernet and wireless interface. In case of scripted networking 15 | # (the default) this is the recommended approach. When using systemd-networkd it's 16 | # still possible to use this option, but it's recommended to use it in conjunction 17 | # with explicit per-interface declarations with `networking.interfaces..useDHCP`. 18 | networking.useDHCP = lib.mkDefault true; 19 | # networking.interfaces.enp0s4.useDHCP = lib.mkDefault true; 20 | 21 | nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; 22 | } 23 | -------------------------------------------------------------------------------- /config/machines/servers/void-warren/default.nix: -------------------------------------------------------------------------------- 1 | # Raspberry Pi Server 2 | { pkgs, inputs, ... }: 3 | 4 | { 5 | imports = [ 6 | ./hardware.nix 7 | inputs.nixos-hardware.nixosModules.raspberry-pi-4 8 | ../../../profiles/virtual-machine 9 | ]; 10 | 11 | networking = { 12 | hostName = "void-warren"; 13 | # use 'systemd-networkd' to manage networking. 14 | useNetworkd = true; 15 | # use 'systemd-resolved' for name resolution. 16 | dhcpcd.enable = false; 17 | useDHCP = true; 18 | firewall.enable = true; 19 | }; 20 | 21 | services.resolved = { 22 | enable = true; 23 | fallbackDns = [ "9.9.9.9" "8.8.8.8" ]; 24 | }; 25 | 26 | # Enable hardware-specific features for Raspberry Pi 27 | hardware = { 28 | raspberry-pi."4".apply-overlays-dtmerge.enable = true; 29 | deviceTree.enable = true; 30 | }; 31 | 32 | # Optimize for ARM architecture 33 | nixpkgs.hostPlatform = "aarch64-linux"; 34 | 35 | # Enable serial console for debugging 36 | boot.kernelParams = [ "console=ttyS0,115200n8" "console=tty0" ]; 37 | } 38 | -------------------------------------------------------------------------------- /config/machines/servers/gnostic-ascension/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, modulesPath, lib, ... }: 2 | 3 | { 4 | imports = [ 5 | ./hardware.nix 6 | ../../../profiles/virtual-machine 7 | ]; 8 | 9 | networking = { 10 | hostName = "gnostic-ascension"; 11 | # generated for this host; ZFS needs a host ID. 12 | hostId = "63d9d016"; 13 | # use 'systemd-networkd' to manage networking. 14 | useNetworkd = true; 15 | # use 'systemd-resolved' for name resolution. 16 | dhcpcd.enable = false; 17 | useDHCP = true; 18 | firewall.enable = true; 19 | }; 20 | 21 | services.resolved = { 22 | enable = true; 23 | fallbackDns = [ "9.9.9.9" "8.8.8.8" ]; 24 | }; 25 | 26 | # services.nginx = { 27 | # enable = true; 28 | 29 | # recommendedGzipSettings = true; 30 | # recommendedOptimisation = true; 31 | # recommendedProxySettings = true; 32 | # virtualHosts."nude-earth.co".root = ./public; 33 | # virtualHosts."short-squeeze.info".root = ./public; 34 | # }; 35 | 36 | 37 | # networking.firewall.allowedTCPPorts = [ 80 443 ]; 38 | 39 | } 40 | -------------------------------------------------------------------------------- /config/machines/servers/sower/mpd.nix: -------------------------------------------------------------------------------- 1 | # - [x] Confirm HTTP streaming works. 2 | # - [ ] Setup "satelite" mode; https://wiki.archlinux.org/title/Music_Player_Daemon#Multi-MPD_setup 3 | # = [ ] Find a good android client 4 | { ... }: 5 | 6 | { 7 | services.mpd = { 8 | enable = true; 9 | musicDirectory = "/mnt/media/Music"; 10 | extraConfig = '' 11 | # must specify one or more outputs in order to play audio! 12 | # (e.g. ALSA, PulseAudio, PipeWire), see next sections 13 | audio_output { 14 | type "httpd" 15 | name "My HTTP Stream" 16 | encoder "opus" # optional 17 | port "8000" 18 | bitrate "128000" 19 | format "48000:16:1" 20 | always_on "yes" # prevent MPD from disconnecting all listeners when playback is stopped. 21 | tags "yes" # httpd supports sending tags to listening streams. 22 | } 23 | ''; 24 | 25 | # Optional: 26 | network.listenAddress = "any"; 27 | startWhenNeeded = true; 28 | }; 29 | 30 | networking.firewall.allowedTCPPorts = [ 6600 8000 ]; 31 | } 32 | -------------------------------------------------------------------------------- /config/modules/ui/eww/scripts/open_apps: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ## Open Applications 4 | FILE="$HOME/.cache/eww_launch.dashboard" 5 | CFG="$HOME/.config/eww/dashboard" 6 | EWW=`which eww` 7 | 8 | close_dash() { 9 | ${EWW} --config "$CFG" close \ 10 | background profile system clock uptime music github \ 11 | reddit twitter youtube weather apps mail logout sleep reboot poweroff folders 12 | rm -rf "$FILE" 13 | } 14 | 15 | if [[ "$1" == "--ff" ]]; then 16 | close_dash && firefox & 17 | 18 | elif [[ "$1" == "--tg" ]]; then 19 | close_dash && telegram-desktop & 20 | 21 | elif [[ "$1" == "--dc" ]]; then 22 | close_dash && discord & 23 | 24 | elif [[ "$1" == "--tr" ]]; then 25 | close_dash && alacritty --working-directory ~ & 26 | 27 | elif [[ "$1" == "--fm" ]]; then 28 | close_dash && thunar ~ & 29 | 30 | elif [[ "$1" == "--ge" ]]; then 31 | close_dash && geany & 32 | 33 | elif [[ "$1" == "--cd" ]]; then 34 | close_dash && code & 35 | 36 | elif [[ "$1" == "--gp" ]]; then 37 | close_dash && gimp & 38 | 39 | elif [[ "$1" == "--vb" ]]; then 40 | close_dash && virtualbox & 41 | 42 | fi 43 | -------------------------------------------------------------------------------- /config/machines/servers/accompaniment-of-shadows/observability.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | 3 | { 4 | services = { 5 | grafana = { 6 | enable = true; 7 | domain = "grafana.service.home.arpa"; 8 | port = 2342; 9 | addr = "127.0.0.1"; 10 | }; 11 | 12 | prometheus = { 13 | enable = true; 14 | 15 | scrapeConfigs = [ 16 | { 17 | job_name = "node"; 18 | static_configs = [{ 19 | targets = builtins.map (s: "${s}:9002") (builtins.attrNames (builtins.readDir ../.)); 20 | }]; 21 | } 22 | ]; 23 | }; 24 | 25 | uptime-kuma = { 26 | enable = true; 27 | }; 28 | 29 | nginx.virtualHosts = { 30 | "grafana.service.home.arpa" = { 31 | locations."/" = { 32 | proxyPass = "http://127.0.0.1:2342"; 33 | proxyWebsockets = true; 34 | }; 35 | }; 36 | 37 | "uptime.service.home.arpa" = { 38 | locations."/" = { 39 | proxyPass = "http://127.0.0.1:3001"; 40 | proxyWebsockets = true; 41 | }; 42 | }; 43 | }; 44 | }; 45 | } 46 | -------------------------------------------------------------------------------- /config/machines/personal-computers/nightshade/home.nix: -------------------------------------------------------------------------------- 1 | { config, pkgs, lib, ... }: 2 | 3 | { 4 | imports = [ 5 | ../../../../modules/home-manager/primary-user.nix 6 | ../../../modules/ui/dmenu/home.nix 7 | ../../../modules/ui/emacs/home.nix 8 | ../../../modules/ui/direnv/home.nix 9 | ../../../modules/ui/dunst/home.nix 10 | ../../../modules/ui/st/home.nix 11 | ../../../modules/ui/starship/home.nix 12 | ../../../modules/ui/zsh/home.nix 13 | ../../../modules/ui/git/home.nix 14 | ../../../modules/ui/ntfy/home.nix 15 | ../../../modules/packages/pc-cli-tools.nix 16 | ../../../modules/packages/gui-applications.nix 17 | ../../../modules/packages/development.nix 18 | ./kmonad.nix 19 | ]; 20 | 21 | programs.bash.enable = true; 22 | targets.genericLinux.enable = true; 23 | 24 | home.packages = with pkgs; [ 25 | # Machine-specific packages not in the shared modules 26 | acpi 27 | curl 28 | xterm 29 | eww 30 | kmonad 31 | (agda.withPackages (p: [ p._1lab p.standard-library ])) 32 | ]; 33 | 34 | home.sessionVariables = { 35 | EDITOR = "vim"; 36 | }; 37 | } 38 | -------------------------------------------------------------------------------- /config/machines/personal-computers/lorean/zfs.nix: -------------------------------------------------------------------------------- 1 | # Sending and Receiving ZFS Data: 2 | # https://docs.oracle.com/cd/E18752_01/html/819-5461/gbchx.html 3 | # 4 | # Required Target Permission: 5 | # compression,create,destroy,hold,mount,mountpoint,receive,refreservation,release,rollback,send,snapshot 6 | # https://github.com/jimsalterjrs/sanoid/issues/660 7 | { config, ... }: 8 | 9 | { 10 | services.zfs = { 11 | autoScrub.enable = true; 12 | autoSnapshot = { 13 | enable = true; 14 | frequent = 4; 15 | hourly = 24; 16 | weekly = 4; 17 | monthly = 3; 18 | }; 19 | trim.enable = true; 20 | }; 21 | 22 | services.syncoid = { 23 | enable = true; 24 | sshKey = config.sops.secrets.syncoid-ssh-key.path; 25 | commands = { 26 | "tank/home" = { 27 | target = "syncoid@sandra-voi.home.arpa:tank/system-snapshots/lorean/home"; 28 | recursive = true; 29 | }; 30 | "tank/root" = { 31 | target = "syncoid@sandra-voi.home.arpa:tank/system-snapshots/lorean/root"; 32 | recursive = true; 33 | }; 34 | }; 35 | }; 36 | 37 | users.users.syncoid.extraGroups = [ "keys" ]; 38 | } 39 | -------------------------------------------------------------------------------- /config/machines/servers/void-warren/disk-config.nix: -------------------------------------------------------------------------------- 1 | { 2 | # Construct the partition table for Raspberry Pi's primary disk (SD card or USB). 3 | # Note: For Raspberry Pi, we use a simpler layout without ZFS due to ARM constraints. 4 | disko.devices = { 5 | disk.main = { 6 | type = "disk"; 7 | device = "/dev/mmcblk0"; # SD card device, change to /dev/sda for USB boot 8 | content = { 9 | type = "gpt"; 10 | partitions = { 11 | # Raspberry Pi firmware partition 12 | firmware = { 13 | size = "512M"; 14 | type = "EF00"; # EFI System Partition 15 | content = { 16 | type = "filesystem"; 17 | format = "vfat"; 18 | mountpoint = "/boot"; 19 | mountOptions = [ "fmask=0022" "dmask=0022" ]; 20 | }; 21 | }; 22 | # Root filesystem 23 | root = { 24 | size = "100%"; 25 | content = { 26 | type = "filesystem"; 27 | format = "ext4"; 28 | mountpoint = "/"; 29 | }; 30 | }; 31 | }; 32 | }; 33 | }; 34 | }; 35 | } 36 | -------------------------------------------------------------------------------- /installer/default.nix: -------------------------------------------------------------------------------- 1 | # https://github.com/Gerg-L/nixos/blob/master/installer/default.nix 2 | { config, pkgs, lib, system ? builtins.currentSystem, modulesPath, ... }: 3 | 4 | { 5 | imports = [ 6 | "${modulesPath}/installer/cd-dvd/installation-cd-minimal.nix" 7 | ]; 8 | 9 | systemd.services.sshd.wantedBy = pkgs.lib.mkForce [ "multi-user.target" ]; 10 | 11 | users = { 12 | mutableUsers = false; 13 | users.root.openssh.authorizedKeys.keys = 14 | import ../config/modules/security/sshd/public-keys.nix; 15 | }; 16 | 17 | isoImage = { 18 | edition = lib.mkForce "solomon-min"; 19 | isoName = lib.mkForce "NixOS.iso"; 20 | }; 21 | 22 | nix = { 23 | settings = { 24 | experimental-features = [ "nix-command" "flakes" "repl-flake" ]; 25 | auto-optimise-store = true; 26 | }; 27 | }; 28 | 29 | environment = { 30 | systemPackages = [ pkgs.vim pkgs.rsync ]; 31 | 32 | etc = { 33 | "connect.sh" = { 34 | source = ./connect.sh; 35 | mode = "0777"; 36 | }; 37 | 38 | "configuration.nix" = { 39 | source = ./configuration.nix; 40 | mode = "0666"; 41 | }; 42 | }; 43 | }; 44 | } 45 | -------------------------------------------------------------------------------- /config/machines/servers/apollyon/hardware.nix: -------------------------------------------------------------------------------- 1 | { config, lib, pkgs, modulesPath, ... }: 2 | 3 | { 4 | imports = [ ]; 5 | 6 | boot.initrd.availableKernelModules = [ "ahci" "virtio_pci" "xhci_pci" "sr_mod" "virtio_blk" ]; 7 | boot.initrd.kernelModules = [ ]; 8 | boot.kernelModules = [ ]; 9 | boot.extraModulePackages = [ ]; 10 | 11 | fileSystems."/" = 12 | { 13 | device = "/dev/disk/by-uuid/28a028e9-4b08-4bc3-9deb-1936969e438d"; 14 | fsType = "ext4"; 15 | }; 16 | 17 | swapDevices = 18 | [{ device = "/dev/disk/by-uuid/1c24aefe-3109-4cdd-8981-2ea5c07ef8d2"; }]; 19 | 20 | # Enables DHCP on each ethernet and wireless interface. In case of scripted networking 21 | # (the default) this is the recommended approach. When using systemd-networkd it's 22 | # still possible to use this option, but it's recommended to use it in conjunction 23 | # with explicit per-interface declarations with `networking.interfaces..useDHCP`. 24 | networking.useDHCP = lib.mkDefault true; 25 | # networking.interfaces.enp0s4.useDHCP = lib.mkDefault true; 26 | 27 | hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; 28 | } 29 | -------------------------------------------------------------------------------- /modules/home-manager/primary-user.nix: -------------------------------------------------------------------------------- 1 | # OS-agnostic home config options for the system's primary account holder. 2 | { config 3 | , lib 4 | , options 5 | , pkgs 6 | , ... 7 | }: 8 | let 9 | inherit (lib) 10 | mkAliasDefinitions 11 | mkAliasOptionModule 12 | mkIf 13 | mkOption 14 | types 15 | ; 16 | inherit (pkgs.buildPlatform) isDarwin; 17 | cfg = config.primary-user; 18 | in 19 | { 20 | options.primary-user.name = mkOption { 21 | type = types.nullOr types.str; 22 | default = "solomon"; 23 | description = "The primary account holder's username (defaults to 'solomon')."; 24 | }; 25 | 26 | options.primary-user.home = mkOption { 27 | type = types.nullOr types.str; 28 | default = if isDarwin then "/Users/${cfg.name}" else "/home/${cfg.name}"; 29 | description = "The primary account holder's home directory."; 30 | }; 31 | 32 | config = mkIf (cfg.name != null) { 33 | home.username = cfg.name; 34 | home.homeDirectory = cfg.home; 35 | home.stateVersion = lib.mkDefault "23.05"; 36 | 37 | # By default, allow `home-manager` to manage its own installation. 38 | programs.home-manager.enable = lib.mkDefault true; 39 | }; 40 | } 41 | -------------------------------------------------------------------------------- /config/machines/servers/apollyon/wireguard.nix: -------------------------------------------------------------------------------- 1 | { config, pkgs, ... }: 2 | 3 | { 4 | environment.systemPackages = [ pkgs.wireguard-tools ]; 5 | 6 | networking.firewall = { 7 | allowedUDPPorts = [ 51820 ]; 8 | 9 | # if packets are still dropped, they will show up in dmesg 10 | logReversePathDrops = true; 11 | }; 12 | 13 | networking.wg-quick.interfaces = { 14 | wg0 = { 15 | address = [ "10.64.123.129/32" "fc00:bbbb:bbbb:bb01::3:9d1e/128" ]; 16 | dns = [ "10.64.0.1" ]; 17 | privateKeyFile = config.sops.secrets.primary-user-wireguard-private-key.path; 18 | postUp = '' 19 | ip route add 192.168.1.0/24 via 192.168.5.1 20 | ''; 21 | preDown = '' 22 | ip route delete 192.168.1.0/24 23 | ''; 24 | 25 | peers = [ (import ./mullvad.nix).us-lax-wg-203 ]; 26 | }; 27 | }; 28 | 29 | sops.secrets.primary-user-wireguard-private-key = { }; 30 | 31 | # We must enforce the order for service launch of tailscale and 32 | # wireguard to set the correct IP rule prioritization. 33 | # https://rakhesh.com/linux-bsd/tailscale-wireguard-co-existing-or-i-love-policy-based-routing/ 34 | #systemd.services.tailscaled.after = ["wg-quick-wg0.service"]; 35 | } 36 | -------------------------------------------------------------------------------- /config/profiles/physical-machine/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | 3 | { 4 | imports = [ 5 | ../../../modules 6 | 7 | ../../modules/data/session-vars 8 | 9 | ../../modules/security/gpg 10 | ../../modules/security/sshd 11 | 12 | ../../modules/system/nix-binary-caches 13 | ../../modules/system/nixpkgs 14 | ../../modules/system/systemd-boot 15 | ../../modules/system/timezone 16 | ../../modules/system/users 17 | 18 | ../../modules/services/tailscale 19 | 20 | ../../modules/ui/git 21 | ../../modules/ui/starship 22 | ../../modules/ui/zsh 23 | ]; 24 | 25 | system.stateVersion = "22.05"; 26 | nix.settings.trusted-users = [ "@wheel" ]; 27 | environment.shells = [ pkgs.zsh pkgs.bashInteractive ]; 28 | 29 | primary-user.home-manager = { 30 | imports = [ 31 | ../../modules/packages/server-cli-tools.nix 32 | ]; 33 | }; 34 | 35 | environment.systemPackages = with pkgs; [ 36 | # System-level tools needed for NixOS functionality 37 | cachix 38 | direnv 39 | git 40 | gnugrep 41 | gnumake 42 | inetutils 43 | pass 44 | zlib 45 | zsh 46 | zsh-syntax-highlighting 47 | ]; 48 | 49 | home-manager.useGlobalPkgs = true; 50 | home-manager.useUserPackages = true; 51 | } 52 | -------------------------------------------------------------------------------- /config/machines/servers/accompaniment-of-shadows/paperless-ngx.nix: -------------------------------------------------------------------------------- 1 | { config, ... }: 2 | 3 | { 4 | fileSystems."/mnt/paperless-ngx" = { 5 | device = "192.168.5.6:/mnt/tank/app-data/paperless-ngx"; 6 | fsType = "nfs"; 7 | options = [ 8 | "defaults" # → rw,suid,dev,exec,auto,nouser,async 9 | "vers=3" # force NFSv3 10 | "proto=tcp" # use TCP transport 11 | "intr" # allow signals (Ctrl-C) to interrupt 12 | "timeo=30" # initial timeout = 3 s (30 deciseconds) 13 | "retrans=3" # retry only 3 times (~9 s total) 14 | "_netdev" # wait for network before mounting 15 | ]; 16 | }; 17 | 18 | services.paperless = { 19 | enable = true; 20 | consumptionDir = "/mnt/paperless-ngx/consume"; 21 | mediaDir = "/mnt/paperless-ngx/media"; 22 | passwordFile = config.sops.secrets.paperless-password.path; 23 | port = 9000; 24 | settings = { 25 | PAPERLESS_CONSUMER_POLLING = 30; 26 | PAPERLESS_CONVERT_TMPDIR = "/var/tmp/paperless"; 27 | PAPERLESS_SCRATCH_DIR = "/var/tmp/paperless-scratch"; 28 | }; 29 | }; 30 | 31 | sops.secrets = { 32 | paperless-password = { }; 33 | }; 34 | 35 | services.nginx.virtualHosts."paperless.service.home.arpa" = { 36 | locations."/".proxyPass = "http://localhost:9000"; 37 | }; 38 | } 39 | -------------------------------------------------------------------------------- /config/machines/personal-computers/voice-of-evening/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, lib, ... }: 2 | 3 | { 4 | imports = [ 5 | ./hardware.nix 6 | ./arduino.nix 7 | ./kmonad.nix 8 | #./nfs.nix 9 | ./zfs.nix 10 | 11 | ../../../profiles/pc 12 | ../../../modules/services/virtualisation/virtualbox 13 | ]; 14 | 15 | nix.extraOptions = '' 16 | experimental-features = nix-command flakes 17 | ''; 18 | 19 | system.fsPackages = [ pkgs.nfs-utils ]; 20 | services.rpcbind.enable = true; 21 | boot.supportedFilesystems = [ "nfs" ]; 22 | 23 | systemd.services.nvidia-control-devices.enable = false; 24 | 25 | environment.systemPackages = [ 26 | pkgs.acpi 27 | pkgs.elan 28 | #(pkgs.clementine.override { withIpod = true;}) 29 | # (pkgs.ollama.override { acceleration = "cuda"; }) 30 | ]; 31 | 32 | services.xserver = { 33 | videoDrivers = lib.mkForce [ 34 | "modesetting" 35 | "fbdev" 36 | ]; 37 | }; 38 | 39 | primary-user.name = "solomon"; 40 | 41 | sops.secrets.syncoid-ssh-key = { 42 | owner = "syncoid"; 43 | mode = "600"; 44 | }; 45 | 46 | networking = { 47 | hostName = "voice-of-evening"; 48 | hostId = "5f111f12"; 49 | networkmanager.enable = true; 50 | 51 | useDHCP = false; 52 | interfaces.wlp4s0.useDHCP = true; 53 | }; 54 | } 55 | -------------------------------------------------------------------------------- /config/machines/servers/accompaniment-of-shadows/homebox.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | 3 | let 4 | homeboxData = "/mnt/homebox"; 5 | in 6 | { 7 | fileSystems."/mnt/homebox" = { 8 | device = "192.168.5.6:/mnt/tank/app-data/homebox"; 9 | fsType = "nfs"; 10 | options = [ 11 | "defaults" # → rw,suid,dev,exec,auto,nouser,async 12 | "vers=3" # force NFSv3 13 | "proto=tcp" # use TCP transport 14 | "intr" # allow signals (Ctrl-C) to interrupt 15 | "timeo=30" # initial timeout = 3 s (30 deciseconds) 16 | "retrans=3" # retry only 3 times (~9 s total) 17 | "_netdev" # wait for network before mounting 18 | ]; 19 | }; 20 | 21 | virtualisation.oci-containers.containers = { 22 | homebox = { 23 | image = "ghcr.io/hay-kot/homebox:latest"; 24 | ports = [ "3100:7745" ]; 25 | 26 | volumes = [ 27 | "${homeboxData}:/data/" 28 | ]; 29 | 30 | environment = { 31 | HBOX_LOG_LEVEL = "info"; 32 | HBOX_LOG_FORMAT = "text"; 33 | HBOX_WEB_MAX_UPLOAD_SIZE = "10"; 34 | }; 35 | 36 | autoStart = true; 37 | }; 38 | }; 39 | 40 | services.nginx.virtualHosts."homebox.service.home.arpa" = { 41 | locations."/" = { 42 | proxyPass = "http://localhost:3100"; 43 | proxyWebsockets = true; 44 | }; 45 | }; 46 | } 47 | -------------------------------------------------------------------------------- /config/profiles/pc/default.nix: -------------------------------------------------------------------------------- 1 | { config, pkgs, inputs, ... }: 2 | 3 | { 4 | imports = [ 5 | ../physical-machine 6 | 7 | ../../modules/security/gpg 8 | 9 | ../../modules/system/devices/bluetooth 10 | ../../modules/system/devices/udisk 11 | ../../modules/system/nix-direnv 12 | 13 | ../../modules/ui/audio 14 | ../../modules/ui/direnv 15 | ../../modules/ui/dunst 16 | ../../modules/ui/dmenu 17 | ../../modules/ui/fonts 18 | ../../modules/ui/opengl 19 | ../../modules/ui/picom 20 | ../../modules/ui/st 21 | ../../modules/ui/xserver 22 | 23 | ./openai-codex.nix 24 | ]; 25 | 26 | #nixpkgs.overlays = [ (import ../../../overlays/graphqurl.nix) ]; 27 | 28 | primary-user.home-manager = { 29 | imports = [ 30 | ../../modules/packages/pc-cli-tools.nix 31 | ../../modules/packages/gui-applications.nix 32 | ../../modules/packages/development.nix 33 | ../../modules/ui/emacs/home.nix 34 | ../../modules/ui/ntfy/home.nix 35 | ]; 36 | }; 37 | 38 | virtualisation = { 39 | containers = { 40 | enable = true; 41 | }; 42 | 43 | docker = { 44 | enable = true; 45 | storageDriver = "overlay2"; 46 | }; 47 | oci-containers.backend = "docker"; 48 | }; 49 | 50 | primary-user.extraGroups = [ "networkmanager" "docker" ]; 51 | } 52 | -------------------------------------------------------------------------------- /config/machines/servers/accompaniment-of-shadows/hardware.nix: -------------------------------------------------------------------------------- 1 | { config, lib, pkgs, modulesPath, ... }: 2 | 3 | { 4 | imports = [ ]; 5 | 6 | boot.initrd.availableKernelModules = [ "ahci" "virtio_pci" "xhci_pci" "sr_mod" "virtio_blk" ]; 7 | boot.initrd.kernelModules = [ ]; 8 | boot.kernelModules = [ ]; 9 | boot.extraModulePackages = [ ]; 10 | 11 | fileSystems."/" = 12 | { 13 | device = "/dev/disk/by-uuid/c194438f-89a7-41c4-9346-426239c95603"; 14 | fsType = "ext4"; 15 | }; 16 | 17 | fileSystems."/boot" = 18 | { 19 | device = "/dev/disk/by-uuid/70FB-A427"; 20 | fsType = "vfat"; 21 | }; 22 | 23 | swapDevices = 24 | [{ device = "/dev/disk/by-uuid/95e67a49-02aa-4018-89e7-4d585560b67f"; }]; 25 | 26 | # Enables DHCP on each ethernet and wireless interface. In case of scripted networking 27 | # (the default) this is the recommended approach. When using systemd-networkd it's 28 | # still possible to use this option, but it's recommended to use it in conjunction 29 | # with explicit per-interface declarations with `networking.interfaces..useDHCP`. 30 | networking.useDHCP = lib.mkDefault true; 31 | # networking.interfaces.enp0s5.useDHCP = lib.mkDefault true; 32 | 33 | hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; 34 | } 35 | -------------------------------------------------------------------------------- /config/machines/servers/sower/soulseek.nix: -------------------------------------------------------------------------------- 1 | { config, lib, pkgs, ... }: 2 | 3 | { 4 | services.slskd = { 5 | enable = true; 6 | settings.shares.directories = [ 7 | "/mnt/media/Music" 8 | ]; 9 | 10 | settings.global.upload = { 11 | speed_limit = 2000; 12 | slots = 5; 13 | }; 14 | 15 | settings.directories = { 16 | downloads = "/mnt/slskd/downloads"; 17 | incomplete = "/mnt/slskd/incomplete"; 18 | }; 19 | domain = "soulseek.service.home.arpa"; 20 | environmentFile = /var/lib/slskd/env; 21 | # Figure out how to use SOPS secrets: 22 | # environmentFile = config.sops.secrets.slskd.path; 23 | }; 24 | 25 | networking.firewall = { 26 | enable = true; 27 | allowedTCPPorts = [ 50300 5030 ]; 28 | }; 29 | 30 | fileSystems."/mnt/slskd" = { 31 | device = "192.168.5.6:/mnt/tank/app-data/slskd"; 32 | fsType = "nfs"; 33 | options = [ 34 | "defaults" # → rw,suid,dev,exec,auto,nouser,async 35 | "vers=4" # force NFSv3 36 | "proto=tcp" # use TCP transport 37 | "intr" # allow signals (Ctrl-C) to interrupt 38 | "timeo=30" # initial timeout = 3 s (30 deciseconds) 39 | "retrans=3" # retry only 3 times (~9 s total) 40 | "_netdev" # wait for network before mounting 41 | ]; 42 | }; 43 | 44 | # sops.secrets = { 45 | # slskd = { }; 46 | # }; 47 | } 48 | -------------------------------------------------------------------------------- /config/profiles/pc/openai-codex.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | 3 | let 4 | codexRepo = pkgs.fetchFromGitHub { 5 | owner = "openai"; 6 | repo = "codex"; 7 | rev = "8f1ea7fa855cc2a130824a272a35dcd031ff519b"; 8 | sha256 = "sha256-gHk2ydublBWuHqli11Dj5C9en2HtwTtwxhuayVHbcXs="; 9 | }; 10 | 11 | codex-cli = pkgs.stdenv.mkDerivation (finalAttrs: { 12 | pname = "codex-cli"; 13 | version = "0.1.0"; 14 | src = codexRepo; 15 | sourceRoot = finalAttrs.src.name; 16 | nativeBuildInputs = with pkgs; [ 17 | nodejs 18 | pnpm 19 | pnpm.configHook 20 | makeWrapper 21 | ]; 22 | 23 | pnpmDeps = pkgs.pnpm.fetchDeps { 24 | inherit (finalAttrs) pname version src; 25 | hash = "sha256-cCl+T9IySBj3TDpAcg8jYCOSHTu5vh5rMU5LOCqX8G8="; 26 | fetcherVersion = 9; 27 | }; 28 | 29 | buildPhase = '' 30 | cd codex-cli 31 | pnpm install --frozen-lockfile 32 | pnpm run build 33 | ''; 34 | 35 | installPhase = '' 36 | runHook preInstall 37 | 38 | mkdir -p $out/bin 39 | mkdir -p $out/lib 40 | 41 | install -Dm644 dist/cli.js $out/lib/cli.js 42 | makeWrapper ${pkgs.nodejs}/bin/node $out/bin/codex \ 43 | --add-flags "$out/lib/cli.js" 44 | 45 | runHook postInstall 46 | ''; 47 | }); 48 | in 49 | { 50 | environment.systemPackages = [ 51 | codex-cli 52 | ]; 53 | } 54 | -------------------------------------------------------------------------------- /installer/templates/server-disk-config.nix: -------------------------------------------------------------------------------- 1 | { lib, ... }: 2 | { 3 | disko.devices = { 4 | disk.disk1 = { 5 | device = "/dev/sda"; 6 | type = "disk"; 7 | content = { 8 | type = "gpt"; 9 | partitions = { 10 | boot = { 11 | name = "boot"; 12 | size = "1M"; 13 | type = "EF02"; 14 | }; 15 | esp = { 16 | name = "ESP"; 17 | size = "500M"; 18 | type = "EF00"; 19 | content = { 20 | type = "filesystem"; 21 | format = "vfat"; 22 | mountpoint = "/boot"; 23 | }; 24 | }; 25 | root = { 26 | name = "root"; 27 | size = "100%"; 28 | content = { 29 | type = "lvm_pv"; 30 | vg = "pool"; 31 | }; 32 | }; 33 | }; 34 | }; 35 | }; 36 | lvm_vg = { 37 | pool = { 38 | type = "lvm_vg"; 39 | lvs = { 40 | root = { 41 | size = "100%FREE"; 42 | content = { 43 | type = "filesystem"; 44 | format = "ext4"; 45 | mountpoint = "/"; 46 | mountOptions = [ 47 | "defaults" 48 | ]; 49 | }; 50 | }; 51 | }; 52 | }; 53 | }; 54 | }; 55 | } -------------------------------------------------------------------------------- /config/machines/servers/test-vm/disk-config.nix: -------------------------------------------------------------------------------- 1 | { lib, ... }: 2 | { 3 | disko.devices = { 4 | disk.disk1 = { 5 | device = "/dev/sda"; 6 | type = "disk"; 7 | content = { 8 | type = "gpt"; 9 | partitions = { 10 | boot = { 11 | name = "boot"; 12 | size = "1M"; 13 | type = "EF02"; 14 | }; 15 | esp = { 16 | name = "ESP"; 17 | size = "500M"; 18 | type = "EF00"; 19 | content = { 20 | type = "filesystem"; 21 | format = "vfat"; 22 | mountpoint = "/boot"; 23 | }; 24 | }; 25 | root = { 26 | name = "root"; 27 | size = "100%"; 28 | content = { 29 | type = "lvm_pv"; 30 | vg = "pool"; 31 | }; 32 | }; 33 | }; 34 | }; 35 | }; 36 | lvm_vg = { 37 | pool = { 38 | type = "lvm_vg"; 39 | lvs = { 40 | root = { 41 | size = "100%FREE"; 42 | content = { 43 | type = "filesystem"; 44 | format = "ext4"; 45 | mountpoint = "/"; 46 | mountOptions = [ 47 | "defaults" 48 | ]; 49 | }; 50 | }; 51 | }; 52 | }; 53 | }; 54 | }; 55 | } 56 | -------------------------------------------------------------------------------- /config/machines/servers/accompaniment-of-shadows/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | 3 | { 4 | imports = [ 5 | ./hardware.nix 6 | 7 | ./coredns.nix 8 | ./filebrowser.nix 9 | ./homebox.nix 10 | ./homepage.nix 11 | ./lubelogger.nix 12 | ./observability.nix 13 | ./paperless-ngx.nix 14 | ./planka.nix 15 | ./postgresql.nix 16 | ./redis.nix 17 | ./sabnzbd.nix 18 | ./servarr.nix 19 | ./tailscale.nix 20 | 21 | ../../../profiles/virtual-machine 22 | ]; 23 | 24 | networking = { 25 | hostName = "accompaniment-of-shadows"; 26 | firewall.allowedTCPPorts = [ 53 80 8080 ]; 27 | firewall.allowedUDPPorts = [ 53 ]; 28 | }; 29 | 30 | services = { 31 | nginx = { 32 | enable = true; 33 | 34 | recommendedGzipSettings = true; 35 | recommendedOptimisation = true; 36 | recommendedProxySettings = true; 37 | }; 38 | 39 | nginx.virtualHosts = { 40 | "qbittorrent.service.home.arpa" = { 41 | locations."/" = { 42 | proxyPass = "http://192.168.5.104:8081"; 43 | }; 44 | }; 45 | }; 46 | }; 47 | 48 | virtualisation = { 49 | containers = { 50 | enable = true; 51 | }; 52 | 53 | docker = { 54 | enable = true; 55 | storageDriver = "overlay2"; 56 | }; 57 | oci-containers.backend = "docker"; 58 | }; 59 | 60 | primary-user.extraGroups = [ "docker" ]; 61 | } 62 | -------------------------------------------------------------------------------- /config/modules/ui/st/0003-Change-Font-Size-Shortcuts.patch: -------------------------------------------------------------------------------- 1 | From 5e0ce42bd8be5fcac042f64575cc9041a5fae143 Mon Sep 17 00:00:00 2001 2 | From: solomon 3 | Date: Thu, 4 Jan 2024 13:36:51 -0800 4 | Subject: [PATCH 3/3] Use +,-,0 for font size. 5 | 6 | --- 7 | config.def.h | 6 +++--- 8 | 1 file changed, 3 insertions(+), 3 deletions(-) 9 | 10 | diff --git a/config.def.h b/config.def.h 11 | index fb828ff..f899f33 100644 12 | --- a/config.def.h 13 | +++ b/config.def.h 14 | @@ -192,9 +192,9 @@ static Shortcut shortcuts[] = { 15 | { ControlMask, XK_Print, toggleprinter, {.i = 0} }, 16 | { ShiftMask, XK_Print, printscreen, {.i = 0} }, 17 | { XK_ANY_MOD, XK_Print, printsel, {.i = 0} }, 18 | - { TERMMOD, XK_Prior, zoom, {.f = +1} }, 19 | - { TERMMOD, XK_Next, zoom, {.f = -1} }, 20 | - { TERMMOD, XK_Home, zoomreset, {.f = 0} }, 21 | + { TERMMOD, XK_plus, zoom, {.f = +1} }, 22 | + { ControlMask, XK_minus, zoom, {.f = -1} }, 23 | + { ControlMask, XK_0, zoomreset, {.f = 0} }, 24 | { TERMMOD, XK_C, clipcopy, {.i = 0} }, 25 | { TERMMOD, XK_V, clippaste, {.i = 0} }, 26 | { TERMMOD, XK_Y, selpaste, {.i = 0} }, 27 | -- 28 | 2.42.0 29 | 30 | -------------------------------------------------------------------------------- /config/machines/servers/gnostic-ascension/disk-config2.nix: -------------------------------------------------------------------------------- 1 | # Example to create a bios compatible gpt partition 2 | { lib, ... }: 3 | { 4 | disko.devices = { 5 | disk.disk1 = { 6 | device = "/dev/vda"; 7 | type = "disk"; 8 | content = { 9 | type = "gpt"; 10 | partitions = { 11 | boot = { 12 | name = "boot"; 13 | size = "1M"; 14 | type = "EF02"; 15 | }; 16 | esp = { 17 | name = "ESP"; 18 | size = "500M"; 19 | type = "EF00"; 20 | content = { 21 | type = "filesystem"; 22 | format = "vfat"; 23 | mountpoint = "/boot"; 24 | }; 25 | }; 26 | root = { 27 | name = "root"; 28 | size = "100%"; 29 | content = { 30 | type = "lvm_pv"; 31 | vg = "pool"; 32 | }; 33 | }; 34 | }; 35 | }; 36 | }; 37 | lvm_vg = { 38 | pool = { 39 | type = "lvm_vg"; 40 | lvs = { 41 | root = { 42 | size = "100%FREE"; 43 | content = { 44 | type = "filesystem"; 45 | format = "ext4"; 46 | mountpoint = "/"; 47 | mountOptions = [ 48 | "defaults" 49 | ]; 50 | }; 51 | }; 52 | }; 53 | }; 54 | }; 55 | }; 56 | } 57 | -------------------------------------------------------------------------------- /config/profiles/virtual-machine/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | 3 | { 4 | imports = [ 5 | ../../../modules 6 | 7 | ../../modules/data/session-vars 8 | 9 | ../../modules/security/sshd 10 | 11 | ../../modules/system/nix-binary-caches 12 | ../../modules/system/nixpkgs 13 | ../../modules/system/systemd-boot 14 | ../../modules/system/timezone 15 | ../../modules/system/users 16 | 17 | ../../modules/services/tailscale 18 | 19 | ../../modules/ui/git 20 | ../../modules/ui/starship 21 | ../../modules/ui/zsh 22 | ]; 23 | 24 | primary-user.name = "solomon"; 25 | 26 | nix.extraOptions = '' 27 | experimental-features = nix-command flakes 28 | ''; 29 | 30 | primary-user.home-manager = { 31 | imports = [ 32 | ../../modules/packages/server-cli-tools.nix 33 | ]; 34 | }; 35 | 36 | system.stateVersion = "22.05"; 37 | nix.settings.trusted-users = [ "@wheel" ]; 38 | environment.shells = [ pkgs.zsh pkgs.bashInteractive ]; 39 | 40 | environment.systemPackages = with pkgs; [ 41 | # System-level tools needed for NixOS functionality 42 | cachix 43 | direnv 44 | git 45 | gnugrep 46 | inetutils 47 | zlib 48 | zsh 49 | zsh-syntax-highlighting 50 | ]; 51 | 52 | home-manager.useGlobalPkgs = true; 53 | home-manager.useUserPackages = true; 54 | 55 | services.prometheus.exporters = { 56 | node = { 57 | enable = true; 58 | enabledCollectors = [ "systemd" ]; 59 | port = 9002; 60 | }; 61 | }; 62 | 63 | networking.firewall.allowedTCPPorts = [ 9002 ]; 64 | } 65 | -------------------------------------------------------------------------------- /config/machines/personal-computers/voice-of-evening/zfs.nix: -------------------------------------------------------------------------------- 1 | # Sending and Receiving ZFS Data: 2 | # https://docs.oracle.com/cd/E18752_01/html/819-5461/gbchx.html 3 | # 4 | # Required Target Permission: 5 | # compression,create,destroy,hold,mount,mountpoint,receive,refreservation,release,rollback,send,snapshot 6 | # https://github.com/jimsalterjrs/sanoid/issues/660 7 | { config, options, ... }: 8 | 9 | { 10 | services.zfs = { 11 | autoScrub.enable = true; 12 | autoSnapshot = { 13 | enable = true; 14 | frequent = 4; 15 | hourly = 24; 16 | weekly = 4; 17 | monthly = 3; 18 | }; 19 | trim.enable = true; 20 | }; 21 | 22 | services.syncoid = { 23 | enable = false; 24 | sshKey = config.sops.secrets.syncoid-ssh-key.path; 25 | commands = { 26 | "tank/home" = { 27 | target = "syncoid@sandra-voi.home.arpa:tank/system-snapshots/voice-of-evening/home"; 28 | recursive = true; 29 | }; 30 | "tank/root" = { 31 | target = "syncoid@sandra-voi.home.arpa:tank/system-snapshots/voice-of-evening/root"; 32 | recursive = true; 33 | }; 34 | }; 35 | }; 36 | 37 | # "Exactly one of users.users.syncoid.isSystemUser and users.users.syncoid.isNormalUser must be set." 38 | # WTF these should be defined already: 39 | # https://github.com/NixOS/nixpkgs/blob/nixos-unstable/nixos/modules/services/backup/syncoid.nix#L343 40 | users.users.syncoid.group = "syncoid"; 41 | users.users.syncoid.isSystemUser = true; 42 | users.groups.syncoid = {}; 43 | 44 | users.users.syncoid.extraGroups = [ "keys" ]; 45 | } 46 | -------------------------------------------------------------------------------- /config/machines/servers/sower/default.nix: -------------------------------------------------------------------------------- 1 | { config, pkgs, ... }: 2 | 3 | { 4 | imports = [ 5 | ./hardware.nix 6 | #./hoogle.nix 7 | ./home-assistant.nix 8 | ./immich.nix 9 | ./jellyfin.nix 10 | #./podgrab.nix 11 | ./soulseek.nix 12 | ./navidrome.nix 13 | ./tubearchivist.nix 14 | 15 | ../../../profiles/physical-machine 16 | ]; 17 | 18 | nix.extraOptions = '' 19 | experimental-features = nix-command flakes 20 | ''; 21 | 22 | environment.systemPackages = [ 23 | pkgs.libva 24 | pkgs.postgresql 25 | ]; 26 | 27 | primary-user.name = "solomon"; 28 | 29 | networking = { 30 | hostName = "sower"; 31 | hostId = "960855f8"; 32 | interfaces.eno1.useDHCP = true; 33 | useDHCP = false; 34 | }; 35 | 36 | services.nginx = { 37 | enable = true; 38 | 39 | recommendedGzipSettings = true; 40 | recommendedOptimisation = true; 41 | recommendedProxySettings = true; 42 | }; 43 | 44 | # 8081 == podgrab 45 | # 8082 == FreshRSS 46 | # 8083 == Hoogle 47 | # 8096 == Jellyfin 48 | 49 | networking.firewall.allowedTCPPorts = [ 80 9002 3000]; 50 | 51 | services.prometheus.exporters = { 52 | node = { 53 | enable = true; 54 | enabledCollectors = [ "systemd" ]; 55 | port = 9002; 56 | }; 57 | }; 58 | 59 | virtualisation = { 60 | containers = { 61 | enable = true; 62 | }; 63 | 64 | docker = { 65 | enable = true; 66 | storageDriver = "overlay2"; 67 | }; 68 | oci-containers.backend = "docker"; 69 | }; 70 | 71 | primary-user.extraGroups = [ "docker" ]; 72 | } 73 | -------------------------------------------------------------------------------- /config/machines/personal-computers/lorean/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | 3 | { 4 | imports = [ 5 | ./hardware.nix 6 | ./arduino.nix 7 | #./immich-sdcard-sync.nix 8 | ./kmonad.nix 9 | ./zfs.nix 10 | 11 | ../../../profiles/pc 12 | #../../../modules/services/virtualisation/libvirt 13 | ../../../modules/services/virtualisation/virtualbox 14 | ../../../modules/system/devices/touchpad 15 | ../../../modules/system/powertop 16 | ]; 17 | 18 | nix.extraOptions = '' 19 | experimental-features = nix-command flakes 20 | ''; 21 | 22 | environment.systemPackages = [ 23 | pkgs.acpi 24 | (pkgs.agda.withPackages (p: [ p._1lab p.standard-library ])) 25 | pkgs.nfs-utils 26 | pkgs.ntfs3g 27 | ]; 28 | 29 | primary-user.name = "solomon"; 30 | 31 | sops.secrets.syncoid-ssh-key = { 32 | owner = "syncoid"; 33 | mode = "600"; 34 | }; 35 | 36 | systemd.services.nvidia-control-devices.enable = false; 37 | 38 | # "...run the OOM killer earlier, but paradoxically that can help clean up wasteful resources and avoid a situation where you end up swamped at the limit" --jkachmar 39 | services.earlyoom.enable = true; 40 | 41 | services.printing.enable = true; 42 | 43 | services.rpcbind.enable = true; 44 | 45 | services.avahi = { 46 | enable = true; 47 | nssmdns4 = true; 48 | openFirewall = true; 49 | }; 50 | 51 | networking = { 52 | hostName = "lorean"; 53 | hostId = "960855f8"; 54 | networkmanager.enable = true; 55 | 56 | useDHCP = false; 57 | interfaces.enp0s31f6.useDHCP = true; 58 | interfaces.wlp4s0.useDHCP = true; 59 | }; 60 | } 61 | -------------------------------------------------------------------------------- /modules/nixos/primary-user.nix: -------------------------------------------------------------------------------- 1 | { config, lib, pkgs, ... }: 2 | let 3 | cfg = config.primary-user; 4 | in 5 | { 6 | options.primary-user.name = lib.mkOption { 7 | type = lib.types.nullOr lib.types.str; 8 | default = null; 9 | description = "The name of the primary user account."; 10 | }; 11 | 12 | imports = [ 13 | (lib.mkAliasOptionModule [ "primary-user" "home-manager" ] [ "home-manager" "users" cfg.name ]) 14 | (lib.mkAliasOptionModule [ "primary-user" "home" ] [ "users" "users" cfg.name "home" ]) 15 | (lib.mkAliasOptionModule [ "primary-user" "shell" ] [ "users" "users" cfg.name "shell" ]) 16 | (lib.mkAliasOptionModule [ "primary-user" "extraGroups" ] [ "users" "users" cfg.name "extraGroups" ]) 17 | (lib.mkAliasOptionModule [ "primary-user" "uid" ] [ "users" "users" cfg.name "uid" ]) 18 | (lib.mkAliasOptionModule [ "primary-user" "openssh" ] [ "users" "users" cfg.name "openssh" ]) 19 | (lib.mkAliasOptionModule [ "primary-user" "isNormalUser" ] [ "users" "users" cfg.name "isNormalUser" ]) 20 | (lib.mkAliasOptionModule [ "primary-user" "hashedPasswordFile" ] [ "users" "users" cfg.name "hashedPasswordFile" ]) 21 | ]; 22 | 23 | config = lib.mkIf (cfg.name != null) { 24 | sops.secrets.primary-user-password.neededForUsers = true; 25 | primary-user = { 26 | extraGroups = [ "wheel" "users" "keys" "input" "plugdev" "dialout" ]; 27 | 28 | home-manager = { 29 | home.username = cfg.name; 30 | home.homeDirectory = "/home/${cfg.name}"; 31 | home.stateVersion = "21.03"; 32 | }; 33 | 34 | isNormalUser = true; 35 | hashedPasswordFile = config.sops.secrets.primary-user-password.path; 36 | shell = pkgs.zsh; 37 | uid = lib.mkDefault 1000; 38 | }; 39 | 40 | programs.zsh.enable = true; 41 | 42 | nix.settings.trusted-users = [ cfg.name ]; 43 | }; 44 | } 45 | -------------------------------------------------------------------------------- /config/modules/ui/dunst/home.nix: -------------------------------------------------------------------------------- 1 | { ... }: 2 | { 3 | services.dunst = { 4 | enable = true; 5 | settings = { 6 | global = { 7 | font = "Monospace 6"; 8 | follow = "mouse"; 9 | geometry = "300x5+1610+30"; 10 | indicate_hidden = "yes"; 11 | shrunk = "no"; 12 | notification_height = 0; 13 | separator_height = 2; 14 | padding = 8; 15 | horizontal_padding = 8; 16 | frame_width = 1; 17 | separator_color = "frame"; 18 | markup = "full"; 19 | format = "%s\n%b"; 20 | alignment = "left"; 21 | show_age_threshold = 60; 22 | word_wrap = "yes"; 23 | ellipsize = "middle"; 24 | ignore_newline = "no"; 25 | stack_duplicates = true; 26 | hide_duplicate_count = false; 27 | show_indicators = "yes"; 28 | icon_position = "left"; 29 | max_icon_size = 32; 30 | sticky_history = "yes"; 31 | history_length = 20; 32 | dmenu = "/usr/bin/dmenu -p dunst"; 33 | browser = "/usr/bin/firefox -new-tab"; 34 | always_run_script = true; 35 | title = "Dunst"; 36 | class = "Dunst"; 37 | startup_notification = false; 38 | force_xinerama = false; 39 | }; 40 | 41 | experimental.per_monitor_dpi = false; 42 | 43 | urgency_low = { 44 | frame_color = "#99cc99"; 45 | background = "#2d2d2d"; 46 | foreground = "#cccccc"; 47 | timeout = 10; 48 | }; 49 | 50 | urgency_normal = { 51 | frame_color = "#f99157"; 52 | background = "#2d2d2d"; 53 | foreground = "#cccccc"; 54 | timeout = 10; 55 | }; 56 | 57 | urgency_critical = { 58 | #frame_color = "#f2777a"; 59 | frame_color = "#ff0000"; 60 | background = "#2d2d2d"; 61 | foreground = "#cccccc"; 62 | timeout = 0; 63 | }; 64 | }; 65 | }; 66 | } 67 | 68 | -------------------------------------------------------------------------------- /config/modules/ui/xserver/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | 3 | { 4 | services.displayManager = { 5 | defaultSession = "none+xmonad"; 6 | }; 7 | 8 | services.displayManager = { 9 | ly.enable = true; 10 | }; 11 | services.xserver = { 12 | enable = true; 13 | xkb = { 14 | layout = "us"; 15 | options = "ctrl:nocaps"; 16 | }; 17 | xautolock = { 18 | time = 15; 19 | enable = false; 20 | locker = "${pkgs.betterlockscreen}/bin/betterlockscreen --lock"; 21 | }; 22 | desktopManager.xfce.enable = true; 23 | windowManager.session = [ 24 | { 25 | name = "xmonad"; 26 | start = '' 27 | /usr/bin/env xmonad-solomon & 28 | waitPID=$! 29 | ''; 30 | } 31 | ]; 32 | # The names of the video drivers the configuration supports. They will be 33 | # tried in order until one that supports your card is found. Don’t combine 34 | # those with “incompatible” OpenGL implementations, e.g. free ones 35 | # (mesa-based) with proprietary ones. 36 | videoDrivers = [ 37 | "nvidia" 38 | "modesetting" 39 | "fbdev" 40 | ]; 41 | }; 42 | 43 | systemd.services.nvidia-control-devices = { 44 | wantedBy = [ "multi-user.target" ]; 45 | serviceConfig.ExecStart = "${pkgs.linuxPackages.nvidia_x11.bin}/bin/nvidia-smi"; 46 | }; 47 | 48 | environment.systemPackages = [ 49 | pkgs.haskellPackages.xmonad-solomon 50 | ]; 51 | 52 | # https://mynixos.com/home-manager/option/xdg.configFile.%3Cname%3E.source 53 | primary-user.home-manager.xdg.configFile."startup.sh".text = '' 54 | ${pkgs.networkmanagerapplet}/bin/nm-applet 55 | ${pkgs.feh}/bin/feh --bg-scale ${./wallpapers/Yosemite-Color-Block.png} 56 | ${pkgs.xbanish}/bin/xbanish 57 | ${pkgs.dunst}/bin/dunst 58 | ${pkgs.batsignal}/bin/batsignal -b -W \"Warnings: Battery Low\" 59 | ${pkgs.volume-bar}/bin/volume-bar 60 | ${pkgs.brightness-bar}/bin/brightness-bar 61 | ''; 62 | } 63 | -------------------------------------------------------------------------------- /installer/configuration.nix: -------------------------------------------------------------------------------- 1 | # Default system config for fresh machines. 2 | { config, pkgs, lib, ... }: 3 | 4 | { 5 | imports = [ ./hardware-configuration.nix ]; 6 | 7 | boot.loader.systemd-boot.enable = true; 8 | boot.loader.efi.canTouchEfiVariables = true; 9 | 10 | services.openssh = { 11 | enable = true; 12 | passwordAuthentication = false; 13 | extraConfig = "PermitUserEnvironment yes"; 14 | }; 15 | 16 | programs.ssh.startAgent = true; 17 | 18 | services.sshd.enable = true; 19 | 20 | security.pam = { 21 | sshAgentAuth.enable = true; 22 | services.sudo.sshAgentAuth = true; 23 | }; 24 | 25 | users = { 26 | mutableUsers = false; 27 | users.solomon = { 28 | isNormalUser = true; 29 | extraGroups = [ "wheel" "networkmanager" "keys" ]; 30 | openssh.authorizedKeys.keys = [ 31 | "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHedhPWMgsGFQS7niiFlgkCty/0yS68tVP0pm4x4PQLp solomon@nightshade" 32 | "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILVTeNwDsHZX06k+o+fz1wmI8h3q2ks+5C7Mv5ADXo+o solomon@lorean" 33 | "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKK3kMxgeA9ivLG/A81PNKhRJx32r7dzFnl+SZNhBc9K solomon@sower" 34 | "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMngowB0i16wwKP0j+L207+dr512n+rbfYsh/MAPD+PS solomon@apollyon" 35 | ]; 36 | passwordFile = "/etc/primary-user-password"; 37 | }; 38 | users.root.hashedPassword = "*"; 39 | }; 40 | 41 | nix.settings.trusted-users = [ "root" "solomon" ]; 42 | nix.extraOptions = '' 43 | experimental-features = nix-command flakes 44 | ''; 45 | 46 | users.users.root.openssh.authorizedKeys.keyFiles = [ /etc/ssh/authorized_keys.d/root ]; 47 | 48 | networking = { 49 | hostName = "nixos"; 50 | wireless.enable = true; 51 | useDHCP = lib.mkDefault true; 52 | # This gets replaced with a generated hostId in the install scripts 53 | hostId = "997f3c8d"; 54 | }; 55 | 56 | environment.systemPackages = [ pkgs.vim pkgs.git ]; 57 | 58 | system.stateVersion = "22.05"; 59 | } 60 | -------------------------------------------------------------------------------- /.sops.yaml: -------------------------------------------------------------------------------- 1 | # NOTE: use 'sops updatekeys --yes secrets.yaml' to add/remove recipients. 2 | keys: 3 | - &solomon age1nq3ecxhfnr4j656gydwmedlrrwj0huuzel0lhu32hq02xwcjzajsfz4s96 4 | - &solomon_lorean age1dx0vjma76vhq0qqvgufh6vcv30r3vvugr5stvgu25yhwjej2g5cstkqxtk 5 | - &solomon_zodiacal-light age16z6z5squmky3vtttnk42fmrufj55hz0kmqnxncg4yswqj788y34sywedrq 6 | - &server_accompaniment-of-shadows age1m34247m4q466wdxrg729ytss0n4jqwa29577m5u2w7rnytyep50s8wuusn 7 | - &server_apollyon age1rd3mdchrf5lnkcl55ve7jq30rptaw7g7qu9jn940uawqu2saadzspr3e7x 8 | - &server_madonna-of-the-wasps age1x0ky8x43artkyr2w47ca6muy7pqr9h57l5284aex6wftltj6u45q6kc6g4 9 | - &server_silence-under-snow age1u4gshp9w29annppw98fyh8wpssj3n324xmm9rvqdkt2mx62tkesq30dcjk 10 | - &server_sower age1h2nlwkzquf4c8ltwuqey2y9z6cpumxw42q8x9qu0dwzx638vtd0scpm80c 11 | - &server_storm-bird age14qgly6gwzf0sc4sfchwkaesd6u4gl4mksjhx2reapnxz7p25kugshk0x2f 12 | - &server_transfigured-night age1cw04hz668dw2gxd8dkpyzk7yjwushtg9v77qzyhn9jgtfl4xm46s2jeexg 13 | - &server_gnostic-ascension age1dwxgs38t62urlqjlvkpun5zxv5skyh2r5p7awa7ye0r97zvyg97qlrgwzl 14 | - &pc_nightshade age1hm87e69thh5c4glk9w2gn2dn3mhexe7cen3c57upfg9zech9qe9sz0ha4j 15 | - &pc_lorean age1h9eclrfude5fkzvt0v7sjyhfsu5f839xw0wwh29lrptq70mfvgescqng6a 16 | - &pc_zodiacal-light age18ghrww7r6adve0aaeaj8gl2pz2hh3qpvc0l9w48nzw89yp4yngpscmqc3d 17 | - &pc_voice-of-evening age132waq5jzd28psft4shwcr2kzq3s4ckmwvpgta6nxmf8t02j4kvaqe43ms4 18 | 19 | creation_rules: 20 | - path_regex: secrets.yaml$ 21 | key_groups: 22 | - age: 23 | - *solomon 24 | - *solomon_lorean 25 | - *solomon_zodiacal-light 26 | - *server_accompaniment-of-shadows 27 | - *server_apollyon 28 | - *server_madonna-of-the-wasps 29 | - *server_silence-under-snow 30 | - *server_sower 31 | - *server_storm-bird 32 | - *server_transfigured-night 33 | - *server_gnostic-ascension 34 | - *pc_nightshade 35 | - *pc_lorean 36 | - *pc_zodiacal-light 37 | - *pc_voice-of-evening 38 | -------------------------------------------------------------------------------- /config/modules/services/syncthing/default.nix: -------------------------------------------------------------------------------- 1 | { config, lib, pkgs, ... }: 2 | 3 | let 4 | hostName = config.networking.hostName; 5 | otherMachineNames = 6 | lib.remove config.networking.hostName ( 7 | builtins.filter (machine: machine != "yellowstone.cofree.coffee") (builtins.attrNames (builtins.readDir ../../../machines)) 8 | ); 9 | 10 | syncthingMachineIds = { 11 | sower = "QR6FWXC-CE7YTWC-D5FTHRL-R74OZ2L-RV4JIWP-TWKBQ7A-ARP637O-LFBPRQK"; 12 | lorean = "BRI57JX-WTQMZJI-IN4PZJQ-QRXQLI2-USIIIYR-DAGJXMP-LDPDGDI-F7TOYQH"; 13 | nightshade = "YWQU6WT-7FKGWGT-UXMNJRT-DONET7A-ZJZ2D6H-AOQEP5S-4ZXZSLL-Y2VDWQ6"; 14 | phone = "3ORQWGG-OX63V27-B4OD2YV-IP54EQE-SUF6PQO-CVBIHBT-RI7DQPJ-2CGZ4AO"; 15 | }; 16 | in 17 | { 18 | boot.kernel.sysctl."fs.inotify.max_user_watches" = 1048576; 19 | 20 | services.syncthing = { 21 | enable = true; 22 | openDefaultPorts = true; 23 | user = config.primary-user.name; 24 | dataDir = config.primary-user.home-manager.home.homeDirectory; 25 | cert = "/secrets/${hostName}-syncthing-cert"; 26 | key = "/secrets/${hostName}-syncthing-key"; 27 | devices = lib.genAttrs otherMachineNames (machine: { 28 | id = syncthingMachineIds."${machine}"; 29 | }); 30 | folders = { 31 | Org = { 32 | path = "${config.primary-user.home}/Org"; 33 | devices = otherMachineNames; 34 | }; 35 | Public = { 36 | path = "${config.primary-user.home}/Public"; 37 | devices = otherMachineNames; 38 | }; 39 | }; 40 | }; 41 | 42 | systemd.services = { 43 | syncthing = { 44 | wants = [ 45 | "syncthing-cert-key.service" 46 | "syncthing-key-key.service" 47 | ]; 48 | after = [ 49 | "syncthing-cert-key.service" 50 | "syncthing-key-key.service" 51 | ]; 52 | }; 53 | 54 | syncthing-init.serviceConfig.ExecStartPost = pkgs.writeShellScript "rm-sync-dir" '' 55 | if [ -d "$HOME/Sync" ] 56 | then 57 | rmdir "$HOME/Sync" 58 | fi 59 | ''; 60 | }; 61 | } 62 | -------------------------------------------------------------------------------- /config/modules/ui/eww/scripts/sys_info: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ## Files and Data 4 | PREV_TOTAL=0 5 | PREV_IDLE=0 6 | cpuFile="/tmp/.cpu_usage" 7 | 8 | ## Get CPU usage 9 | get_cpu() { 10 | if [[ -f "${cpuFile}" ]]; then 11 | fileCont=$(cat "${cpuFile}") 12 | PREV_TOTAL=$(echo "${fileCont}" | head -n 1) 13 | PREV_IDLE=$(echo "${fileCont}" | tail -n 1) 14 | fi 15 | 16 | CPU=(`cat /proc/stat | grep '^cpu '`) # Get the total CPU statistics. 17 | unset CPU[0] # Discard the "cpu" prefix. 18 | IDLE=${CPU[4]} # Get the idle CPU time. 19 | 20 | # Calculate the total CPU time. 21 | TOTAL=0 22 | 23 | for VALUE in "${CPU[@]:0:4}"; do 24 | let "TOTAL=$TOTAL+$VALUE" 25 | done 26 | 27 | if [[ "${PREV_TOTAL}" != "" ]] && [[ "${PREV_IDLE}" != "" ]]; then 28 | # Calculate the CPU usage since we last checked. 29 | let "DIFF_IDLE=$IDLE-$PREV_IDLE" 30 | let "DIFF_TOTAL=$TOTAL-$PREV_TOTAL" 31 | let "DIFF_USAGE=(1000*($DIFF_TOTAL-$DIFF_IDLE)/$DIFF_TOTAL+5)/10" 32 | echo "${DIFF_USAGE}" 33 | else 34 | echo "?" 35 | fi 36 | 37 | # Remember the total and idle CPU times for the next check. 38 | echo "${TOTAL}" > "${cpuFile}" 39 | echo "${IDLE}" >> "${cpuFile}" 40 | } 41 | 42 | ## Get Used memory 43 | get_mem() { 44 | printf "%.0f\n" $(free -m | grep Mem | awk '{print ($3/$2)*100}') 45 | } 46 | 47 | ## Get Brightness 48 | get_blight() { 49 | CARD=`ls /sys/class/backlight | head -n 1` 50 | 51 | if [[ "$CARD" == *"intel_"* ]]; then 52 | BNESS=`xbacklight -get` 53 | LIGHT=${BNESS%.*} 54 | else 55 | BNESS=`blight -d $CARD get brightness` 56 | PERC="$(($BNESS*100/255))" 57 | LIGHT=${PERC%.*} 58 | fi 59 | 60 | echo "$LIGHT" 61 | } 62 | 63 | ## Get Battery 64 | get_battery() { 65 | BAT=`ls /sys/class/power_supply | grep BAT | head -n 1` 66 | cat /sys/class/power_supply/${BAT}/capacity 67 | } 68 | 69 | ## Execute accordingly 70 | if [[ "$1" == "--cpu" ]]; then 71 | get_cpu 72 | elif [[ "$1" == "--mem" ]]; then 73 | get_mem 74 | elif [[ "$1" == "--blight" ]]; then 75 | get_blight 76 | elif [[ "$1" == "--bat" ]]; then 77 | get_battery 78 | fi 79 | -------------------------------------------------------------------------------- /config/modules/ui/eww/scripts/music_info: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ## Get data 4 | STATUS="$(mpc status)" 5 | COVER="/tmp/.music_cover.jpg" 6 | MUSIC_DIR="$HOME/Music" 7 | 8 | ## Get status 9 | get_status() { 10 | if [[ $STATUS == *"[playing]"* ]]; then 11 | echo "" 12 | else 13 | echo "喇" 14 | fi 15 | } 16 | 17 | ## Get song 18 | get_song() { 19 | song=`mpc -f %title% current` 20 | if [[ -z "$song" ]]; then 21 | echo "Offline" 22 | else 23 | echo "$song" 24 | fi 25 | } 26 | 27 | ## Get artist 28 | get_artist() { 29 | artist=`mpc -f %artist% current` 30 | if [[ -z "$artist" ]]; then 31 | echo "Offline" 32 | else 33 | echo "$artist" 34 | fi 35 | } 36 | 37 | ## Get time 38 | get_time() { 39 | time=`mpc status | grep "%)" | awk '{print $4}' | tr -d '(%)'` 40 | if [[ -z "$time" ]]; then 41 | echo "0" 42 | else 43 | echo "$time" 44 | fi 45 | } 46 | get_ctime() { 47 | ctime=`mpc status | grep "#" | awk '{print $3}' | sed 's|/.*||g'` 48 | if [[ -z "$ctime" ]]; then 49 | echo "0:00" 50 | else 51 | echo "$ctime" 52 | fi 53 | } 54 | get_ttime() { 55 | ttime=`mpc -f %time% current` 56 | if [[ -z "$ttime" ]]; then 57 | echo "0:00" 58 | else 59 | echo "$ttime" 60 | fi 61 | } 62 | 63 | ## Get cover 64 | get_cover() { 65 | ffmpeg -i "${MUSIC_DIR}/$(mpc current -f %file%)" "${COVER}" -y &> /dev/null 66 | STATUS=$? 67 | 68 | # Check if the file has a embbeded album art 69 | if [ "$STATUS" -eq 0 ];then 70 | echo "$COVER" 71 | else 72 | echo "images/music.png" 73 | fi 74 | } 75 | 76 | ## Execute accordingly 77 | if [[ "$1" == "--song" ]]; then 78 | get_song 79 | elif [[ "$1" == "--artist" ]]; then 80 | get_artist 81 | elif [[ "$1" == "--status" ]]; then 82 | get_status 83 | elif [[ "$1" == "--time" ]]; then 84 | get_time 85 | elif [[ "$1" == "--ctime" ]]; then 86 | get_ctime 87 | elif [[ "$1" == "--ttime" ]]; then 88 | get_ttime 89 | elif [[ "$1" == "--cover" ]]; then 90 | get_cover 91 | elif [[ "$1" == "--toggle" ]]; then 92 | mpc -q toggle 93 | elif [[ "$1" == "--next" ]]; then 94 | { mpc -q next; get_cover; } 95 | elif [[ "$1" == "--prev" ]]; then 96 | { mpc -q prev; get_cover; } 97 | fi 98 | -------------------------------------------------------------------------------- /config/machines/personal-computers/nightshade/kmonad.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | 3 | let 4 | config-file = device: pkgs.writeText "kmonad.cfg" 5 | '' 6 | (defcfg 7 | input (device-file "${device}") 8 | output (uinput-sink "internal-keyboard") 9 | allow-cmd true 10 | fallthrough true 11 | ) 12 | 13 | (defsrc 14 | esc f1 f2 f3 f4 f5 f6 f7 f8 f9 f10 f11 f12 home end ins del 15 | grv 1 2 3 4 5 6 7 8 9 0 - = bspc 16 | tab q w e r t y u i o p [ ] \ 17 | caps a s d f g h j k l ; ' ret 18 | lsft z x c v b n m , . / rsft 19 | lctl lalt lmet spc ralt prnt rctl pgup up pgdn 20 | left down rght 21 | ) 22 | 23 | (defalias 24 | tmt (tap-next tab lmet) 25 | \mt (tap-next \ rmet) 26 | xcp (tap-next esc lctl) 27 | ) 28 | 29 | (deflayer test 30 | esc f1 f2 f3 f4 f5 f6 f7 f8 f9 f10 f11 f12 home end ins del 31 | grv 1 2 3 4 5 6 7 8 9 0 - = bspc 32 | @tmt q w e r t y u i o p [ ] @\mt 33 | @xcp a s d f g h j k l ; ' ret 34 | lsft z x c v b n m , . / rsft 35 | lctl lalt lmet spc ralt prnt rctl pgup up pgdn 36 | left down rght 37 | ) 38 | ''; 39 | in 40 | { 41 | systemd.user.services.kmonad-internal = { 42 | Unit = { 43 | description = "kmonad internal keyboard config"; 44 | }; 45 | 46 | Install = { 47 | WantedBy = [ "default.target" ]; 48 | }; 49 | 50 | Service = { 51 | Restart = "always"; 52 | RestartSec = "3"; 53 | ExecStart = "${pkgs.kmonad}/bin/kmonad ${config-file "/dev/input/by-path/platform-i8042-serio-0-event-kbd"}"; 54 | }; 55 | }; 56 | } 57 | -------------------------------------------------------------------------------- /config/modules/ui/kmonad/default.nix: -------------------------------------------------------------------------------- 1 | { ... }: 2 | 3 | { 4 | primary-user.home-manager.programs.kmonad = { 5 | enable = true; 6 | 7 | defcfg = '' 8 | (defcfg 9 | input (device-file "/dev/input/by-id/usb-HID_Keyboard_HID_Keyboard-event-kbd") 10 | output (uinput-sink "My Kmonad Output") 11 | allow-cmd true 12 | ) 13 | ''; 14 | 15 | defsrc = '' 16 | (defsrc 17 | esc f1 f2 f3 f4 f5 f6 f7 f8 f9 f10 f11 f12 prnt slck pause 18 | grv 1 2 3 4 5 6 7 8 9 0 - = bspc ins home pgup 19 | tab q w e r t y u i o p [ ] \ del end pgdn 20 | caps a s d f g h j k l ; ' ret 21 | lsft z x c v b n m , . / rsft up 22 | lctl lalt lmet spc ralt menu rctrl left down right 23 | ) 24 | ''; 25 | 26 | defaliases = '' 27 | (defalias 28 | tmt (tap-next tab lmet) 29 | \mt (tap-next \ rmet) 30 | xcp (tap-next esc lctl) 31 | ;; @tmt = TH 150 tab lmet 32 | ;; @\mt = TH 150 \ rmet 33 | ;; @xcp = TH 150 esc lctl 34 | ) 35 | ''; 36 | 37 | deflayers = '' 38 | (deflayer layer1 39 | esc f1 f2 f3 f4 f5 f6 f7 f8 f9 f10 f11 f12 prnt slck pause 40 | grv 1 2 3 4 5 6 7 8 9 0 - = bspc ins home pgup 41 | @tmt q w e r t y u i o p [ ] @\mt del end pgdn 42 | @xcp a s d f g h j k l ; ' ret 43 | lsft z x c v b n m , . / rsft up 44 | lctl lalt lmet spc ralt menu rctrl left down right 45 | ) 46 | ''; 47 | }; 48 | 49 | primary-user.extraGroups = [ "uinput" "input" ]; 50 | 51 | users.groups = { uinput = { }; }; 52 | boot.kernelModules = [ "uinput" ]; 53 | services.udev.extraRules = '' 54 | # KMonad user access to /dev/uinput 55 | KERNEL=="uinput", MODE="0660", GROUP="uinput", OPTIONS+="static_node=uinput" 56 | ''; 57 | } 58 | -------------------------------------------------------------------------------- /modules/nixos/s3fs.nix: -------------------------------------------------------------------------------- 1 | { config, pkgs, lib, ... }: 2 | 3 | with lib; 4 | let 5 | cfg = config.programs.s3fs; 6 | #passwords = pkgs.callPackage ../../lib/passwords.nix { }; 7 | in 8 | { 9 | options.programs.s3fs = { 10 | enable = mkOption { 11 | type = types.bool; 12 | default = false; 13 | description = "Enable s3fs-fuse"; 14 | }; 15 | }; 16 | 17 | config = mkIf cfg.enable { 18 | #deployment.keys = { 19 | # s3fs-api-token = { 20 | # keyCommand = passwords.getPassword "system/absolution-gap/api-token"; 21 | # destDir = "/secrets"; 22 | # user = config.primary-user.name; 23 | # group = "users"; 24 | # }; 25 | #}; 26 | 27 | environment.systemPackages = [ pkgs.s3fs ]; 28 | environment.variables.AWS_CREDENTIAL_FILE = /secrets/absolution-gap-api-token; #config.deployment.keys.s3fs-api-token.path; 29 | 30 | systemd.user.services.s3fs = 31 | { 32 | description = "Mount S3fs drive"; 33 | wantedBy = [ "detect-online.service" ]; 34 | after = [ "detect-online.service" ]; 35 | serviceConfig = { 36 | Type = "oneshot"; 37 | RemainAfterExit = true; 38 | ExecStart = "${pkgs.s3fs.outPath}/bin/s3fs absolution-gap /home/${config.primary-user.name}/mnt -o url=https://sfo3.digitaloceanspaces.com -o use_path_request_style -o passwd_file=/secrets/s3fs-api-token -o endpoint=sfo3"; 39 | ExecStop = "fusermount -u /home${config.primary-user.name}/mnt"; 40 | }; 41 | }; 42 | 43 | systemd.user.services.detect-online = 44 | { 45 | description = "Detects if we have internet access"; 46 | wantedBy = [ "default.target" ]; 47 | script = 48 | '' 49 | PATH="/run/wrappers/bin:$PATH" 50 | host="''${1:-8.8.8.8}" 51 | 52 | pingcheck() { 53 | ping -n -c 1 -w 5 $1 >/dev/null 2>&1 54 | } 55 | 56 | # Do you want a timeout ? 57 | while :; do 58 | pingcheck ''${host} && exit 0 59 | sleep 10 60 | done 61 | ''; 62 | serviceConfig = { 63 | Type = "oneshot"; 64 | RemainAfterExit = true; 65 | }; 66 | }; 67 | }; 68 | } 69 | -------------------------------------------------------------------------------- /config/machines/servers/sower/kmonad.nix: -------------------------------------------------------------------------------- 1 | { ... }: 2 | 3 | { 4 | primary-user.home-manager.programs.kmonad = { 5 | enable = true; 6 | 7 | defcfg = '' 8 | (defcfg 9 | input (device-file "/dev/input/event1") 10 | output (uinput-sink "external-keyboard") 11 | allow-cmd true 12 | fallthrough true 13 | ) 14 | ''; 15 | 16 | defsrc = '' 17 | (defsrc 18 | esc f1 f2 f3 f4 f5 f6 f7 f8 f9 f10 f11 f12 ssrq slck pause 19 | grv 1 2 3 4 5 6 7 8 9 0 - = bspc ins home pgup 20 | tab q w e r t y u i o p [ ] \ del end pgdn 21 | caps a s d f g h j k l ; ' ret 22 | lsft z x c v b n m , . / rsft 23 | lctl lmet lalt spc ralt rmet cmp rctl up 24 | left down rght 25 | ) 26 | ''; 27 | 28 | defaliases = '' 29 | (defalias 30 | tmt (tap-next tab lmet) 31 | \mt (tap-next \ rmet) 32 | xcp (tap-next esc lctl) 33 | ) 34 | ''; 35 | 36 | deflayers = '' 37 | (deflayer test 38 | esc f1 f2 f3 f4 f5 f6 f7 f8 f9 f10 f11 f12 ssrq slck pause 39 | grv 1 2 3 4 5 6 7 8 9 0 - = bspc ins home pgup 40 | @tmt q w e r t y u i o p [ ] @\mt del end pgdn 41 | @xcp a s d f g h j k l ; ' ret 42 | lsft z x c v b n m , . / rsft 43 | lctl lmet lalt spc ralt rmet cmp rctl up 44 | left down rght 45 | ) 46 | ''; 47 | }; 48 | 49 | primary-user.extraGroups = [ "uinput" "input" ]; 50 | 51 | users.groups = { uinput = { }; }; 52 | boot.kernelModules = [ "uinput" ]; 53 | services.udev.extraRules = '' 54 | # KMonad user access to /dev/uinput 55 | KERNEL=="uinput", MODE="0660", GROUP="uinput", OPTIONS+="static_node=uinput" 56 | ''; 57 | } 58 | -------------------------------------------------------------------------------- /config/machines/servers/sower/jellyfin.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | 3 | let 4 | jellyseerData = "/mnt/jellyseerr"; 5 | in 6 | { 7 | services.jellyfin = { 8 | enable = true; 9 | openFirewall = true; 10 | }; 11 | 12 | systemd.services.jellyfin = { 13 | serviceConfig = { 14 | RequiresMountsFor = [ "/mnt/media" ]; 15 | After = [ "mnt-media.automount" ]; 16 | }; 17 | }; 18 | 19 | virtualisation.oci-containers.containers = { 20 | jellyseerr = { 21 | image = "fallenbagel/jellyseerr:latest"; 22 | ports = [ "5055:5055" ]; 23 | 24 | volumes = [ 25 | "${jellyseerData}/config:/app/config" 26 | ]; 27 | 28 | environment = { 29 | LOG_LEVEL = "debug"; 30 | TZ = "America/Los_Angeles"; 31 | }; 32 | 33 | autoStart = true; 34 | }; 35 | }; 36 | 37 | services.nginx.virtualHosts = { 38 | "jellyfin.local.home.arpa" = { 39 | locations."/".proxyPass = "http://localhost:8096"; 40 | }; 41 | 42 | "jellyfin.service.home.arpa" = { 43 | locations."/".proxyPass = "http://localhost:8096"; 44 | }; 45 | 46 | "jellyseerr.local.home.arpa" = { 47 | locations."/".proxyPass = "http://localhost:5055"; 48 | }; 49 | 50 | "jellyseerr.service.home.arpa" = { 51 | locations."/".proxyPass = "http://localhost:5055"; 52 | }; 53 | }; 54 | 55 | environment.systemPackages = [ 56 | pkgs.jellyfin 57 | pkgs.jellyfin-web 58 | pkgs.jellyfin-ffmpeg 59 | ]; 60 | 61 | users.groups.nas.gid = 998; 62 | users.users.jellyfin.extraGroups = [ "nas" ]; 63 | 64 | hardware.graphics = { 65 | enable = true; 66 | extraPackages = [ 67 | # Hardware transcoding. 68 | pkgs.intel-media-driver # LIBVA_DRIVER_NAME=iHD 69 | pkgs.libvdpau-va-gl 70 | pkgs.vaapiIntel # LIBVA_DRIVER_NAME=i965 (older but can work better for some applications) 71 | pkgs.vaapiVdpau 72 | pkgs.intel-compute-runtime 73 | pkgs.ocl-icd 74 | ]; 75 | 76 | enable32Bit = true; 77 | 78 | extraPackages32 = [ 79 | # Hardware transcoding. 80 | pkgs.intel-media-driver # LIBVA_DRIVER_NAME=iHD 81 | pkgs.libvdpau-va-gl 82 | pkgs.vaapiIntel # LIBVA_DRIVER_NAME=i965 (older but can work better for some applications) 83 | pkgs.vaapiVdpau 84 | pkgs.intel-compute-runtime 85 | pkgs.ocl-icd 86 | ]; 87 | }; 88 | } 89 | -------------------------------------------------------------------------------- /config/modules/ui/git/home.nix: -------------------------------------------------------------------------------- 1 | { ... }: 2 | 3 | { 4 | programs.git = { 5 | enable = true; 6 | userName = "solomon"; 7 | userEmail = "ssbothwell@gmail.com"; 8 | aliases = { 9 | # list files which have changed since REVIEW_BASE 10 | # (REVIEW_BASE defaults to 'master' in zshrc) 11 | files = "!git diff --name-only $(git merge-base HEAD \"$REVIEW_BASE\")"; 12 | 13 | # Same as above, but with a diff stat instead of just names 14 | # (better for interactive use) 15 | stat = "!git diff --stat $(git merge-base HEAD \"$REVIEW_BASE\")"; 16 | }; 17 | extraConfig = { 18 | branch = { 19 | sort = "-committerdate"; 20 | }; 21 | column = { 22 | ui = "auto"; 23 | }; 24 | 25 | # https://blog.dbrgn.ch/2021/11/16/git-ssh-signatures/ 26 | commit = { 27 | gpgsign = true; 28 | }; 29 | 30 | core = { 31 | fscache = false; 32 | }; 33 | 34 | gpg = { 35 | format = "ssh"; 36 | ssh = { 37 | allowedSignersFile = "~/.config/git/allowed_signers"; 38 | }; 39 | }; 40 | 41 | init = { 42 | defaultBranch = "main"; 43 | }; 44 | 45 | # https://jvns.ca/blog/2024/02/16/popular-git-config-options/#merge-conflictstyle-zdiff3 46 | merge = { 47 | conflictstyle = "zdiff3"; 48 | }; 49 | 50 | # https://jvns.ca/blog/2024/02/16/popular-git-config-options/#pull-ff-only-or-pull-rebase-true 51 | pull = { 52 | rebase = true; 53 | }; 54 | 55 | # https://jvns.ca/blog/2024/02/16/popular-git-config-options/#push-default-simple-push-default-current 56 | push = { 57 | autoSetupRemote = true; 58 | }; 59 | 60 | # https://jvns.ca/blog/2024/02/16/popular-git-config-options/#rerere-enabled-true 61 | rerere = { 62 | enabled = true; 63 | }; 64 | 65 | safe = { 66 | directory = "/etc/nixos/flake"; 67 | }; 68 | 69 | # https://blog.dbrgn.ch/2021/11/16/git-ssh-signatures/ 70 | tag = { 71 | gpgsign = true; 72 | }; 73 | 74 | user = { 75 | email = "ssbothwell@gmail.com"; 76 | signingKey = "~/.ssh/id_ed25519.pub"; 77 | }; 78 | }; 79 | includes = [ 80 | { 81 | path = ./work-profile; 82 | condition = "gitdir:~/Development/Bitnomial/"; 83 | } 84 | ]; 85 | }; 86 | } 87 | -------------------------------------------------------------------------------- /config/machines/personal-computers/voice-of-evening/kmonad.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | 3 | let 4 | config-file = device: pkgs.writeText "kmonad.cfg" 5 | '' 6 | (defcfg 7 | input (device-file "${device}") 8 | output (uinput-sink "external-keyboard") 9 | allow-cmd true 10 | fallthrough true 11 | ) 12 | 13 | (defsrc 14 | esc f1 f2 f3 f4 f5 f6 f7 f8 f9 f10 f11 f12 home end ins del 15 | grv 1 2 3 4 5 6 7 8 9 0 - = bspc 16 | tab q w e r t y u i o p [ ] \ 17 | caps a s d f g h j k l ; ' ret 18 | lsft z x c v b n m , . / rsft 19 | lctl lmet lalt spc ralt rctl up 20 | left down rght 21 | ) 22 | 23 | (defalias 24 | tmt (tap-next tab lmet) 25 | \mt (tap-next \ rmet) 26 | xcp (tap-next esc lctl) 27 | ) 28 | 29 | (deflayer test 30 | esc f1 f2 f3 f4 f5 f6 f7 f8 f9 f10 f11 f12 home end ins del 31 | grv 1 2 3 4 5 6 7 8 9 0 - = bspc 32 | @tmt q w e r t y u i o p [ ] @\mt 33 | @xcp a s d f g h j k l ; ' ret 34 | lsft z x c v b n m , . / rsft 35 | lctl lmet lalt spc ralt rctl up 36 | left down rght 37 | ) 38 | ''; 39 | in 40 | { 41 | primary-user.extraGroups = [ "uinput" "input" ]; 42 | 43 | users.groups = { uinput = { }; }; 44 | boot.kernelModules = [ "uinput" ]; 45 | services.udev.extraRules = '' 46 | # KMonad user access to /dev/uinput 47 | KERNEL=="uinput", MODE="0660", GROUP="uinput", OPTIONS+="static_node=uinput" 48 | ''; 49 | 50 | systemd.services.kmonad-external = { 51 | enable = true; 52 | description = "kmonad external keyboard config"; 53 | serviceConfig = { 54 | Restart = "always"; 55 | RestartSec = "3"; 56 | ExecStart = "${pkgs.kmonad}/bin/kmonad ${config-file "/dev/input/by-id/usb-HID_Keyboard_HID_Keyboard-event-kbd"}"; 57 | }; 58 | wantedBy = [ "default.target" ]; 59 | }; 60 | } 61 | -------------------------------------------------------------------------------- /config/machines/personal-computers/lorean/kmonad.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | 3 | let 4 | config-file = device: pkgs.writeText "kmonad.cfg" 5 | '' 6 | (defcfg 7 | input (device-file "${device}") 8 | output (uinput-sink "internal-keyboard") 9 | allow-cmd true 10 | fallthrough true 11 | ) 12 | 13 | (defsrc 14 | esc f1 f2 f3 f4 f5 f6 f7 f8 f9 f10 f11 f12 home end ins del 15 | grv 1 2 3 4 5 6 7 8 9 0 - = bspc 16 | tab q w e r t y u i o p [ ] \ 17 | caps a s d f g h j k l ; ' ret 18 | lsft z x c v b n m , . / rsft 19 | lctl lalt lmet spc ralt prnt rctl pgup up pgdn 20 | left down rght 21 | ) 22 | 23 | (defalias 24 | tmt (tap-next tab lmet) 25 | \mt (tap-next \ rmet) 26 | xcp (tap-next esc lctl) 27 | ) 28 | 29 | (deflayer test 30 | esc f1 f2 f3 f4 f5 f6 f7 f8 f9 f10 f11 f12 home end ins del 31 | grv 1 2 3 4 5 6 7 8 9 0 - = bspc 32 | @tmt q w e r t y u i o p [ ] @\mt 33 | @xcp a s d f g h j k l ; ' ret 34 | lsft z x c v b n m , . / rsft 35 | lctl lalt lmet spc ralt prnt rctl pgup up pgdn 36 | left down rght 37 | ) 38 | ''; 39 | in 40 | { 41 | primary-user.extraGroups = [ "uinput" "input" ]; 42 | 43 | users.groups = { uinput = { }; }; 44 | boot.kernelModules = [ "uinput" ]; 45 | services.udev.extraRules = '' 46 | # KMonad user access to /dev/uinput 47 | KERNEL=="uinput", MODE="0660", GROUP="uinput", OPTIONS+="static_node=uinput" 48 | ''; 49 | 50 | systemd.services.kmonad-internal = { 51 | enable = true; 52 | description = "kmonad internal keyboard config"; 53 | serviceConfig = { 54 | Restart = "always"; 55 | RestartSec = "3"; 56 | ExecStart = "${pkgs.kmonad}/bin/kmonad ${config-file "/dev/input/by-path/platform-i8042-serio-0-event-kbd"}"; 57 | }; 58 | wantedBy = [ "default.target" ]; 59 | }; 60 | } 61 | -------------------------------------------------------------------------------- /config/machines/servers/accompaniment-of-shadows/planka.nix: -------------------------------------------------------------------------------- 1 | { config, pkgs, ... }: 2 | 3 | let 4 | plankaLocation = "/mnt/planka"; 5 | userAvatars = "${plankaLocation}/user-avatars"; 6 | projectBackgroundImages = "${plankaLocation}/project-background-images"; 7 | attachaments = "${plankaLocation}/attachaments"; 8 | in 9 | { 10 | fileSystems."/mnt/planka" = { 11 | device = "192.168.5.6:/mnt/tank/app-data/planka"; 12 | fsType = "nfs"; 13 | options = [ 14 | "defaults" # → rw,suid,dev,exec,auto,nouser,async 15 | "vers=3" # force NFSv3 16 | "proto=tcp" # use TCP transport 17 | "intr" # allow signals (Ctrl-C) to interrupt 18 | "timeo=30" # initial timeout = 3 s (30 deciseconds) 19 | "retrans=3" # retry only 3 times (~9 s total) 20 | "_netdev" # wait for network before mounting 21 | ]; 22 | }; 23 | 24 | virtualisation.oci-containers.containers = { 25 | planka = { 26 | image = "ghcr.io/plankanban/planka:latest"; 27 | 28 | extraOptions = [ 29 | "--network=host" 30 | ]; 31 | 32 | volumes = [ 33 | "${userAvatars}:/app/public/user-avatars" 34 | "${projectBackgroundImages}:/app/public/project-background-images" 35 | "${attachaments}:/app/private/attachements" 36 | "${config.sops.secrets.planka-secret-key.path}:/run/secrets/planka-secret-key:ro" 37 | "${config.sops.secrets.planka-database-password.path}:/run/secrets/planka-database-password:ro" 38 | ]; 39 | 40 | environment = { 41 | BASE_URL = "http://planka.service.home.arpa"; 42 | TRUST_PROXY = "1"; 43 | DATABASE_URL = "postgresql://planka_admin:hunter2@localhost/planka"; 44 | SECRET_KEY__FILE = "/run/secrets/planka-secret-key"; 45 | DATABASE_PASSWORD__FILE = "/run/secrets/planka-database-password"; 46 | }; 47 | 48 | autoStart = true; 49 | }; 50 | }; 51 | 52 | services.nginx.virtualHosts."planka.service.home.arpa" = { 53 | locations."/" = { 54 | proxyPass = "http://localhost:1337"; 55 | proxyWebsockets = true; 56 | }; 57 | }; 58 | 59 | # The Planka application is: 60 | # 61 | # UID: 1000 62 | # Username: node 63 | # 64 | # This means I can't create a matching user in the host system for assigning 65 | # SOPS secret permissions. 66 | sops.secrets.planka-secret-key = { 67 | mode = "0444"; 68 | }; 69 | sops.secrets.planka-database-password = { 70 | mode = "0444"; 71 | }; 72 | } 73 | -------------------------------------------------------------------------------- /config/modules/ui/kmonad/home.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | 3 | let 4 | config-file = device: pkgs.writeText "kmonad.cfg" 5 | '' 6 | (defcfg 7 | input (device-file "${device}") 8 | output (uinput-sink "internal-keyboard") 9 | allow-cmd true 10 | fallthrough true 11 | ) 12 | 13 | (defsrc 14 | esc f1 f2 f3 f4 f5 f6 f7 f8 f9 f10 f11 f12 home end ins del 15 | grv 1 2 3 4 5 6 7 8 9 0 - = bspc 16 | tab q w e r t y u i o p [ ] \ 17 | caps a s d f g h j k l ; ' ret 18 | lsft z x c v b n m , . / rsft 19 | lctl lalt lmet spc ralt prnt rctl pgup up pgdn 20 | left down rght 21 | ) 22 | 23 | (defalias 24 | tmt (tap-next tab lmet) 25 | \mt (tap-next \ rmet) 26 | xcp (tap-next esc lctl) 27 | ) 28 | 29 | (deflayer test 30 | esc f1 f2 f3 f4 f5 f6 f7 f8 f9 f10 f11 f12 home end ins del 31 | grv 1 2 3 4 5 6 7 8 9 0 - = bspc 32 | @tmt q w e r t y u i o p [ ] @\mt 33 | @xcp a s d f g h j k l ; ' ret 34 | lsft z x c v b n m , . / rsft 35 | lctl lalt lmet spc ralt prnt rctl pgup up pgdn 36 | left down rght 37 | ) 38 | ''; 39 | in 40 | { 41 | #primary-user.extraGroups = [ "uinput" "input" ]; 42 | 43 | #users.groups = { uinput = { }; }; 44 | #boot.kernelModules = [ "uinput" ]; 45 | #services.udev.extraRules = '' 46 | # # KMonad user access to /dev/uinput 47 | # KERNEL=="uinput", MODE="0660", GROUP="uinput", OPTIONS+="static_node=uinput" 48 | #''; 49 | 50 | systemd.user.services.kmonad-internal = { 51 | Unit = { 52 | description = "kmonad internal keyboard config"; 53 | }; 54 | 55 | Install = { 56 | WantedBy = [ "default.target" ]; 57 | }; 58 | 59 | Service = { 60 | Restart = "always"; 61 | RestartSec = "3"; 62 | ExecStart = "${pkgs.kmonad}/bin/kmonad ${config-file "/dev/input/by-path/platform-i8042-serio-0-event-kbd"}"; 63 | }; 64 | }; 65 | } 66 | -------------------------------------------------------------------------------- /config/machines/servers/sower/immich.nix: -------------------------------------------------------------------------------- 1 | { config, pkgs, lib, ... }: 2 | 3 | let 4 | dbHostname = "192.168.5.105"; # accompaniment-of-shadows 5 | dbUsername = "immich"; 6 | dbPassword = "immich"; 7 | dbDatabaseName = "immich"; 8 | 9 | redisHostname = "192.168.5.105"; # accompaniment-of-shadows 10 | redisPassword = "hunter2"; 11 | photosLocation = "/mnt/immich"; 12 | 13 | typesenseApiKey = "abcxyz123"; 14 | in 15 | { 16 | fileSystems."/mnt/immich" = { 17 | device = "192.168.5.6:/mnt/tank/app-data/immich"; 18 | fsType = "nfs"; 19 | options = [ 20 | "defaults" # → rw,suid,dev,exec,auto,nouser,async 21 | "vers=3" # force NFSv3 22 | "proto=tcp" # use TCP transport 23 | "intr" # allow signals (Ctrl-C) to interrupt 24 | "timeo=30" # initial timeout = 3 s (30 deciseconds) 25 | "retrans=3" # retry only 3 times (~9 s total) 26 | "_netdev" # wait for network before mounting 27 | ]; 28 | }; 29 | 30 | services.immich = { 31 | enable = true; 32 | machine-learning.enable = true; 33 | 34 | database = { 35 | enable = false; 36 | createDB = false; 37 | host = dbHostname; 38 | port = 5432; 39 | user = dbUsername; 40 | name = dbDatabaseName; 41 | }; 42 | 43 | redis = { 44 | # Note: see above WRT to `database.enable`. 45 | enable = false; 46 | host = redisHostname; 47 | }; 48 | 49 | secretsFile = config.sops.secrets.immich-postgres-password.path; 50 | 51 | environment = { 52 | IMMICH_LOG_LEVEL = "log"; 53 | # Redis 54 | REDIS_HOSTNAME = redisHostname; 55 | REDIS_PASSWORD = redisPassword; 56 | 57 | # Upload path 58 | # UPLOAD_LOCATION = photosLocation; 59 | }; 60 | 61 | mediaLocation = photosLocation; 62 | settings.server.externalDomain = "http://immich.service.home.arpa"; 63 | settings.newVersionCheck.enabled = true; 64 | }; 65 | 66 | systemd.services.immich-server = { 67 | serviceConfig = { 68 | SupplementaryGroups = [ "immich-nfs" ]; 69 | }; 70 | }; 71 | 72 | users.groups.immich-nfs = { 73 | gid = 1007; 74 | }; 75 | 76 | services.nginx.virtualHosts."immich.service.home.arpa" = { 77 | locations."/" = { 78 | proxyPass = "http://localhost:2283"; 79 | extraConfig = '' 80 | client_max_body_size 0; 81 | proxy_max_temp_file_size 96384m; 82 | ''; 83 | }; 84 | }; 85 | 86 | sops.secrets = { 87 | immich-postgres-password = { }; 88 | }; 89 | } 90 | -------------------------------------------------------------------------------- /config/machines/servers/sower/hardware.nix: -------------------------------------------------------------------------------- 1 | { config, lib, pkgs, modulesPath, ... }: 2 | 3 | { 4 | imports = 5 | [ (modulesPath + "/installer/scan/not-detected.nix") ]; 6 | 7 | boot.loader.efi.canTouchEfiVariables = true; 8 | boot.supportedFilesystems = [ "zfs" ]; 9 | 10 | boot.initrd.availableKernelModules = [ "xhci_pci" "ahci" "usb_storage" "usbhid" "sd_mod" "sdhci_pci" ]; 11 | boot.initrd.kernelModules = [ ]; 12 | boot.kernelModules = [ "kvm-intel" ]; 13 | boot.extraModulePackages = [ ]; 14 | 15 | fileSystems = { 16 | "/" = { 17 | device = "tank/root"; 18 | fsType = "zfs"; 19 | }; 20 | 21 | "/home" = { 22 | device = "tank/user"; 23 | fsType = "zfs"; 24 | }; 25 | 26 | "/nix" = { 27 | device = "tank/nix"; 28 | fsType = "zfs"; 29 | }; 30 | 31 | "/boot" = { 32 | device = "/dev/disk/by-uuid/2F36-F75A"; 33 | fsType = "vfat"; 34 | }; 35 | 36 | "/mnt/media" = { 37 | device = "192.168.5.6:/mnt/tank/Media"; 38 | fsType = "nfs"; 39 | options = [ 40 | "defaults" # → rw,suid,dev,exec,auto,nouser,async 41 | "vers=3" # force NFSv3 42 | "proto=tcp" # use TCP transport 43 | "soft" # give up after retries (don’t hang forever) 44 | "intr" # allow signals (Ctrl-C) to interrupt 45 | "timeo=30" # initial timeout = 3 s (30 deciseconds) 46 | "retrans=3" # retry only 3 times (~9 s total) 47 | "_netdev" # wait for network before mounting 48 | ]; 49 | }; 50 | 51 | "/mnt/jellyseerr" = { 52 | device = "192.168.5.6:/mnt/tank/app-data/jellyseerr"; 53 | fsType = "nfs"; 54 | options = [ 55 | "defaults" # → rw,suid,dev,exec,auto,nouser,async 56 | "vers=3" # force NFSv3 57 | "proto=tcp" # use TCP transport 58 | "intr" # allow signals (Ctrl-C) to interrupt 59 | "timeo=30" # initial timeout = 3 s (30 deciseconds) 60 | "retrans=3" # retry only 3 times (~9 s total) 61 | "_netdev" # wait for network before mounting 62 | ]; 63 | }; 64 | }; 65 | 66 | swapDevices = [ ]; 67 | 68 | powerManagement.cpuFreqGovernor = lib.mkDefault "powersave"; 69 | 70 | systemd.services.zfs-zed = { 71 | after = [ "zfs-import.target" "zfs-mount.service" ]; 72 | wants = [ "zfs-import.target" ]; 73 | 74 | # Add a delay to avoid race conditions 75 | serviceConfig = { 76 | ExecStartPre = "${pkgs.coreutils}/bin/sleep 2"; 77 | RestartSec = "5s"; 78 | }; 79 | }; 80 | 81 | # nix.buildCores = ??? 82 | # nix.maxJobs = lib.mkDefault ??? 83 | } 84 | -------------------------------------------------------------------------------- /config/modules/ui/st/0002-Adds-Sanity-Inc-Tomorrow-Eighties-Theme.patch: -------------------------------------------------------------------------------- 1 | From 023acb4d77de17a7ad954e2911de174bc7b63358 Mon Sep 17 00:00:00 2001 2 | From: solomon 3 | Date: Thu, 4 Jan 2024 12:57:10 -0800 4 | Subject: [PATCH 2/3] Adds Sanity Inc Tomorrow Eighties Theme 5 | 6 | https://github.com/chriskempson/tomorrow-theme/blob/master/Xdefaults/XresourceTomorrowNightEighties 7 | --- 8 | config.def.h | 51 +++++++++++++++++++++++++-------------------------- 9 | 1 file changed, 25 insertions(+), 26 deletions(-) 10 | 11 | diff --git a/config.def.h b/config.def.h 12 | index 52b2ec3..962f78c 100644 13 | --- a/config.def.h 14 | +++ b/config.def.h 15 | @@ -96,43 +96,42 @@ unsigned int tabspaces = 8; 16 | /* Terminal colors (16 first used in escape sequence) */ 17 | static const char *colorname[] = { 18 | /* 8 normal colors */ 19 | - "black", 20 | - "red3", 21 | - "green3", 22 | - "yellow3", 23 | - "blue2", 24 | - "magenta3", 25 | - "cyan3", 26 | - "gray90", 27 | + [0] = "#000000", 28 | + [1] = "#f2777a", 29 | + [2] = "#99cc99", 30 | + [3] = "#f99157", 31 | + [4] = "#6699cc", 32 | + [5] = "#cc99cc", 33 | + [6] = "#66cccc", 34 | + [7] = "#515151", 35 | 36 | /* 8 bright colors */ 37 | - "gray50", 38 | - "red", 39 | - "green", 40 | - "yellow", 41 | - "#5c5cff", 42 | - "magenta", 43 | - "cyan", 44 | - "white", 45 | + [8] = "#666666", 46 | + [9] = "#FF3334", 47 | + [10] = "#9ec400", 48 | + [11] = "#ffcc66", 49 | + [12] = "#6699cc", 50 | + [13] = "#b777e0", 51 | + [14] = "#54ced6", 52 | + [15] = "#393939", 53 | 54 | [255] = 0, 55 | 56 | /* more colors can be added after 255 to use with DefaultXX */ 57 | - "#cccccc", 58 | - "#555555", 59 | - "gray90", /* default foreground colour */ 60 | - "black", /* default background colour */ 61 | + [256] = "#cccccc", // foreground 62 | + [257] = "#2d2d2d", // background 63 | + [258] = "#aeafad", // cursor 64 | }; 65 | 66 | 67 | /* 68 | * Default colors (colorname index) 69 | - * foreground, background, cursor, reverse cursor 70 | + * foreground, background, cursor 71 | */ 72 | -unsigned int defaultfg = 258; 73 | -unsigned int defaultbg = 259; 74 | -unsigned int defaultcs = 256; 75 | -static unsigned int defaultrcs = 257; 76 | +unsigned int defaultfg = 256; 77 | +unsigned int defaultbg = 257; 78 | +unsigned int defaultcs = 258; 79 | +static unsigned int defaultrcs = 258; 80 | 81 | /* 82 | * Default shape of cursor 83 | @@ -154,7 +153,7 @@ static unsigned int rows = 24; 84 | * Default colour and shape of the mouse cursor 85 | */ 86 | static unsigned int mouseshape = XC_xterm; 87 | -static unsigned int mousefg = 7; 88 | +static unsigned int mousefg = 256; 89 | static unsigned int mousebg = 0; 90 | 91 | /* 92 | -- 93 | 2.42.0 94 | 95 | -------------------------------------------------------------------------------- /config/modules/ui/st/st-scrollback-mouse-altscreen-20220127-2c5edf2.diff: -------------------------------------------------------------------------------- 1 | From 580e3f386e9215707100e9ba44797701943fd927 Mon Sep 17 00:00:00 2001 2 | From: asparagii 3 | Date: Thu, 27 Jan 2022 15:49:27 +0100 4 | Subject: [PATCH] st-scrollback-mouse-altscreen 5 | 6 | --- 7 | config.def.h | 4 ++-- 8 | st.c | 5 +++++ 9 | st.h | 1 + 10 | x.c | 2 ++ 11 | 4 files changed, 10 insertions(+), 2 deletions(-) 12 | 13 | diff --git a/config.def.h b/config.def.h 14 | index c217315..c223706 100644 15 | --- a/config.def.h 16 | +++ b/config.def.h 17 | @@ -176,8 +176,8 @@ static uint forcemousemod = ShiftMask; 18 | */ 19 | static MouseShortcut mshortcuts[] = { 20 | /* mask button function argument release */ 21 | - { ShiftMask, Button4, kscrollup, {.i = 1} }, 22 | - { ShiftMask, Button5, kscrolldown, {.i = 1} }, 23 | + { XK_ANY_MOD, Button4, kscrollup, {.i = 1}, 0, /* !alt */ -1 }, 24 | + { XK_ANY_MOD, Button5, kscrolldown, {.i = 1}, 0, /* !alt */ -1 }, 25 | { XK_ANY_MOD, Button2, selpaste, {.i = 0}, 1 }, 26 | { ShiftMask, Button4, ttysend, {.s = "\033[5;2~"} }, 27 | { XK_ANY_MOD, Button4, ttysend, {.s = "\031"} }, 28 | diff --git a/st.c b/st.c 29 | index f3af82b..876a6bf 100644 30 | --- a/st.c 31 | +++ b/st.c 32 | @@ -1060,6 +1060,11 @@ tnew(int col, int row) 33 | treset(); 34 | } 35 | 36 | +int tisaltscr(void) 37 | +{ 38 | + return IS_SET(MODE_ALTSCREEN); 39 | +} 40 | + 41 | void 42 | tswapscreen(void) 43 | { 44 | diff --git a/st.h b/st.h 45 | index da36b34..e95c6f8 100644 46 | --- a/st.h 47 | +++ b/st.h 48 | @@ -89,6 +89,7 @@ void sendbreak(const Arg *); 49 | void toggleprinter(const Arg *); 50 | 51 | int tattrset(int); 52 | +int tisaltscr(void); 53 | void tnew(int, int); 54 | void tresize(int, int); 55 | void tsetdirtattr(int); 56 | diff --git a/x.c b/x.c 57 | index cd96575..9274556 100644 58 | --- a/x.c 59 | +++ b/x.c 60 | @@ -34,6 +34,7 @@ typedef struct { 61 | void (*func)(const Arg *); 62 | const Arg arg; 63 | uint release; 64 | + int altscrn; /* 0: don't care, -1: not alt screen, 1: alt screen */ 65 | } MouseShortcut; 66 | 67 | typedef struct { 68 | @@ -455,6 +456,7 @@ mouseaction(XEvent *e, uint release) 69 | for (ms = mshortcuts; ms < mshortcuts + LEN(mshortcuts); ms++) { 70 | if (ms->release == release && 71 | ms->button == e->xbutton.button && 72 | + (!ms->altscrn || (ms->altscrn == (tisaltscr() ? 1 : -1))) && 73 | (match(ms->mod, state) || /* exact or forced */ 74 | match(ms->mod, state & ~forcemousemod))) { 75 | ms->func(&(ms->arg)); 76 | -- 77 | 2.34.1 78 | 79 | -------------------------------------------------------------------------------- /config/machines/servers/accompaniment-of-shadows/postgresql.nix: -------------------------------------------------------------------------------- 1 | # PostgreSQL Service 2 | { config, pkgs, lib, ... }: 3 | 4 | { 5 | # Mount PostgreSQL data from NFS 6 | fileSystems."/mnt/postgresql" = { 7 | device = "192.168.5.6:/mnt/tank/postgresql"; 8 | fsType = "nfs"; 9 | options = [ 10 | "defaults" # → rw,suid,dev,exec,auto,nouser,async 11 | "vers=3" # force NFSv3 12 | "proto=tcp" # use TCP transport 13 | "intr" # allow signals (Ctrl-C) to interrupt 14 | "timeo=30" # initial timeout = 3 s (30 deciseconds) 15 | "retrans=3" # retry only 3 times (~9 s total) 16 | "_netdev" # wait for network before mounting 17 | ]; 18 | }; 19 | 20 | services.postgresql = { 21 | enable = true; 22 | dataDir = "/mnt/postgresql"; 23 | enableTCPIP = true; 24 | extraPlugins = with pkgs.postgresql_14.pkgs; [ pgvecto-rs ]; 25 | package = pkgs.postgresql_14; 26 | 27 | settings = { 28 | shared_preload_libraries = "vectors.so"; 29 | }; 30 | 31 | ensureDatabases = [ 32 | "tt_rss" 33 | "planka" 34 | "immich" 35 | "hass" 36 | "irrigation" 37 | ]; 38 | 39 | ensureUsers = [ 40 | { 41 | name = "tt_rss"; 42 | ensureDBOwnership = true; 43 | } 44 | { 45 | name = "planka_admin"; 46 | } 47 | { 48 | name = "immich"; 49 | ensureDBOwnership = true; 50 | } 51 | { 52 | name = "hass"; 53 | ensureDBOwnership = true; 54 | } 55 | { 56 | name = "irrigation"; 57 | ensureDBOwnership = true; 58 | } 59 | ]; 60 | 61 | authentication = '' 62 | # Local connections from accompaniment-of-shadows services 63 | host tt_rss tt_rss 127.0.0.1/32 md5 64 | host tt_rss tt_rss localhost md5 65 | host planka planka_admin 127.0.0.1/32 md5 66 | host planka planka_admin localhost md5 67 | 68 | # Remote connections from sower (Immich, Home Assistant) 69 | host immich immich 100.80.98.4/32 md5 70 | host immich immich 192.168.5.7/32 md5 71 | host immich immich 192.168.1.131/32 md5 72 | host immich immich sower md5 73 | 74 | host hass hass 100.80.98.4/32 md5 75 | host hass hass 192.168.5.7/32 md5 76 | host hass hass sower md5 77 | 78 | host irrigation irrigation 100.80.98.4/32 md5 79 | host irrigation irrigation 192.168.5.7/32 md5 80 | host irrigation irrigation sower md5 81 | 82 | # Admin access from personal computers 83 | host all all 100.76.234.131/32 md5 84 | host all all zodiacal-light md5 85 | host all all 100.100.33.33/32 md5 86 | host all all nightshade md5 87 | host all all 100.126.171.15/32 md5 88 | host all all lorean md5 89 | host all all 192.168.5.7/32 md5 90 | ''; 91 | }; 92 | 93 | networking.firewall.allowedTCPPorts = [ 5432 ]; 94 | } 95 | -------------------------------------------------------------------------------- /config/modules/ui/dmenu/home.nix: -------------------------------------------------------------------------------- 1 | { pkgs, lib, config, ... }: 2 | let 3 | dmenu = pkgs.stdenv.mkDerivation { 4 | name = "dmenu-wrapper"; 5 | 6 | dontUnpack = true; 7 | 8 | nativeBuildInputs = [ pkgs.makeWrapper ]; 9 | 10 | installPhase = '' 11 | mkdir -p $out/bin 12 | makeWrapper ${pkgs.dmenu}/bin/dmenu $out/bin/dmenu \ 13 | --add-flags "-nb '#2d2d2d' -nf '#cccccc' -sb '#333333' -sf '#cccccc'" 14 | 15 | makeWrapper ${pkgs.dmenu}/bin/dmenu_run $out/bin/dmenu_run \ 16 | --add-flags "-l 30 -nb '#2d2d2d' -nf '#cccccc' -sb '#333333' -sf '#cccccc'" 17 | 18 | cp ${pkgs.dmenu}/bin/dmenu_path $out/bin/dmenu_path 19 | ''; 20 | }; 21 | 22 | pass-menu = pkgs.stdenv.mkDerivation { 23 | name = "passmenu-wrapper"; 24 | dontUnpack = true; 25 | nativeBuildInputs = [ pkgs.makeWrapper ]; 26 | 27 | installPhase = '' 28 | mkdir -p $out/bin 29 | 30 | makeWrapper ${pkgs.pass}/bin/passmenu $out/bin/passmenu \ 31 | --add-flags "-l 30 -nb '#2d2d2d' -nf '#cccccc' -sb '#333333' -sf '#cccccc'" 32 | ''; 33 | }; 34 | 35 | emoji-prompt = pkgs.writeScriptBin "emoji-prompt" '' 36 | cat ${./emojis} | ${dmenu}/bin/dmenu -l 30 | cut -d ' ' -f 1 | xclip -sel clip 37 | ''; 38 | 39 | exit-prompt = (pkgs.writeScriptBin "exit-prompt" (builtins.readFile ./exit-prompt.sh)).overrideAttrs (old: { 40 | buildCommand = "${old.buildCommand}\n patchShebangs $out"; 41 | }); 42 | 43 | kill-window-prompt = (pkgs.writeScriptBin "kill-window-prompt" (builtins.readFile ./kill-window-prompt.sh)).overrideAttrs (old: { 44 | buildCommand = "${old.buildCommand}\n patchShebangs $out"; 45 | }); 46 | 47 | screenshot-prompt = (pkgs.writeScriptBin "screenshot-prompt" (builtins.readFile ./screenshot-prompt.sh)).overrideAttrs (old: { 48 | buildCommand = "${old.buildCommand}\n patchShebangs $out"; 49 | }); 50 | in 51 | { 52 | home.packages = [ 53 | dmenu 54 | emoji-prompt 55 | exit-prompt 56 | kill-window-prompt 57 | pass-menu 58 | pkgs.networkmanager_dmenu 59 | screenshot-prompt 60 | ]; 61 | 62 | xdg.configFile."networkmanager-dmenu/config.ini".text = '' 63 | [dmenu] 64 | dmenu_command = dmenu -l 30 65 | active_chars = == 66 | highlight = True 67 | highlight_fg = 68 | highlight_bg = 69 | highlight_bold = True 70 | compact = False 71 | pinentry = pinentry-gtk-2 72 | wifi_icons = 󰤯󰤟󰤢󰤥󰤨 73 | format = {name:<{max_len_name}s} {sec:<{max_len_sec}s} {icon:>4} 74 | list_saved = False 75 | prompt = Networks 76 | 77 | [dmenu_passphrase] 78 | obscure = False 79 | obscure_color = #222222 80 | 81 | [pinentry] 82 | description = Get network password 83 | prompt = Password: 84 | 85 | [editor] 86 | terminal = xterm 87 | gui_if_available = True 88 | gui = nm-connection-editor 89 | 90 | [nmdm] 91 | rescan_delay = 5 92 | ''; 93 | } 94 | -------------------------------------------------------------------------------- /config/modules/packages/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | 3 | { 4 | server-cli-tools = with pkgs; [ 5 | btop 6 | dysk 7 | fd 8 | fzf 9 | gum 10 | jq 11 | ripgrep 12 | sqlite 13 | sysz 14 | tmux 15 | tree 16 | unzip 17 | wget 18 | 19 | # Editors 20 | vim-full 21 | 22 | # Custom scripts 23 | (pkgs.writeShellApplication { 24 | name = "zfs-status"; 25 | runtimeInputs = with pkgs; [ zfs openssh systemd coreutils gum ]; 26 | text = '' 27 | # Set default environment variables 28 | export ZFS_STATUS_DEST_HOST=''${ZFS_STATUS_DEST_HOST:-"sandra-voi.home.arpa"} 29 | export ZFS_STATUS_DEST_USER=''${ZFS_STATUS_DEST_USER:-"solomon"} 30 | export ZFS_STATUS_DEST_BASE=''${ZFS_STATUS_DEST_BASE:-"tank/system-snapshots"} 31 | 32 | ${builtins.readFile ../../../scripts/zfs-status.sh} 33 | ''; 34 | }) 35 | ]; 36 | 37 | pc-cli-tools = with pkgs; [ 38 | # General 39 | btop 40 | dysk 41 | fd 42 | fzf 43 | gum 44 | ispell 45 | jq 46 | nix-search 47 | postgresql 48 | ranger 49 | ripgrep 50 | sqlite 51 | sysz 52 | tmux 53 | tree 54 | udiskie 55 | unzip 56 | wget 57 | 58 | # AI 59 | # claude-code 60 | 61 | # Editors 62 | vim-full 63 | 64 | # File Management 65 | filezilla 66 | 67 | # Document Processing 68 | pandoc 69 | texlive.combined.scheme-full 70 | poppler_utils 71 | 72 | # Desktop Environment Utilities 73 | xclip 74 | xdotool 75 | xorg.xkill 76 | brightnessctl 77 | libnotify 78 | #xlayoutdisplay 79 | wmctrl 80 | 81 | # Media Tools 82 | scrot 83 | 84 | # Desktop Secrets 85 | pinentry-gtk2 86 | 87 | # Custom scripts 88 | (pkgs.writeShellApplication { 89 | name = "zfs-status"; 90 | runtimeInputs = with pkgs; [ zfs openssh systemd coreutils gum ]; 91 | text = '' 92 | # Set default environment variables 93 | export ZFS_STATUS_DEST_HOST=''${ZFS_STATUS_DEST_HOST:-"sandra-voi.home.arpa"} 94 | export ZFS_STATUS_DEST_USER=''${ZFS_STATUS_DEST_USER:-"solomon"} 95 | export ZFS_STATUS_DEST_BASE=''${ZFS_STATUS_DEST_BASE:-"tank/system-snapshots"} 96 | 97 | ${builtins.readFile ../../../scripts/zfs-status.sh} 98 | ''; 99 | }) 100 | ]; 101 | 102 | gui-applications = with pkgs; [ 103 | # Web Browsers 104 | firefox 105 | google-chrome 106 | # surf 107 | 108 | # Chat/Communication 109 | discord 110 | signal-desktop 111 | slack 112 | telegram-desktop 113 | zoom-us 114 | 115 | # Media 116 | feh 117 | pavucontrol 118 | picard 119 | vlc 120 | zathura 121 | 122 | # Secrets 123 | yubioath-flutter 124 | 125 | # Misc Graphical Tools 126 | zotero 127 | ]; 128 | 129 | development = with pkgs; [ 130 | # Editors 131 | vscodium 132 | 133 | # CAD 134 | kicad 135 | ]; 136 | } 137 | -------------------------------------------------------------------------------- /CLAUDE.md: -------------------------------------------------------------------------------- 1 | # CLAUDE.md 2 | 3 | This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. 4 | 5 | ## Commands 6 | 7 | ### Building and Deployment 8 | - `nix build '.#nixos-iso'` - Build custom NixOS installer ISO with SSH keys 9 | - `colmena apply --on @server` - Deploy all servers 10 | - `colmena apply --on @pc` - Deploy all personal computers 11 | - `colmena apply --on ` - Deploy specific machine 12 | - `nix run '.#deploy'` - Interactive deployment selector 13 | - `nix run '.#deploy-all'` - Deploy all servers 14 | - `nix run '.#install-pc'` - Provision new physical machine 15 | - `nix run '.#install-server'` - Provision new virtual machine/server 16 | 17 | ### Development 18 | - `nix develop` - Enter development shell with colmena and sops 19 | - `nix flake update` - Update flake inputs 20 | - `nixpkgs-fmt .` - Format Nix files 21 | - `sops updatekeys --yes secrets.yaml` - Update SOPS encryption keys 22 | 23 | ## Architecture 24 | 25 | This is a NixOS flake configuration managing a homelab with personal computers and servers using: 26 | 27 | ### Core Structure 28 | - **config/machines/**: Machine-specific configurations 29 | - `personal-computers/`: Desktop PCs (lorean, voice-of-evening, nightshade) 30 | - `servers/`: Server configurations organized by function 31 | - **config/profiles/**: Base configurations (pc, physical-machine, virtual-machine) 32 | - **config/modules/**: Reusable NixOS modules (security, services, system, ui) 33 | - **modules/**: Custom nixos and home-manager modules 34 | - **installer/**: Custom installer ISO and provisioning scripts 35 | 36 | ### Key Technologies 37 | - **Colmena**: Deployment orchestration 38 | - **SOPS**: Secret management with age encryption 39 | - **Disko**: Declarative disk partitioning 40 | - **Home Manager**: User environment management 41 | - **nixos-anywhere**: Remote machine provisioning 42 | 43 | ### Server Architecture 44 | Each server has a specific role: 45 | - **accompaniment-of-shadows**: nginx reverse proxy, docker orchestration 46 | - **apollyon**: qBittorrent 47 | - **madonna-of-the-wasps**: Tailscale exit node and subnet relay 48 | - **silence-under-snow**: DNS server 49 | - **sower**: Media streaming (Immich, Jellyfin, Podgrab, TubeArchivist, Navidrome) 50 | - **storm-bird**: Monitoring (Prometheus/Grafana/Uptime) 51 | - **transfigured-night**: PostgreSQL service 52 | 53 | ### Machine Provisioning Workflow 54 | 1. Create machine profile in `config/machines/` 55 | 2. Boot from custom ISO with SSH keys 56 | 3. Generate hardware config: `nixos-generate-config --show-hardware-config` 57 | 4. Add SSH host keys to `pass` 58 | 5. Generate age key and update `.sops.yaml` 59 | 6. Run installer script (`install-pc` or `install-server`) 60 | 7. Deploy with colmena 61 | 62 | ### Secret Management 63 | - Secrets stored in `secrets.yaml` encrypted with SOPS 64 | - Machine SSH host keys stored in `pass` under `machine/${MACHINE}/ssh-host-key/` 65 | - Age keys derived from SSH host keys for SOPS encryption 66 | - Primary user passwords managed through SOPS 67 | 68 | ### Home Manager Integration 69 | - Personal computers use home-manager for user environment 70 | - Configurations in `config/machines/personal-computers/*/home.nix` 71 | - Standalone config for nightshade: `homeConfigurations.nightshade` -------------------------------------------------------------------------------- /config/machines/servers/accompaniment-of-shadows/servarr.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | 3 | let 4 | profilarrConfig = "/mnt/profilarr"; 5 | in 6 | { 7 | # Skip failing tests in prowlarr package 8 | # https://github.com/NixOS/nixpkgs/issues - upstream test failure 9 | nixpkgs.overlays = [ 10 | (final: prev: { 11 | prowlarr = prev.prowlarr.overrideAttrs (old: { 12 | doCheck = false; 13 | }); 14 | }) 15 | ]; 16 | 17 | services = { 18 | bazarr = { 19 | enable = true; 20 | openFirewall = true; 21 | }; 22 | 23 | prowlarr = { 24 | enable = true; 25 | openFirewall = true; 26 | }; 27 | 28 | 29 | radarr = { 30 | enable = true; 31 | openFirewall = true; 32 | }; 33 | 34 | sonarr = { 35 | enable = true; 36 | openFirewall = true; 37 | }; 38 | }; 39 | 40 | fileSystems."/mnt/profilarr" = { 41 | device = "192.168.5.6:/mnt/tank/app-data/profilarr"; 42 | fsType = "nfs"; 43 | options = [ 44 | "defaults" # → rw,suid,dev,exec,auto,nouser,async 45 | "vers=3" # force NFSv3 46 | "proto=tcp" # use TCP transport 47 | "intr" # allow signals (Ctrl-C) to interrupt 48 | "timeo=30" # initial timeout = 3 s (30 deciseconds) 49 | "retrans=3" # retry only 3 times (~9 s total) 50 | "_netdev" # wait for network before mounting 51 | ]; 52 | }; 53 | 54 | virtualisation.oci-containers.containers = { 55 | profilarr = { 56 | image = "santiagosayshey/profilarr:latest"; 57 | 58 | extraOptions = [ 59 | "--network=host" 60 | ]; 61 | 62 | volumes = [ 63 | "${profilarrConfig}:/config" 64 | ]; 65 | 66 | environment = { 67 | TZ = "America/Los_Angeles"; 68 | }; 69 | 70 | autoStart = true; 71 | }; 72 | }; 73 | 74 | environment.systemPackages = [ 75 | pkgs.ffmpeg 76 | ]; 77 | 78 | # https://github.com/NixOS/nixpkgs/issues/155475#issuecomment-1093940244 79 | systemd.services.prowlarr.environment.HOME = "/var/empty"; 80 | 81 | fileSystems."/mnt/media" = { 82 | device = "192.168.5.6:/mnt/tank/Media "; 83 | fsType = "nfs"; 84 | options = [ 85 | "defaults" # → rw,suid,dev,exec,auto,nouser,async 86 | "vers=3" # force NFSv3 87 | "proto=tcp" # use TCP transport 88 | "intr" # allow signals (Ctrl-C) to interrupt 89 | "timeo=30" # initial timeout = 3 s (30 deciseconds) 90 | "retrans=3" # retry only 3 times (~9 s total) 91 | "_netdev" # wait for network before mounting 92 | ]; 93 | }; 94 | 95 | services.nginx.virtualHosts = { 96 | "bazarr.service.home.arpa" = { 97 | locations."/" = { 98 | proxyPass = "http://localhost:6767"; 99 | }; 100 | }; 101 | 102 | "prowlarr.service.home.arpa" = { 103 | locations."/" = { 104 | proxyPass = "http://localhost:9696"; 105 | }; 106 | }; 107 | 108 | "profilarr.service.home.arpa" = { 109 | locations."/" = { 110 | proxyPass = "http://localhost:6868"; 111 | }; 112 | }; 113 | 114 | "radarr.service.home.arpa" = { 115 | locations."/" = { 116 | proxyPass = "http://localhost:7878"; 117 | }; 118 | }; 119 | 120 | "sonarr.service.home.arpa" = { 121 | locations."/" = { 122 | proxyPass = "http://localhost:8989"; 123 | }; 124 | }; 125 | }; 126 | } 127 | -------------------------------------------------------------------------------- /config/machines/servers/gnostic-ascension/disk-config.nix: -------------------------------------------------------------------------------- 1 | { 2 | # Construct the partition table for the system's primary disk. 3 | disko.devices = { 4 | disk.vda = { 5 | type = "disk"; 6 | device = "/dev/vda"; 7 | content = { 8 | type = "gpt"; 9 | partitions = { 10 | boot = { 11 | size = "1M"; 12 | type = "EF02"; 13 | }; 14 | esp = { 15 | size = "500M"; 16 | type = "EF00"; 17 | content = { 18 | type = "filesystem"; 19 | format = "vfat"; 20 | mountpoint = "/boot"; 21 | }; 22 | }; 23 | zfs = { 24 | size = "100%"; 25 | content = { 26 | type = "zfs"; 27 | pool = "tank"; 28 | }; 29 | }; 30 | }; 31 | }; 32 | }; 33 | 34 | # Construct the primary ZFS pool for this system. 35 | zpool.tank = { 36 | type = "zpool"; 37 | 38 | options = { 39 | ashift = "12"; 40 | autotrim = "on"; 41 | listsnapshots = "on"; 42 | }; 43 | 44 | rootFsOptions = { 45 | acltype = "posixacl"; 46 | atime = "off"; 47 | canmount = "off"; 48 | compression = "zstd"; 49 | dnodesize = "auto"; 50 | mountpoint = "none"; 51 | normalization = "formD"; 52 | relatime = "on"; 53 | xattr = "sa"; 54 | "com.sun:auto-snapshot" = "true"; 55 | }; 56 | 57 | datasets = { 58 | # Static reservation so the pool will never be 100% full. 59 | # 60 | # If a pool fills up completely, delete this & reclaim space; don't 61 | # forget to re-create it afterwards! 62 | reservation = { 63 | type = "zfs_fs"; 64 | options = { 65 | canmount = "off"; 66 | mountpoint = "none"; 67 | refreservation = "2G"; 68 | primarycache = "none"; 69 | secondarycache = "none"; 70 | }; 71 | }; 72 | 73 | # Root filesystem. 74 | root = { 75 | type = "zfs_fs"; 76 | mountpoint = "/"; 77 | options = { 78 | mountpoint = "legacy"; 79 | secondarycache = "none"; 80 | "com.sun:auto-snapshot" = "true"; 81 | }; 82 | }; 83 | 84 | # `/nix/store` dataset; no snapshots required. 85 | nix = { 86 | type = "zfs_fs"; 87 | mountpoint = "/nix"; 88 | options = { 89 | mountpoint = "legacy"; 90 | relatime = "off"; 91 | secondarycache = "none"; 92 | "com.sun:auto-snapshot" = "false"; 93 | }; 94 | }; 95 | 96 | # User filesystem. 97 | home = { 98 | type = "zfs_fs"; 99 | mountpoint = "/home"; 100 | options = { 101 | mountpoint = "legacy"; 102 | secondarycache = "none"; 103 | "com.sun:auto-snapshot" = "true"; 104 | }; 105 | }; 106 | 107 | # `journald` log. 108 | systemd-logs = { 109 | type = "zfs_fs"; 110 | mountpoint = "/var/log"; 111 | options = { 112 | mountpoint = "legacy"; 113 | secondarycache = "none"; 114 | "com.sun:auto-snapshot" = "false"; 115 | }; 116 | }; 117 | }; 118 | }; 119 | }; 120 | } 121 | -------------------------------------------------------------------------------- /config/modules/ui/zsh/home.nix: -------------------------------------------------------------------------------- 1 | { pkgs, lib, config, ... }: 2 | 3 | { 4 | programs.zsh = { 5 | dotDir = ".config/zsh"; 6 | enable = true; 7 | autosuggestion.enable = true; 8 | 9 | history = { 10 | expireDuplicatesFirst = true; 11 | extended = true; 12 | ignoreDups = true; 13 | ignoreSpace = true; 14 | save = 10000; 15 | share = true; 16 | size = 50000; 17 | }; 18 | 19 | historySubstringSearch.enable = true; 20 | 21 | plugins = [ 22 | { 23 | name = "zsh-nix-shell"; 24 | file = "nix-shell.plugin.zsh"; 25 | src = pkgs.fetchFromGitHub { 26 | owner = "chisui"; 27 | repo = "zsh-nix-shell"; 28 | rev = "v0.1.0"; 29 | sha256 = "0snhch9hfy83d4amkyxx33izvkhbwmindy0zjjk28hih1a9l2jmx"; 30 | }; 31 | } 32 | ]; 33 | 34 | initContent = '' 35 | function git-jump() { 36 | local REPO 37 | REPO=$(fd -H -t d '.git' ${config.home.homeDirectory}/Development -d 5 -E '.github' | sed 's|/\.git/$||' | awk '{ print; system("dirname \"" $0 "\"") }' | sed 's|^${config.home.homeDirectory}/Development/||' | sort -u | fzf --height 25) 38 | 39 | if [[ -n $REPO ]]; then 40 | local DEST="${config.home.homeDirectory}/Development/''${REPO}" 41 | 42 | cd "$DEST" || return 43 | # Refresh prompt after changing directory 44 | zle reset-prompt 45 | 46 | fi 47 | } 48 | 49 | zle -N git-jump 50 | 51 | function jump-to-git-root { 52 | local ROOT_DIR="$(git rev-parse --show-toplevel 2>/dev/null)" 53 | [[ -z $ROOT_DIR ]] || [[ $ROOT_DIR == $(pwd) ]] || cd $ROOT_DIR 54 | } 55 | 56 | function fzf-checkout { 57 | BRANCH=$(git branch | fzf --height 25| tr -d '[:space:]') 58 | [[ -n $BRANCH ]] && git checkout $BRANCH 59 | } 60 | 61 | function git-checkout-branch { 62 | $(git rev-parse --is-inside-work-tree >& /dev/null) 63 | 64 | if [ "$?" -eq "0" ]; then 65 | [[ -n $1 ]] && git checkout $1 || fzf-checkout 66 | fi 67 | } 68 | 69 | bindkey '^l' autosuggest-accept 70 | bindkey '^h' autosuggest-clear 71 | bindkey '^k' history-substring-search-up 72 | bindkey '^j' history-substring-search-down 73 | #bindkey '^R' history-incremental-search-backward 74 | bindkey '^g' git-jump 75 | 76 | autoload edit-command-line 77 | zle -N edit-command-line 78 | bindkey “^x^e” edit-command-line 79 | 80 | # Load Functions 81 | if [ -f ${config.xdg.configHome}/functions ]; then 82 | source ${config.xdg.configHome}/functions 83 | fi 84 | [ $TERM = "dumb" ] && unsetopt zle && PS1='$ ' 85 | ''; 86 | 87 | shellAliases = { 88 | cursor = "$HOME/.local/bin/cursor"; 89 | refresh = "exec $SHELL -l"; 90 | g = "git-jump"; 91 | gr = "jump-to-git-root"; 92 | gp = "git pull"; 93 | gf = "git fetch"; 94 | gc = "git-checkout-branch"; 95 | s = "sysz"; 96 | t = "sudo tailscale"; 97 | 98 | "~" = "cd ~"; 99 | "/" = "cd /"; 100 | ".." = "cd .."; 101 | "..." = "cd ../.."; 102 | "...." = "cd ../../.."; 103 | "....." = "cd ../../../.."; 104 | "......" = "cd ../../../../.."; 105 | "rmd" = "rm -r"; 106 | }; 107 | }; 108 | programs.fzf = { 109 | enable = true; 110 | enableZshIntegration = true; 111 | }; 112 | } 113 | -------------------------------------------------------------------------------- /config/machines/personal-computers/voice-of-evening/disk-config.nix: -------------------------------------------------------------------------------- 1 | { 2 | # Construct the partition table for the system's primary disk. 3 | disko.devices.disk.nvme0n1 = { 4 | type = "disk"; 5 | device = "/dev/nvme0n1"; 6 | content = { 7 | type = "gpt"; 8 | partitions = { 9 | # Create a large boot partition. 10 | # 11 | # NixOS creates a separate boot entry for each generation, which 12 | # can fill up the partition faster than other operating systems. 13 | # 14 | # Storage is cheap, so this can be more generous than necessary. 15 | ESP = { 16 | size = "2G"; 17 | type = "EF00"; 18 | content = { 19 | type = "filesystem"; 20 | format = "vfat"; 21 | mountpoint = "/boot"; 22 | mountOptions = [ "defaults" ]; 23 | }; 24 | }; 25 | # Partition the remainder of the disk as a LUKS container. 26 | luks = { 27 | size = "100%"; 28 | content = { 29 | type = "luks"; 30 | name = "CRYPT"; 31 | extraOpenArgs = [ ]; 32 | passwordFile = "/tmp/disk.key"; 33 | settings = { 34 | allowDiscards = true; 35 | }; 36 | content = { 37 | type = "zfs"; 38 | pool = "tank"; 39 | }; 40 | }; 41 | }; 42 | }; 43 | }; 44 | }; 45 | 46 | # Construct the primary ZFS pool for this system. 47 | disko.devices.zpool.tank = { 48 | type = "zpool"; 49 | 50 | options = { 51 | ashift = "12"; 52 | autotrim = "on"; 53 | listsnapshots = "on"; 54 | }; 55 | 56 | rootFsOptions = { 57 | acltype = "posixacl"; 58 | atime = "off"; 59 | canmount = "off"; 60 | compression = "zstd"; 61 | dnodesize = "auto"; 62 | mountpoint = "none"; 63 | normalization = "formD"; 64 | relatime = "on"; 65 | xattr = "sa"; 66 | "com.sun:auto-snapshot" = "true"; 67 | }; 68 | 69 | datasets = { 70 | # Static reservation so the pool will never be 100% full. 71 | # 72 | # If a pool fills up completely, delete this & reclaim space; don't 73 | # forget to re-create it afterwards! 74 | reservation = { 75 | type = "zfs_fs"; 76 | options = { 77 | canmount = "off"; 78 | mountpoint = "none"; 79 | refreservation = "2G"; 80 | primarycache = "none"; 81 | secondarycache = "none"; 82 | }; 83 | }; 84 | 85 | # Root filesystem. 86 | root = { 87 | type = "zfs_fs"; 88 | mountpoint = "/"; 89 | options = { 90 | mountpoint = "legacy"; 91 | secondarycache = "none"; 92 | "com.sun:auto-snapshot" = "true"; 93 | }; 94 | }; 95 | 96 | # `/nix/store` dataset; no snapshots required. 97 | nix = { 98 | type = "zfs_fs"; 99 | mountpoint = "/nix"; 100 | options = { 101 | mountpoint = "legacy"; 102 | relatime = "off"; 103 | secondarycache = "none"; 104 | "com.sun:auto-snapshot" = "false"; 105 | }; 106 | }; 107 | 108 | # User filesystem. 109 | home = { 110 | type = "zfs_fs"; 111 | mountpoint = "/home"; 112 | options = { 113 | mountpoint = "legacy"; 114 | secondarycache = "none"; 115 | "com.sun:auto-snapshot" = "true"; 116 | }; 117 | }; 118 | 119 | # `journald` log. 120 | systemd-logs = { 121 | type = "zfs_fs"; 122 | mountpoint = "/var/log"; 123 | options = { 124 | mountpoint = "legacy"; 125 | secondarycache = "none"; 126 | "com.sun:auto-snapshot" = "false"; 127 | }; 128 | }; 129 | }; 130 | }; 131 | } 132 | -------------------------------------------------------------------------------- /installer/templates/pc-disk-config.nix: -------------------------------------------------------------------------------- 1 | { 2 | # Construct the partition table for the system's primary disk. 3 | disko.devices.disk.sda = { 4 | type = "disk"; 5 | device = "/dev/sda"; 6 | content = { 7 | type = "gpt"; 8 | partitions = { 9 | # Create a large boot partition. 10 | # 11 | # NixOS creates a separate boot entry for each generation, which 12 | # can fill up the partition faster than other operating systems. 13 | # 14 | # Storage is cheap, so this can be more generous than necessary. 15 | ESP = { 16 | size = "2G"; 17 | type = "EF00"; 18 | content = { 19 | type = "filesystem"; 20 | format = "vfat"; 21 | mountpoint = "/boot"; 22 | mountOptions = [ "defaults" ]; 23 | }; 24 | }; 25 | # Partition the remainder of the disk as a LUKS container. 26 | # 27 | # This system should be able to boot without manual intervention, so 28 | # the LUKS container will be set up to use a random segment data from 29 | # an external device constructed in a separate step. 30 | luks = { 31 | size = "100%"; 32 | content = { 33 | type = "luks"; 34 | name = "CRYPT"; 35 | extraOpenArgs = [ ]; 36 | content = { 37 | type = "zfs"; 38 | pool = "tank"; 39 | }; 40 | }; 41 | }; 42 | }; 43 | }; 44 | }; 45 | 46 | # Construct the primary ZFS pool for this system. 47 | disko.devices.zpool.tank = { 48 | type = "zpool"; 49 | 50 | options = { 51 | ashift = "12"; 52 | autotrim = "on"; 53 | listsnapshots = "on"; 54 | }; 55 | 56 | rootFsOptions = { 57 | acltype = "posixacl"; 58 | atime = "off"; 59 | canmount = "off"; 60 | compression = "zstd"; 61 | dnodesize = "auto"; 62 | mountpoint = "none"; 63 | normalization = "formD"; 64 | relatime = "on"; 65 | xattr = "sa"; 66 | "com.sun:auto-snapshot" = "true"; 67 | }; 68 | 69 | datasets = { 70 | # Static reservation so the pool will never be 100% full. 71 | # 72 | # If a pool fills up completely, delete this & reclaim space; don't 73 | # forget to re-create it afterwards! 74 | reservation = { 75 | type = "zfs_fs"; 76 | options = { 77 | canmount = "off"; 78 | mountpoint = "none"; 79 | refreservation = "2G"; 80 | primarycache = "none"; 81 | secondarycache = "none"; 82 | }; 83 | }; 84 | 85 | # Root filesystem. 86 | root = { 87 | type = "zfs_fs"; 88 | mountpoint = "/"; 89 | options = { 90 | mountpoint = "legacy"; 91 | secondarycache = "none"; 92 | "com.sun:auto-snapshot" = "true"; 93 | }; 94 | }; 95 | 96 | # `/nix/store` dataset; no snapshots required. 97 | nix = { 98 | type = "zfs_fs"; 99 | mountpoint = "/nix"; 100 | options = { 101 | mountpoint = "legacy"; 102 | relatime = "off"; 103 | secondarycache = "none"; 104 | "com.sun:auto-snapshot" = "false"; 105 | }; 106 | }; 107 | 108 | # User filesystem. 109 | home = { 110 | type = "zfs_fs"; 111 | mountpoint = "/home"; 112 | options = { 113 | mountpoint = "legacy"; 114 | secondarycache = "none"; 115 | "com.sun:auto-snapshot" = "true"; 116 | }; 117 | }; 118 | 119 | # `journald` log. 120 | systemd-logs = { 121 | type = "zfs_fs"; 122 | mountpoint = "/var/log"; 123 | options = { 124 | mountpoint = "legacy"; 125 | secondarycache = "none"; 126 | "com.sun:auto-snapshot" = "false"; 127 | }; 128 | }; 129 | }; 130 | }; 131 | } -------------------------------------------------------------------------------- /config/machines/personal-computers/lorean/disk-config.nix: -------------------------------------------------------------------------------- 1 | { 2 | # Construct the partition table for the system's primary disk. 3 | disko.devices.disk.sda = { 4 | type = "disk"; 5 | device = "/dev/sda"; 6 | content = { 7 | type = "gpt"; 8 | partitions = { 9 | # Create a large boot partition. 10 | # 11 | # NixOS creates a separate boot entry for each generation, which 12 | # can fill up the partition faster than other operating systems. 13 | # 14 | # Storage is cheap, so this can be more generous than necessary. 15 | ESP = { 16 | size = "2G"; 17 | type = "EF00"; 18 | content = { 19 | type = "filesystem"; 20 | format = "vfat"; 21 | mountpoint = "/boot"; 22 | mountOptions = [ "defaults" ]; 23 | }; 24 | }; 25 | # Partition the remainder of the disk as a LUKS container. 26 | # 27 | # This system should be able to boot without manual intervention, so 28 | # the LUKS container will be set up to use a random segment data from 29 | # an external device constructed in a separate step. 30 | luks = { 31 | size = "100%"; 32 | content = { 33 | type = "luks"; 34 | name = "CRYPT"; 35 | extraOpenArgs = [ ]; 36 | content = { 37 | type = "zfs"; 38 | pool = "tank"; 39 | }; 40 | }; 41 | }; 42 | }; 43 | }; 44 | }; 45 | 46 | # Construct the primary ZFS pool for this system. 47 | disko.devices.zpool.tank = { 48 | type = "zpool"; 49 | 50 | options = { 51 | ashift = "12"; 52 | autotrim = "on"; 53 | listsnapshots = "on"; 54 | }; 55 | 56 | rootFsOptions = { 57 | acltype = "posixacl"; 58 | atime = "off"; 59 | canmount = "off"; 60 | compression = "zstd"; 61 | dnodesize = "auto"; 62 | mountpoint = "none"; 63 | normalization = "formD"; 64 | relatime = "on"; 65 | xattr = "sa"; 66 | "com.sun:auto-snapshot" = "true"; 67 | }; 68 | 69 | datasets = { 70 | # Static reservation so the pool will never be 100% full. 71 | # 72 | # If a pool fills up completely, delete this & reclaim space; don't 73 | # forget to re-create it afterwards! 74 | reservation = { 75 | type = "zfs_fs"; 76 | options = { 77 | canmount = "off"; 78 | mountpoint = "none"; 79 | refreservation = "2G"; 80 | primarycache = "none"; 81 | secondarycache = "none"; 82 | }; 83 | }; 84 | 85 | # Root filesystem. 86 | root = { 87 | type = "zfs_fs"; 88 | mountpoint = "/"; 89 | options = { 90 | mountpoint = "legacy"; 91 | secondarycache = "none"; 92 | "com.sun:auto-snapshot" = "true"; 93 | }; 94 | }; 95 | 96 | # `/nix/store` dataset; no snapshots required. 97 | nix = { 98 | type = "zfs_fs"; 99 | mountpoint = "/nix"; 100 | options = { 101 | mountpoint = "legacy"; 102 | relatime = "off"; 103 | secondarycache = "none"; 104 | "com.sun:auto-snapshot" = "false"; 105 | }; 106 | }; 107 | 108 | # User filesystem. 109 | home = { 110 | type = "zfs_fs"; 111 | mountpoint = "/home"; 112 | options = { 113 | mountpoint = "legacy"; 114 | secondarycache = "none"; 115 | "com.sun:auto-snapshot" = "true"; 116 | }; 117 | }; 118 | 119 | # `journald` log. 120 | systemd-logs = { 121 | type = "zfs_fs"; 122 | mountpoint = "/var/log"; 123 | options = { 124 | mountpoint = "legacy"; 125 | secondarycache = "none"; 126 | "com.sun:auto-snapshot" = "false"; 127 | }; 128 | }; 129 | }; 130 | }; 131 | } 132 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Nixos Homelab and Dotfiles 2 | 3 | ## Structure 4 | 5 | - [config](https://github.com/solomon-b/nixos-config/tree/main/config): This folder contains all configurations. It is organized into the following subdirectories: 6 | - [machines/personal-computers/](https://github.com/solomon-b/nixos-config/tree/main/config/machines/personal-computers): The entrypoints to desktop PCs. 7 | - [machines/servers/](https://github.com/solomon-b/nixos-config/tree/main/config/machines/servers): The entrypoints to servers. 8 | - [profiles](https://github.com/solomon-b/nixos-config/tree/main/config/profiles): Basic nixos module sets for virtual machines, physical machines, and PCs. These are imported into machine configurations to form a standard baseline for all my systems. 9 | - [modules](https://github.com/solomon-b/nixos-config/tree/main/config/modules): Nixos configuration modules which constitute the `profiles`. Any machine unique configuration should go in the `machines/` folder for the specific machine in question. 10 | - [modules](https://github.com/solomon-b/nixos-config/tree/main/modules): Custom nixos and home-manager modules I use in my `config`. 11 | - [installer](https://github.com/solomon-b/nixos-config/tree/main/installer): Custom nixos installer iso I use to quickly provision new machines. 12 | ## Usage 13 | ### Adding A New Machine 14 | 15 | 1. Create a machine profile in `config/machines`. Import a non-existent `./hardware.nix` file. 16 | 2. Boot the new machine off a linux boot disk where you have authorizedKeys for `root`. 17 | 18 | You can build one with: 19 | ``` 20 | nix build '.#nixos-iso' 21 | ``` 22 | This ISO will include SSH public keys from `config/modules/security/sshd/public-keys.nix` as authorizedKeys for `root`. 23 | 3. Generate a hardware config for the new machine: 24 | ``` 25 | ssh "root@${IP}" nixos-generate-config --no-filesystems --show-hardware-config > "config/machines/servers/${MACHINE}/hardware.nix" 26 | ``` 27 | 4. Add ssh host keys to `pass`: 28 | ``` 29 | ssh "root@${IP}" cat /etc/ssh/ssh_host_ed25519_key.pub | pass insert -m "machine/${MACHINE}/ssh-host-key/ed25519/public" 30 | ssh "root@${IP}" cat /etc/ssh/ssh_host_ed25519_key | pass insert -m "machine/${MACHINE}/ssh-host-key/ed25519/private" 31 | ssh "root@${IP}" cat /etc/ssh/ssh_host_rsa_key.pub | pass insert -m "machine/${MACHINE}/ssh-host-key/rsa/public" 32 | ssh "root@${IP}" cat /etc/ssh/ssh_host_rsa_key | pass insert -m "machine/${MACHINE}/ssh-host-key/rsa/private" 33 | ``` 34 | 35 | User Keys 36 | ``` 37 | ssh-keygen -t ed25519 -C "solomon@${MACHINE}" 38 | cat /tmp/ed25519_key | pass insert -m "machine/${MACHINE}/solomon/ssh/private-key" 39 | cat /tmp/ed25519_key.pub | pass insert -m "machine/${MACHINE}/solomon/ssh/public-key" 40 | ``` 41 | 5. Generate an `age` key for the new machine and add it to `.sops.yaml`. 42 | ``` 43 | pass machine/${MACHINE}/ssh-host-key/ed25519/public | ssh-to-age 44 | vim .sops.yaml 45 | sops updatekeys --yes secrets.yaml 46 | ``` 47 | 6. Run the installer script 48 | 49 | For physical machines: 50 | ``` 51 | nix run '.#install-pc' 52 | ``` 53 | 54 | For Virtual Machines: 55 | ``` 56 | nix run '.#install-server' 57 | ``` 58 | 59 | The scripts use [nixos-anywhere](https://github.com/numtide/nixos-anywhere) to provision a new phyiscal machine. 60 | 7. Detach the boot disk and reboot. 61 | 62 | ### Post install 63 | Once you have your machine provisioned you can use `colmena apply --on $MACHINE` to deploy it. 64 | 65 | If you are deploying with [colmena](https://colmena.cli.rs/unstable/reference/deployment.html#deploymenttargethost) then you will either need a DNS entry for your machine names or you will need to tweak the `mkMachine` function and set the IP with [deployment.targetHost](https://colmena.cli.rs/unstable/reference/deployment.html#deploymenttargethost). 66 | 67 | ### Deployment 68 | Build all servers: 69 | ``` 70 | $ colmena apply --on @server 71 | ``` 72 | 73 | Build a specific server: 74 | ``` 75 | $ colmena apply --on @sower 76 | ``` 77 | 78 | Build all PCs: 79 | ``` 80 | $ colmena apply --on @pc 81 | ``` 82 | 83 | ## Prior Art/Inspirations 84 | 85 | - https://github.com/cprussin/dotfiles 86 | - https://github.com/jkachmar/dotnix 87 | - https://github.com/wagdav/homelab 88 | -------------------------------------------------------------------------------- /config/machines/servers/sower/home-assistant.nix: -------------------------------------------------------------------------------- 1 | { config, pkgs, ... }: 2 | 3 | let 4 | home-assistant-image = "ghcr.io/home-assistant/home-assistant:stable"; 5 | home-assistant-port = "8123"; 6 | in 7 | { 8 | fileSystems."/mnt/home-assistant" = { 9 | device = "192.168.5.6:/mnt/tank/app-data/home-assistant"; 10 | fsType = "nfs"; 11 | options = [ 12 | "defaults" # → rw,suid,dev,exec,auto,nouser,async 13 | "vers=3" # force NFSv3 14 | "proto=tcp" # use TCP transport 15 | "intr" # allow signals (Ctrl-C) to interrupt 16 | "timeo=30" # initial timeout = 3 s (30 deciseconds) 17 | "retrans=3" # retry only 3 times (~9 s total) 18 | "_netdev" # wait for network before mounting 19 | ]; 20 | }; 21 | 22 | fileSystems."/mnt/mosquitto" = { 23 | device = "192.168.5.6:/mnt/tank/app-data/mosquitto"; 24 | fsType = "nfs"; 25 | options = [ 26 | "defaults" # → rw,suid,dev,exec,auto,nouser,async 27 | "vers=3" # force NFSv3 28 | "proto=tcp" # use TCP transport 29 | "intr" # allow signals (Ctrl-C) to interrupt 30 | "timeo=30" # initial timeout = 3 s (30 deciseconds) 31 | "retrans=3" # retry only 3 times (~9 s total) 32 | "_netdev" # wait for network before mounting 33 | ]; 34 | }; 35 | 36 | fileSystems."/mnt/zigbee2mqtt" = { 37 | device = "192.168.5.6:/mnt/tank/app-data/zigbee2mqtt"; 38 | fsType = "nfs"; 39 | options = [ 40 | "defaults" # → rw,suid,dev,exec,auto,nouser,async 41 | "vers=3" # force NFSv3 42 | "proto=tcp" # use TCP transport 43 | "intr" # allow signals (Ctrl-C) to interrupt 44 | "timeo=30" # initial timeout = 3 s (30 deciseconds) 45 | "retrans=3" # retry only 3 times (~9 s total) 46 | "_netdev" # wait for network before mounting 47 | ]; 48 | }; 49 | 50 | networking.firewall = { 51 | enable = true; 52 | # For Mosquitto: 53 | allowedTCPPorts = [ 1883 ]; 54 | }; 55 | 56 | virtualisation.oci-containers.containers.home-assistant = { 57 | image = home-assistant-image; 58 | environment.TZ = "America/Los_Angeles"; 59 | ports = [ "${home-assistant-port}:${home-assistant-port}" ]; 60 | 61 | extraOptions = [ 62 | "--pull=always" 63 | "--network=host" 64 | ]; 65 | 66 | volumes = [ "/mnt/home-assistant:/config" ]; 67 | 68 | devices = [ 69 | "/dev/ttyUSB0:/dev/ttyUSB0" 70 | ]; 71 | 72 | autoStart = true; 73 | }; 74 | 75 | # https://nixos.wiki/wiki/Mosquitto 76 | services.mosquitto = { 77 | enable = true; 78 | dataDir = "/mnt/mosquitto"; 79 | # TODO: Add auth: 80 | listeners = [ 81 | { 82 | acl = [ "pattern readwrite #" ]; 83 | omitPasswordAuth = true; 84 | settings.allow_anonymous = true; 85 | } 86 | ]; 87 | }; 88 | 89 | services.zigbee2mqtt = { 90 | enable = true; 91 | dataDir = "/mnt/zigbee2mqtt"; 92 | settings = { 93 | homeassistant = config.services.home-assistant.enable; 94 | permit_join = true; 95 | 96 | serial = { 97 | port = "/dev/ttyUSB0"; 98 | baudrate = 115200; 99 | rtscts = false; 100 | adapter = "ember"; 101 | }; 102 | 103 | mqtt = { 104 | server = "mqtt://localhost:1883"; 105 | }; 106 | 107 | frontend = { 108 | port = 8080; 109 | host = "0.0.0.0"; 110 | }; 111 | }; 112 | }; 113 | 114 | services.nginx.virtualHosts."home-assistant.service.home.arpa" = { 115 | extraConfig = "proxy_buffering off;"; 116 | locations."/" = { 117 | proxyPass = "http://localhost:8123"; 118 | proxyWebsockets = true; 119 | }; 120 | }; 121 | 122 | services.nginx.virtualHosts."home-assistant.local.home.arpa" = { 123 | extraConfig = "proxy_buffering off;"; 124 | locations."/" = { 125 | proxyPass = "http://localhost:8123"; 126 | proxyWebsockets = true; 127 | }; 128 | }; 129 | 130 | services.nginx.virtualHosts."zigbee2mqtt.service.home.arpa" = { 131 | extraConfig = "proxy_buffering off;"; 132 | locations."/" = { 133 | proxyPass = "http://localhost:8080"; 134 | proxyWebsockets = true; 135 | }; 136 | }; 137 | } 138 | -------------------------------------------------------------------------------- /config/modules/ui/eww/eww.scss: -------------------------------------------------------------------------------- 1 | /** Global *******************************************/ 2 | *{ 3 | all: unset; 4 | font-family: Iosevka; 5 | } 6 | 7 | .background { 8 | color: #2d2d2d; 9 | } 10 | 11 | .altBackground { 12 | color: #333333; 13 | } 14 | 15 | .selection { 16 | color: #515151; 17 | } 18 | 19 | .foreground { 20 | color: #cccccc; 21 | } 22 | 23 | .comment { 24 | color: #999999; 25 | } 26 | 27 | .red { 28 | color: #f2777a; 29 | } 30 | 31 | .orange { 32 | color: #f99157; 33 | } 34 | 35 | .yellow { 36 | color: #ffcc66; 37 | } 38 | 39 | .green { 40 | color: #99cc99; 41 | } 42 | 43 | .aqua { 44 | color: #66cccc; 45 | } 46 | 47 | .blue { 48 | color: #6699cc; 49 | } 50 | 51 | .purple { 52 | color: #cc99cc; 53 | } 54 | 55 | .bold { 56 | font-weight : bold; 57 | } 58 | 59 | /** Background ***************************************/ 60 | .bg { 61 | background-image: url("/home/solomon/Public/wallpapers/Yosemite-Color-Block.png"); 62 | background-color: #474D59; 63 | opacity: 1; 64 | } 65 | 66 | /** Generic window ***********************************/ 67 | .genwin { 68 | background-color: #333333; 69 | border-radius: 16px; 70 | font-size : 1.5rem; 71 | font-weight : normal; 72 | //padding: 20px; 73 | } 74 | 75 | /** Profile ******************************************/ 76 | .face { 77 | background-size: 400px; 78 | min-height: 200px; 79 | min-width: 200px; 80 | //margin: 65px 0px 0px 0px; 81 | border-radius: 100%; 82 | } 83 | 84 | .fullname { 85 | font-size : 30px; 86 | font-weight : bold; 87 | } 88 | 89 | .username { 90 | font-size : 22px; 91 | font-weight : bold; 92 | //margin : -15px 0px 0px 0px; 93 | } 94 | 95 | /** System ********************************************/ 96 | .icon { 97 | font-size : 32px; 98 | font-weight : normal; 99 | } 100 | 101 | .hdd_bar, .cpu_bar, .mem_bar, .bright_bar, .bat_bar, scale trough { 102 | all: unset; 103 | background-color: #2d2d2d; 104 | border-radius: 16px; 105 | min-height: 18px; 106 | min-width: 240px; 107 | } 108 | 109 | .hdd_bar, .cpu_bar, .mem_bar, .bright_bar, .bat_bar, scale trough highlight { 110 | all: unset; 111 | border-radius: 16px; 112 | } 113 | .hdd_bar scale trough highlight { 114 | background-color: #cc99cc; 115 | } 116 | .cpu_bar scale trough highlight { 117 | background-color: #f2777a; 118 | } 119 | .mem_bar scale trough highlight { 120 | background-color: #99cc99; 121 | } 122 | .vol_bar scale trough highlight { 123 | background-color: #6699cc; 124 | } 125 | .bright_bar scale trough highlight { 126 | background-color: #ffcc66; 127 | } 128 | .bat_bar scale trough highlight { 129 | background-color: #66cccc; 130 | } 131 | 132 | /** Clock ********************************************/ 133 | .time_hour, .time_min { 134 | font-size : 70px; 135 | font-weight : bold; 136 | } 137 | 138 | .time_mer { 139 | font-size : 40px; 140 | font-weight : bold; 141 | } 142 | 143 | .time_day { 144 | font-size : 30px; 145 | font-weight : normal; 146 | } 147 | 148 | /** Uptime ********************************************/ 149 | .icontimer { 150 | font-size : 90px; 151 | font-weight : normal; 152 | } 153 | 154 | .uphour { 155 | font-size : 42px; 156 | font-weight : bold; 157 | } 158 | 159 | .upmin { 160 | font-size : 32px; 161 | font-weight : bold; 162 | } 163 | 164 | /** Weather ***************************************/ 165 | .iconweather { 166 | font-family: Iosevka Nerd Font; 167 | font-size : 120px; 168 | font-weight : normal; 169 | //margin : 15px 0px 0px 30px; 170 | } 171 | 172 | .label_temp { 173 | color : #A6D1DD; 174 | font-size : 80px; 175 | font-weight : bold; 176 | //margin : -15px 30px 0px 0px; 177 | } 178 | 179 | .label_stat { 180 | color : #BF616A; 181 | font-size : 38px; 182 | font-weight : bold; 183 | } 184 | 185 | .label_quote { 186 | color : #E5E5E5; 187 | font-size : 18px; 188 | font-weight : normal; 189 | } 190 | 191 | /** Power buttons ***************************************/ 192 | .btn_logout, .btn_sleep, .btn_reboot, .btn_poweroff { 193 | font-size : 48px; 194 | font-weight : bold; 195 | } 196 | 197 | tooltip.background { 198 | background-color: #333333; 199 | font-size: 18; 200 | color: #cccccc; 201 | } 202 | -------------------------------------------------------------------------------- /config/machines/servers/accompaniment-of-shadows/coredns.nix: -------------------------------------------------------------------------------- 1 | { ... }: 2 | 3 | { 4 | boot.kernel.sysctl."net.ipv4.ip_forward" = 1; 5 | 6 | systemd.services.coredns = { 7 | after = [ "tailscaled.service" ]; 8 | wants = [ "tailscaled.service" ]; 9 | }; 10 | 11 | services.coredns = { 12 | enable = true; 13 | config = '' 14 | # Block 1: Tailscale clients (queries to 100.123.147.26) 15 | .:53 { 16 | bind 100.123.147.26 17 | 18 | cache 30 19 | errors 20 | log 21 | 22 | forward . 1.1.1.1 1.0.0.1 { 23 | except home.arpa 24 | } 25 | 26 | hosts { 27 | # Machines without Tailscale (accessed via subnet route) 28 | 192.168.5.6 sandra-voi.home.arpa 29 | 192.168.5.104 apollyon 30 | 31 | # Services on accompaniment-of-shadows (Tailscale IP) 32 | 100.123.147.26 filebrowser.service.home.arpa 33 | 100.123.147.26 homebox.service.home.arpa 34 | 100.123.147.26 homepage.service.home.arpa 35 | 100.123.147.26 lubelogger.service.home.arpa 36 | 100.123.147.26 qbittorrent.service.home.arpa 37 | 100.123.147.26 bazarr.service.home.arpa 38 | 100.123.147.26 lidarr.service.home.arpa 39 | 100.123.147.26 prowlarr.service.home.arpa 40 | 100.123.147.26 profilarr.service.home.arpa 41 | 100.123.147.26 radarr.service.home.arpa 42 | 100.123.147.26 sonarr.service.home.arpa 43 | 100.123.147.26 sabnzbd.service.home.arpa 44 | 100.123.147.26 planka.service.home.arpa 45 | 100.123.147.26 paperless.service.home.arpa 46 | 100.123.147.26 grafana.service.home.arpa 47 | 100.123.147.26 uptime.service.home.arpa 48 | 49 | # Services on sower (Tailscale IP) 50 | 100.80.98.4 jellyfin.service.home.arpa 51 | 100.80.98.4 jellyseerr.service.home.arpa 52 | 100.80.98.4 podgrab.service.home.arpa 53 | 100.80.98.4 navidrome.service.home.arpa 54 | 100.80.98.4 immich.service.home.arpa 55 | 100.80.98.4 tubearchivist.service.home.arpa 56 | 100.80.98.4 home-assistant.service.home.arpa 57 | 100.80.98.4 zigbee2mqtt.service.home.arpa 58 | 100.80.98.4 soulseek.service.home.arpa 59 | 60 | fallthrough 61 | } 62 | } 63 | 64 | # Block 2: LAN clients (queries to 192.168.5.105) 65 | .:53 { 66 | bind 192.168.5.105 67 | 68 | cache 30 69 | errors 70 | log 71 | 72 | forward . 1.1.1.1 1.0.0.1 { 73 | except home.arpa 74 | } 75 | 76 | hosts { 77 | # Machines on LAN 78 | 192.168.5.6 sandra-voi.home.arpa 79 | 192.168.5.104 apollyon 80 | 81 | # Services on accompaniment-of-shadows (local IP) 82 | 192.168.5.105 filebrowser.service.home.arpa 83 | 192.168.5.105 homebox.service.home.arpa 84 | 192.168.5.105 homepage.service.home.arpa 85 | 192.168.5.105 lubelogger.service.home.arpa 86 | 192.168.5.105 qbittorrent.service.home.arpa 87 | 192.168.5.105 bazarr.service.home.arpa 88 | 192.168.5.105 lidarr.service.home.arpa 89 | 192.168.5.105 prowlarr.service.home.arpa 90 | 192.168.5.105 profilarr.service.home.arpa 91 | 192.168.5.105 radarr.service.home.arpa 92 | 192.168.5.105 sonarr.service.home.arpa 93 | 192.168.5.105 sabnzbd.service.home.arpa 94 | 192.168.5.105 planka.service.home.arpa 95 | 192.168.5.105 paperless.service.home.arpa 96 | 192.168.5.105 grafana.service.home.arpa 97 | 192.168.5.105 uptime.service.home.arpa 98 | 99 | # Services on sower (local IP) 100 | 192.168.5.7 jellyfin.service.home.arpa 101 | 192.168.5.7 jellyseerr.service.home.arpa 102 | 192.168.5.7 podgrab.service.home.arpa 103 | 192.168.5.7 navidrome.service.home.arpa 104 | 192.168.5.7 immich.service.home.arpa 105 | 192.168.5.7 tubearchivist.service.home.arpa 106 | 192.168.5.7 home-assistant.service.home.arpa 107 | 192.168.5.7 zigbee2mqtt.service.home.arpa 108 | 192.168.5.7 soulseek.service.home.arpa 109 | 110 | fallthrough 111 | } 112 | } 113 | ''; 114 | }; 115 | } 116 | -------------------------------------------------------------------------------- /config/modules/ui/st/st-blinking_cursor-20230819-3a6d6d7.diff: -------------------------------------------------------------------------------- 1 | From feec0e2239d81373bd2ffcc28bdcf331328391f6 Mon Sep 17 00:00:00 2001 2 | From: Steven Ward 3 | Date: Sat, 19 Aug 2023 08:05:36 -0500 4 | Subject: [PATCH] Add different cursor styles 5 | 6 | --- 7 | config.def.h | 19 +++++++++++++------ 8 | x.c | 47 +++++++++++++++++++++++++++++++++++------------ 9 | 2 files changed, 48 insertions(+), 18 deletions(-) 10 | 11 | diff --git a/config.def.h b/config.def.h 12 | index 91ab8ca..b33a2ac 100644 13 | --- a/config.def.h 14 | +++ b/config.def.h 15 | @@ -135,13 +135,20 @@ unsigned int defaultcs = 256; 16 | static unsigned int defaultrcs = 257; 17 | 18 | /* 19 | - * Default shape of cursor 20 | - * 2: Block ("█") 21 | - * 4: Underline ("_") 22 | - * 6: Bar ("|") 23 | - * 7: Snowman ("☃") 24 | + * https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h4-Functions-using-CSI-_-ordered-by-the-final-character-lparen-s-rparen:CSI-Ps-SP-q.1D81 25 | + * Default style of cursor 26 | + * 0: blinking block 27 | + * 1: blinking block (default) 28 | + * 2: steady block ("█") 29 | + * 3: blinking underline 30 | + * 4: steady underline ("_") 31 | + * 5: blinking bar 32 | + * 6: steady bar ("|") 33 | + * 7: blinking st cursor 34 | + * 8: steady st cursor 35 | */ 36 | -static unsigned int cursorshape = 2; 37 | +static unsigned int cursorstyle = 1; 38 | +static Rune stcursor = 0x2603; /* snowman ("☃") */ 39 | 40 | /* 41 | * Default columns and rows numbers 42 | diff --git a/x.c b/x.c 43 | index aa09997..b33a963 100644 44 | --- a/x.c 45 | +++ b/x.c 46 | @@ -253,6 +253,7 @@ static char *opt_name = NULL; 47 | static char *opt_title = NULL; 48 | 49 | static uint buttons; /* bit field of pressed buttons */ 50 | +static int cursorblinks = 0; 51 | 52 | void 53 | clipcopy(const Arg *dummy) 54 | @@ -1558,29 +1559,44 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) 55 | /* draw the new one */ 56 | if (IS_SET(MODE_FOCUSED)) { 57 | switch (win.cursor) { 58 | - case 7: /* st extension */ 59 | - g.u = 0x2603; /* snowman (U+2603) */ 60 | + default: 61 | + case 0: /* blinking block */ 62 | + case 1: /* blinking block (default) */ 63 | + if (IS_SET(MODE_BLINK)) 64 | + break; 65 | /* FALLTHROUGH */ 66 | - case 0: /* Blinking Block */ 67 | - case 1: /* Blinking Block (Default) */ 68 | - case 2: /* Steady Block */ 69 | + case 2: /* steady block */ 70 | xdrawglyph(g, cx, cy); 71 | break; 72 | - case 3: /* Blinking Underline */ 73 | - case 4: /* Steady Underline */ 74 | + case 3: /* blinking underline */ 75 | + if (IS_SET(MODE_BLINK)) 76 | + break; 77 | + /* FALLTHROUGH */ 78 | + case 4: /* steady underline */ 79 | XftDrawRect(xw.draw, &drawcol, 80 | borderpx + cx * win.cw, 81 | borderpx + (cy + 1) * win.ch - \ 82 | cursorthickness, 83 | win.cw, cursorthickness); 84 | break; 85 | - case 5: /* Blinking bar */ 86 | - case 6: /* Steady bar */ 87 | + case 5: /* blinking bar */ 88 | + if (IS_SET(MODE_BLINK)) 89 | + break; 90 | + /* FALLTHROUGH */ 91 | + case 6: /* steady bar */ 92 | XftDrawRect(xw.draw, &drawcol, 93 | borderpx + cx * win.cw, 94 | borderpx + cy * win.ch, 95 | cursorthickness, win.ch); 96 | break; 97 | + case 7: /* blinking st cursor */ 98 | + if (IS_SET(MODE_BLINK)) 99 | + break; 100 | + /* FALLTHROUGH */ 101 | + case 8: /* steady st cursor */ 102 | + g.u = stcursor; 103 | + xdrawglyph(g, cx, cy); 104 | + break; 105 | } 106 | } else { 107 | XftDrawRect(xw.draw, &drawcol, 108 | @@ -1737,9 +1753,12 @@ xsetmode(int set, unsigned int flags) 109 | int 110 | xsetcursor(int cursor) 111 | { 112 | - if (!BETWEEN(cursor, 0, 7)) /* 7: st extension */ 113 | + if (!BETWEEN(cursor, 0, 8)) /* 7-8: st extensions */ 114 | return 1; 115 | win.cursor = cursor; 116 | + cursorblinks = win.cursor == 0 || win.cursor == 1 || 117 | + win.cursor == 3 || win.cursor == 5 || 118 | + win.cursor == 7; 119 | return 0; 120 | } 121 | 122 | @@ -1986,6 +2005,10 @@ run(void) 123 | if (FD_ISSET(ttyfd, &rfd) || xev) { 124 | if (!drawing) { 125 | trigger = now; 126 | + if (IS_SET(MODE_BLINK)) { 127 | + win.mode ^= MODE_BLINK; 128 | + } 129 | + lastblink = now; 130 | drawing = 1; 131 | } 132 | timeout = (maxlatency - TIMEDIFF(now, trigger)) \ 133 | @@ -1996,7 +2019,7 @@ run(void) 134 | 135 | /* idle detected or maxlatency exhausted -> draw */ 136 | timeout = -1; 137 | - if (blinktimeout && tattrset(ATTR_BLINK)) { 138 | + if (blinktimeout && (cursorblinks || tattrset(ATTR_BLINK))) { 139 | timeout = blinktimeout - TIMEDIFF(now, lastblink); 140 | if (timeout <= 0) { 141 | if (-timeout > blinktimeout) /* start visible */ 142 | @@ -2032,7 +2055,7 @@ main(int argc, char *argv[]) 143 | { 144 | xw.l = xw.t = 0; 145 | xw.isfixed = False; 146 | - xsetcursor(cursorshape); 147 | + xsetcursor(cursorstyle); 148 | 149 | ARGBEGIN { 150 | case 'a': 151 | -- 152 | 2.41.0 153 | -------------------------------------------------------------------------------- /modules/nixos/qbittorrent.nix: -------------------------------------------------------------------------------- 1 | # TODO: Figure out how to merge config options. Reference syncthing 2 | # module. 3 | { config, pkgs, lib, ... }: 4 | 5 | with lib; 6 | let 7 | cfg = config.services.qBittorrent; 8 | defaultUser = "qBittorrent"; 9 | defaultGroup = defaultUser; 10 | 11 | webUIAddressSubmodule = lib.types.submodule { 12 | options = { 13 | address = lib.mkOption { 14 | default = "127.0.0.1"; 15 | type = lib.types.str; 16 | description = "The IP address to which the webui will bind."; 17 | }; 18 | port = lib.mkOption { 19 | default = 8080; 20 | type = lib.types.int; 21 | description = "The port to which the webui will bind."; 22 | }; 23 | }; 24 | }; 25 | 26 | initializeAndRun = pkgs.writers.writeBash "initializeAndRun-qBittorrent-config" '' 27 | set -efu 28 | 29 | mkdir -p ${cfg.configDir} 30 | 31 | if [ ! -f ${cfg.configDir}/qBittorrent.conf ]; then 32 | cat >${cfg.configDir}/qBittorrent.conf <${defaultUser} will be created. 79 | ''; 80 | }; 81 | 82 | group = mkOption { 83 | type = types.str; 84 | default = defaultGroup; 85 | example = "yourGroup"; 86 | description = '' 87 | The group to run Syncthing under. 88 | By default, a group named ${defaultGroup} will be created. 89 | ''; 90 | }; 91 | 92 | dataDir = mkOption { 93 | type = types.path; 94 | default = "/var/lib/${defaultUser}"; 95 | example = "/home/yourUser"; 96 | description = '' 97 | The path where qBittorrent config and state lives. 98 | ''; 99 | }; 100 | 101 | configDir = mkOption { 102 | type = types.path; 103 | description = '' 104 | The path where the settings will exist. 105 | ''; 106 | default = cfg.dataDir + "/.config/qBittorrent"; 107 | defaultText = literalDocBook '' 108 | [LegalNotice] 109 | Accepted=true 110 | 111 | [Network] 112 | Cookies=@Invalid() 113 | 114 | [Preferences] 115 | Connection\PortRangeMin=62876 116 | Queueing\QueueingEnabled=false 117 | WebUI\Port=${cfg.webUIAddress.port} 118 | ''; 119 | }; 120 | 121 | package = mkOption { 122 | type = types.package; 123 | default = pkgs.qbittorrent-nox; 124 | defaultText = literalExpression "pkgs.qbittorrent-nox"; 125 | description = '' 126 | The qbittorrent package to use. 127 | ''; 128 | }; 129 | }; 130 | 131 | config = mkIf cfg.enable { 132 | networking.firewall.allowedTCPPorts = 133 | mkIf cfg.openFirewall [ cfg.webUIAddress.port ]; 134 | 135 | systemd.packages = [ cfg.package ]; 136 | 137 | users.users = mkIf (cfg.systemService && cfg.user == defaultUser) { 138 | ${defaultUser} = 139 | { 140 | group = cfg.group; 141 | home = cfg.dataDir; 142 | createHome = true; 143 | uid = 999; 144 | isSystemUser = true; 145 | description = "qBittorrent service user"; 146 | }; 147 | }; 148 | 149 | users.groups = mkIf (cfg.systemService && cfg.group == defaultGroup) { 150 | ${defaultGroup}.gid = 999; 151 | }; 152 | 153 | systemd.services = { 154 | qBittorrent = mkIf cfg.systemService { 155 | description = "aBittorrent service"; 156 | documentation = [ "man:qbittorrent-nox(1)" ]; 157 | wantedBy = [ "multi-user.target" ]; 158 | wants = [ "multi-user.target" ]; 159 | after = [ "network-online.target" "nss-lookup.target" ]; 160 | serviceConfig = { 161 | Type = "exec"; 162 | User = cfg.user; 163 | Group = cfg.group; 164 | ExecStart = initializeAndRun; 165 | }; 166 | }; 167 | }; 168 | }; 169 | } 170 | -------------------------------------------------------------------------------- /config/machines/servers/sower/tubearchivist.nix: -------------------------------------------------------------------------------- 1 | { config, pkgs, ... }: 2 | 3 | let 4 | media = "/mnt/tubearchivist/media"; 5 | cache = "/mnt/tubearchivist/cache"; 6 | in 7 | { 8 | fileSystems."/mnt/tubearchivist/media" = { 9 | device = "192.168.5.6:/mnt/tank/app-data/tube-archivist/media"; 10 | fsType = "nfs"; 11 | options = [ 12 | "defaults" # → rw,suid,dev,exec,auto,nouser,async 13 | "vers=3" # force NFSv3 14 | "proto=tcp" # use TCP transport 15 | "intr" # allow signals (Ctrl-C) to interrupt 16 | "timeo=30" # initial timeout = 3 s (30 deciseconds) 17 | "retrans=3" # retry only 3 times (~9 s total) 18 | "_netdev" # wait for network before mounting 19 | ]; 20 | }; 21 | 22 | fileSystems."/mnt/tubearchivist/cache" = { 23 | device = "192.168.5.6:/mnt/tank/app-data/tube-archivist/cache"; 24 | fsType = "nfs"; 25 | options = [ 26 | "defaults" # → rw,suid,dev,exec,auto,nouser,async 27 | "vers=3" # force NFSv3 28 | "proto=tcp" # use TCP transport 29 | "intr" # allow signals (Ctrl-C) to interrupt 30 | "timeo=30" # initial timeout = 3 s (30 deciseconds) 31 | "retrans=3" # retry only 3 times (~9 s total) 32 | "_netdev" # wait for network before mounting 33 | ]; 34 | }; 35 | 36 | virtualisation.oci-containers.containers = { 37 | tubearchivist = { 38 | imageFile = pkgs.dockerTools.pullImage { 39 | imageName = "bbilly1/tubearchivist"; 40 | imageDigest = "sha256:589dbfcd7ba36e608294cc586b80820a6651eaa80cc22eba871aa9980cdc85fd"; 41 | sha256 = "sha256-kREVFRdayfeSgVaxaBKBRmUTXdkFA2ddI06MTBlaChY="; 42 | }; 43 | image = "bbilly1/tubearchivist:v0.4.5"; 44 | 45 | ports = [ "8000:8000" ]; 46 | 47 | volumes = [ 48 | "${media}:/youtube" 49 | "${cache}:/cache" 50 | ]; 51 | 52 | environment = { 53 | ES_URL = "http://archivist-es:9200"; 54 | REDIS_HOST = "archivist-redis"; 55 | TA_HOST = "tubearchivist.service.home.arpa localhost"; 56 | TA_USERNAME = "solomon"; 57 | TA_PASSWORD = "hunter2"; 58 | ELASTIC_PASSWORD = "hunter2"; 59 | TZ = "America/Los_Angeles"; 60 | }; 61 | 62 | dependsOn = [ "archivist-es" "archivist-redis" ]; 63 | 64 | extraOptions = [ 65 | "--network=tubearchivist-br" 66 | ]; 67 | autoStart = true; 68 | }; 69 | 70 | archivist-es = { 71 | imageFile = pkgs.dockerTools.pullImage { 72 | imageName = "bbilly1/tubearchivist-es"; 73 | imageDigest = "sha256:152ed8f62d80725ef0d2338765b3b5d8c87f5e6f9b70d864980059616eb4a2ff"; 74 | sha256 = "sha256-XpnbnUl75If/gXqSGznhTZTUaBZF4PoQimt6qIcNUxI="; 75 | }; 76 | image = "bbilly1/tubearchivist-es:8.13.2"; 77 | 78 | environment = { 79 | ELASTIC_PASSWORD = "hunter2"; 80 | ES_JAVA_OPTS = "-Xms512m -Xmx512m"; 81 | "xpack.security.enabled" = "true"; 82 | "discovery.type" = "single-node"; 83 | "path.repo" = "/usr/share/elasticsearch/data/snapshot"; 84 | }; 85 | 86 | ports = [ "9200:9200" ]; 87 | 88 | volumes = [ 89 | "es:/usr/share/elasticsearch/data" 90 | ]; 91 | 92 | extraOptions = [ 93 | "--network=tubearchivist-br" 94 | ]; 95 | 96 | autoStart = true; 97 | }; 98 | 99 | archivist-redis = { 100 | imageFile = pkgs.dockerTools.pullImage { 101 | imageName = "redis/redis-stack-server"; 102 | imageDigest = "sha256:7df84d4e2f0e1d3d5d85f6ee96f1a42effe851527a72170933f8822341f83b74"; 103 | sha256 = "sha256-yeT9lhX1ArzVUyRG50a01PJn8Kifv7HPORzEf7DtT5c="; 104 | }; 105 | image = "redis/redis-stack-server:latest"; 106 | 107 | ports = [ "6379:6379" ]; 108 | 109 | volumes = [ 110 | "es:/usr/share/elasticsearch/data" 111 | ]; 112 | 113 | #dependsOn = [ "archivist-es" ]; 114 | 115 | extraOptions = [ 116 | "--network=tubearchivist-br" 117 | ]; 118 | 119 | autoStart = true; 120 | }; 121 | 122 | }; 123 | 124 | systemd.services.init-tubearchivist-bridge = { 125 | description = "Create the network bridge for tubearchivist."; 126 | after = [ "network.target" ]; 127 | wantedBy = [ "multi-user.target" ]; 128 | serviceConfig.Type = "oneshot"; 129 | script = 130 | let dockercli = "${config.virtualisation.docker.package}/bin/docker"; 131 | in '' 132 | # Put a true at the end to prevent getting non-zero return code, which will 133 | # crash the whole service. 134 | check=$(${dockercli} network ls | grep "tubearchivist-br" || true) 135 | if [ -z "$check" ]; then 136 | ${dockercli} network create tubearchivist-br 137 | else 138 | echo "tubearchivist-br already exists in docker" 139 | fi 140 | ''; 141 | }; 142 | 143 | services.nginx.virtualHosts."tubearchivist.service.home.arpa" = { 144 | locations."/".proxyPass = "http://localhost:8000"; 145 | }; 146 | 147 | services.nginx.virtualHosts."tubearchivist.local.home.arpa" = { 148 | locations."/".proxyPass = "http://localhost:8000"; 149 | }; 150 | } 151 | -------------------------------------------------------------------------------- /PROVISIONING.md: -------------------------------------------------------------------------------- 1 | # VM Provisioning Guide for TrueNAS/bhyve 2 | 3 | This guide walks you through provisioning a new virtual machine in your NixOS homelab setup using TrueNAS with bhyve virtualization. 4 | 5 | ## Prerequisites 6 | 7 | 1. **TrueNAS VM running** with bhyve virtualization 8 | 2. **Development environment** with this flake repo 9 | 3. **Pass password store** set up with existing secrets 10 | 4. **SSH access** to your development machine from TrueNAS 11 | 12 | ## Step 1: Create VM in TrueNAS/bhyve 13 | 14 | 1. **Create VM in TrueNAS interface:** 15 | - Guest OS: Linux 16 | - Memory: 2GB+ (adjust based on services) 17 | - CPU: 2+ cores 18 | - Storage: Create virtual disk (20GB+ recommended) 19 | - Network: Bridge to your home network 20 | 21 | 2. **Boot from NixOS ISO:** 22 | ```bash 23 | # Build the custom installer ISO with your SSH keys 24 | nix build '.#nixos-iso' 25 | # Mount the ISO to the VM and boot 26 | ``` 27 | 28 | ## Step 2: Prepare Machine Configuration 29 | 30 | 1. **Choose a name for your new server** (following the poetic naming convention): 31 | ```bash 32 | NEW_MACHINE="your-server-name" # e.g., "whisper-in-twilight" 33 | ``` 34 | 35 | 2. **Create machine directory:** 36 | ```bash 37 | mkdir -p "config/machines/servers/${NEW_MACHINE}" 38 | ``` 39 | 40 | 3. **Create basic default.nix:** 41 | ```bash 42 | cat > "config/machines/servers/${NEW_MACHINE}/default.nix" << EOF 43 | { pkgs, ... }: 44 | 45 | { 46 | imports = [ 47 | ./hardware.nix 48 | 49 | ../../../profiles/virtual-machine 50 | ]; 51 | 52 | networking.hostName = "${NEW_MACHINE}"; 53 | 54 | # Add service-specific imports here 55 | # ./some-service.nix 56 | } 57 | EOF 58 | ``` 59 | 60 | 4. **Choose disk configuration** - copy one of these templates: 61 | 62 | **For simple LVM setup (recommended for most VMs):** 63 | ```bash 64 | cp config/machines/servers/test-vm/disk-config.nix "config/machines/servers/${NEW_MACHINE}/" 65 | # Edit device path if needed (bhyve typically uses /dev/vda) 66 | ``` 67 | 68 | **For ZFS setup (if you need snapshots/compression):** 69 | ```bash 70 | cp config/machines/servers/gnostic-ascension/disk-config.nix "config/machines/servers/${NEW_MACHINE}/" 71 | ``` 72 | 73 | ## Step 3: Get VM IP Address 74 | 75 | 1. **Find the VM's IP** after it boots from ISO: 76 | ```bash 77 | # Check TrueNAS VM console or your router's DHCP leases 78 | VM_IP="192.168.x.x" # Replace with actual IP 79 | ``` 80 | 81 | 2. **Test SSH access:** 82 | ```bash 83 | ssh "root@${VM_IP}" 84 | # Should work due to SSH keys in the ISO 85 | ``` 86 | 87 | ## Step 4: Run Automated Provisioning 88 | 89 | 1. **Run the install script:** 90 | ```bash 91 | nix run '.#install-server' 92 | # Select your new machine from the list 93 | # Enter the VM IP when prompted 94 | ``` 95 | 96 | **The script will automatically:** 97 | - Generate random hostId for ZFS 98 | - Generate hardware.nix from the VM 99 | - Generate NEW SSH host keys for the machine 100 | - Store the SSH keys in your pass password store 101 | - Deploy NixOS with your configuration 102 | 103 | ## Step 5: Configure Secrets 104 | 105 | 1. **Generate age key for SOPS:** 106 | ```bash 107 | pass machine/${NEW_MACHINE}/ssh-host-key/ed25519/public | ssh-to-age 108 | # Copy the output key 109 | ``` 110 | 111 | 2. **Add to .sops.yaml:** 112 | ```bash 113 | vim .sops.yaml 114 | # Add the age key under creation_rules 115 | ``` 116 | 117 | 3. **Update secrets:** 118 | ```bash 119 | sops updatekeys --yes secrets.yaml 120 | ``` 121 | 122 | ## Step 6: Deploy and Configure 123 | 124 | 1. **Detach ISO and reboot VM** in TrueNAS interface 125 | 126 | 2. **Wait for VM to boot**, then deploy your configuration: 127 | ```bash 128 | colmena apply --on ${NEW_MACHINE} 129 | ``` 130 | 131 | 3. **Add services** by editing the machine's default.nix: 132 | ```nix 133 | imports = [ 134 | ./hardware.nix 135 | ./your-service.nix # Add service configs 136 | ../../../profiles/virtual-machine 137 | ]; 138 | ``` 139 | 140 | ## Available Disk Configuration Templates 141 | 142 | ### Simple LVM (test-vm template) 143 | - **Best for**: Most general-purpose VMs 144 | - **Storage**: Single ext4 filesystem on LVM 145 | - **Device**: `/dev/sda` (update to `/dev/vda` for bhyve) 146 | - **Partitions**: 1MB boot + 500MB ESP + 100% LVM root 147 | 148 | ### Advanced ZFS (gnostic-ascension template) 149 | - **Best for**: VMs needing snapshots, compression, or data integrity 150 | - **Storage**: ZFS pool with separate datasets 151 | - **Device**: `/dev/vda` (ideal for bhyve) 152 | - **Features**: 153 | - Automatic snapshots 154 | - zstd compression 155 | - Separate datasets for root, nix, home, logs 156 | - 2GB reservation to prevent pool filling 157 | 158 | ## Troubleshooting 159 | 160 | **If VM doesn't boot:** 161 | - Check bhyve logs in TrueNAS 162 | - Verify disk configuration matches VM setup 163 | - Ensure VM has sufficient resources 164 | 165 | **If SSH fails:** 166 | - Check firewall settings in TrueNAS 167 | - Verify SSH keys are in the custom ISO 168 | - Try console access through TrueNAS interface 169 | 170 | **If deployment fails:** 171 | - Check `colmena apply --on ${NEW_MACHINE} --verbose` 172 | - Verify secrets are properly configured 173 | - Check DNS resolution for the hostname 174 | 175 | **Common Issues:** 176 | - **Device path mismatch**: bhyve VMs typically use `/dev/vda`, update disk-config.nix accordingly 177 | - **Network connectivity**: Ensure VM network is bridged to your home network 178 | - **Resource constraints**: VMs need adequate RAM and CPU for the services they'll run 179 | 180 | ## Post-Deployment 181 | 182 | After successful deployment, your VM will be: 183 | - ✅ Accessible via SSH as user `solomon` 184 | - ✅ Monitored via Prometheus (port 9002) 185 | - ✅ Connected to Tailscale VPN 186 | - ✅ Ready for service-specific configuration 187 | 188 | Remember to update your monitoring and backup configurations to include the new VM. -------------------------------------------------------------------------------- /config/modules/ui/eww/scripts/weather_info: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ## Collect data 4 | cache_dir="$HOME/.cache/eww/weather" 5 | cache_weather_stat=${cache_dir}/weather-stat 6 | cache_weather_degree=${cache_dir}/weather-degree 7 | cache_weather_quote=${cache_dir}/weather-quote 8 | cache_weather_hex=${cache_dir}/weather-hex 9 | cache_weather_icon=${cache_dir}/weather-icon 10 | 11 | ## Weather data 12 | KEY=cat /secrets/primary-user-openweathermap-api-key 13 | ID=5368361 14 | UNIT=metric # Available options : 'metric' or 'imperial' 15 | 16 | ## Make cache dir 17 | if [[ ! -d "$cache_dir" ]]; then 18 | mkdir -p "${cache_dir}" 19 | fi 20 | 21 | ## Get data 22 | get_weather_data() { 23 | weather=$(curl -sf "http://api.openweathermap.org/data/2.5/weather?APPID=$KEY&id=$ID&units=$UNIT") 24 | echo "${weather}" 25 | 26 | if [ -n "$weather" ]; then 27 | weather_temp=$(echo "$weather" | jq ".main.temp" | cut -d "." -f 1) 28 | weather_icon_code=$(echo "$weather" | jq -r ".weather[].icon" | head -1) 29 | weather_description=$(echo "$weather" | jq -r ".weather[].description" | head -1 | sed -e "s/\b\(.\)/\u\1/g") 30 | 31 | #Big long if statement of doom 32 | if [ "$weather_icon_code" == "50d" ]; then 33 | weather_icon="󰖑 " 34 | weather_quote="Forecast says it's misty \nMake sure you don't get lost on your way..." 35 | weather_hex="#84afdb" 36 | elif [ "$weather_icon_code" == "50n" ]; then 37 | weather_icon=" " 38 | weather_quote="Forecast says it's a misty night \nDon't go anywhere tonight or you might get lost..." 39 | weather_hex="#84afdb" 40 | elif [ "$weather_icon_code" == "01d" ]; then 41 | weather_icon="󰖙 " 42 | weather_quote="It's a sunny day, gonna be fun! \nDon't go wandering all by yourself though..." 43 | weather_hex="#ffd86b" 44 | elif [ "$weather_icon_code" == "01n" ]; then 45 | weather_icon=" " 46 | weather_quote="It's a clear night \nYou might want to take a evening stroll to relax..." 47 | weather_hex="#fcdcf6" 48 | elif [ "$weather_icon_code" == "02d" ]; then 49 | weather_icon=" " 50 | weather_quote="It's cloudy, sort of gloomy \nYou'd better get a book to read..." 51 | weather_hex="#adadff" 52 | elif [ "$weather_icon_code" == "02n" ]; then 53 | weather_icon=" " 54 | weather_quote="It's a cloudy night \nHow about some hot chocolate and a warm bed?" 55 | weather_hex="#adadff" 56 | elif [ "$weather_icon_code" == "03d" ]; then 57 | weather_icon=" " 58 | weather_quote="It's cloudy, sort of gloomy \nYou'd better get a book to read..." 59 | weather_hex="#adadff" 60 | elif [ "$weather_icon_code" == "03n" ]; then 61 | weather_icon=" " 62 | weather_quote="It's a cloudy night \nHow about some hot chocolate and a warm bed?" 63 | weather_hex="#adadff" 64 | elif [ "$weather_icon_code" == "04d" ]; then 65 | weather_icon=" " 66 | weather_quote="It's cloudy, sort of gloomy \nYou'd better get a book to read..." 67 | weather_hex="#adadff" 68 | elif [ "$weather_icon_code" == "04n" ]; then 69 | weather_icon=" " 70 | weather_quote="It's a cloudy night \nHow about some hot chocolate and a warm bed?" 71 | weather_hex="#adadff" 72 | elif [ "$weather_icon_code" == "09d" ]; then 73 | weather_icon="󰖗 " 74 | weather_quote="It's rainy, it's a great day! \nGet some ramen and watch as the rain falls..." 75 | weather_hex="#6b95ff" 76 | elif [ "$weather_icon_code" == "09n" ]; then 77 | weather_icon=" " 78 | weather_quote=" It's gonna rain tonight it seems \nMake sure your clothes aren't still outside..." 79 | weather_hex="#6b95ff" 80 | elif [ "$weather_icon_code" == "10d" ]; then 81 | weather_icon="󰖗 " 82 | weather_quote="It's rainy, it's a great day! \nGet some ramen and watch as the rain falls..." 83 | weather_hex="#6b95ff" 84 | elif [ "$weather_icon_code" == "10n" ]; then 85 | weather_icon=" " 86 | weather_quote=" It's gonna rain tonight it seems \nMake sure your clothes aren't still outside..." 87 | weather_hex="#6b95ff" 88 | elif [ "$weather_icon_code" == "11d" ]; then 89 | weather_icon="" 90 | weather_quote="There's storm for forecast today \nMake sure you don't get blown away..." 91 | weather_hex="#ffeb57" 92 | elif [ "$weather_icon_code" == "11n" ]; then 93 | weather_icon="" 94 | weather_quote="There's gonna be storms tonight \nMake sure you're warm in bed and the windows are shut..." 95 | weather_hex="#ffeb57" 96 | elif [ "$weather_icon_code" == "13d" ]; then 97 | weather_icon=" " 98 | weather_quote="It's gonna snow today \nYou'd better wear thick clothes and make a snowman as well!" 99 | weather_hex="#e3e6fc" 100 | elif [ "$weather_icon_code" == "13n" ]; then 101 | weather_icon=" " 102 | weather_quote="It's gonna snow tonight \nMake sure you get up early tomorrow to see the sights..." 103 | weather_hex="#e3e6fc" 104 | elif [ "$weather_icon_code" == "40d" ]; then 105 | weather_icon=" " 106 | weather_quote="Forecast says it's misty \nMake sure you don't get lost on your way..." 107 | weather_hex="#84afdb" 108 | elif [ "$weather_icon_code" == "40n" ]; then 109 | weather_icon=" " 110 | weather_quote="Forecast says it's a misty night \nDon't go anywhere tonight or you might get lost..." 111 | weather_hex="#84afdb" 112 | else 113 | weather_icon=" " 114 | weather_quote="Sort of odd, I don't know what to forecast \nMake sure you have a good time!" 115 | weather_hex="#adadff" 116 | fi 117 | echo "$weather_icon" > "${cache_weather_icon}" 118 | echo "$weather_description" > "${cache_weather_stat}" 119 | echo "$weather_temp""°C" > "${cache_weather_degree}" 120 | echo -e "$weather_quote" > "${cache_weather_quote}" 121 | echo "$weather_hex" > "${cache_weather_hex}" 122 | else 123 | echo "Weather Unavailable" > "${cache_weather_stat}" 124 | echo " " > "${cache_weather_icon}" 125 | echo -e "Ah well, no weather huh? \nEven if there's no weather, it's gonna be a great day!" > "${cache_weather_quote}" 126 | echo "-" > "${cache_weather_degree}" 127 | echo "#adadff" > "${cache_weather_hex}" 128 | fi 129 | } 130 | 131 | ## Execute 132 | if [[ "$1" == "--getdata" ]]; then 133 | get_weather_data 134 | elif [[ "$1" == "--icon" ]]; then 135 | cat "${cache_weather_icon}" 136 | elif [[ "$1" == "--temp" ]]; then 137 | cat "${cache_weather_degree}" 138 | elif [[ "$1" == "--hex" ]]; then 139 | cat "${cache_weather_hex}" 140 | elif [[ "$1" == "--stat" ]]; then 141 | cat "${cache_weather_stat}" 142 | elif [[ "$1" == "--quote" ]]; then 143 | head -n1 "${cache_weather_quote}" 144 | elif [[ "$1" == "--quote2" ]]; then 145 | tail -n1 "${cache_weather_quote}" 146 | fi 147 | 148 | -------------------------------------------------------------------------------- /installer/install-server.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # https://github.com/numtide/nixos-anywhere/blob/main/docs/howtos.md#secrets-and-full-disk-encryption 4 | # https://gist.github.com/jkachmar/7305140b55ff1297703a2d2218964e54 5 | # https://github.com/jkachmar/termina/blob/main/hosts/tatl/disks.nix 6 | # https://nixos.wiki/wiki/Yubikey_based_Full_Disk_Encryption_(FDE)_on_NixOS#Partitioning 7 | 8 | set -e 9 | 10 | # Function to cleanup temporary directory on exit 11 | cleanup() { 12 | rm -rf "$temp" 13 | } 14 | 15 | main () { 16 | gum log --level info "Pick a machine:" 17 | MACHINE=$(ls config/machines/servers | gum choose) 18 | IP=$(gum input --placeholder "enter address..") 19 | 20 | # Test SSH connectivity 21 | gum log --level info "Testing SSH connectivity to ${IP}..." 22 | if ! ssh -o ConnectTimeout=10 -o BatchMode=yes "root@${IP}" true; then 23 | gum log --level error "Cannot connect to root@${IP}" 24 | gum log --level error "Please ensure:" 25 | gum log --level error " - The machine is booted from the NixOS ISO" 26 | gum log --level error " - SSH is running and accessible" 27 | gum log --level error " - Your SSH keys are authorized for root" 28 | exit 1 29 | fi 30 | gum log --level info "SSH connectivity confirmed" 31 | 32 | # Generate hardware config 33 | gum log --level info "Generating hardware configuration..." 34 | ssh "root@${IP}" nixos-generate-config --no-filesystems --show-hardware-config > "config/machines/servers/${MACHINE}/hardware.nix" 35 | 36 | # Validate hardware config was generated successfully 37 | if [ ! -f "config/machines/servers/${MACHINE}/hardware.nix" ]; then 38 | gum log --level error "CRITICAL: hardware.nix was not created" 39 | gum log --level error "SSH command may have failed" 40 | exit 1 41 | elif [ ! -s "config/machines/servers/${MACHINE}/hardware.nix" ]; then 42 | gum log --level error "CRITICAL: hardware.nix is empty" 43 | gum log --level error "Hardware detection may have failed on the target machine" 44 | exit 1 45 | fi 46 | gum log --level info "Hardware configuration generated successfully" 47 | 48 | # Add hardware config to git (required for Nix flakes) 49 | gum log --level info "Adding hardware configuration to git..." 50 | if ! git add "config/machines/servers/${MACHINE}/hardware.nix"; then 51 | gum log --level error "CRITICAL: Failed to add hardware.nix to git" 52 | gum log --level error "Nix flakes require all files to be git-tracked" 53 | gum log --level error "Without this, the installation will fail" 54 | gum log --level error "Please ensure you're in a clean git repository and try again" 55 | exit 1 56 | fi 57 | gum log --level info "Hardware configuration added to git successfully" 58 | 59 | # Generate default.nix if it doesn't exist 60 | if [ ! -f "config/machines/servers/${MACHINE}/default.nix" ]; then 61 | gum log --level info "Generating default.nix configuration from template..." 62 | sed "s/{{MACHINE_NAME}}/${MACHINE}/g" installer/templates/server-default.nix > "config/machines/servers/${MACHINE}/default.nix" 63 | 64 | # Add default.nix to git 65 | if ! git add "config/machines/servers/${MACHINE}/default.nix"; then 66 | gum log --level error "CRITICAL: Failed to add default.nix to git" 67 | exit 1 68 | fi 69 | gum log --level info "Default configuration generated and added to git successfully" 70 | else 71 | gum log --level info "default.nix already exists, skipping generation" 72 | fi 73 | 74 | # Generate disk-config.nix if it doesn't exist 75 | if [ ! -f "config/machines/servers/${MACHINE}/disk-config.nix" ]; then 76 | gum log --level info "Generating disk-config.nix configuration from template..." 77 | cp installer/templates/server-disk-config.nix "config/machines/servers/${MACHINE}/disk-config.nix" 78 | 79 | # Add disk-config.nix to git 80 | if ! git add "config/machines/servers/${MACHINE}/disk-config.nix"; then 81 | gum log --level error "CRITICAL: Failed to add disk-config.nix to git" 82 | exit 1 83 | fi 84 | gum log --level info "Disk configuration generated and added to git successfully" 85 | else 86 | gum log --level info "disk-config.nix already exists, skipping generation" 87 | fi 88 | 89 | # Create a temporary directory 90 | temp=$(mktemp -d) 91 | trap cleanup EXIT 92 | 93 | # Create the directory where sshd expects to find the host keys 94 | install -d -m755 "$temp/etc/ssh" 95 | 96 | # Check if SSH keys already exist in pass 97 | if pass show "machine/${MACHINE}/ssh-host-key/ed25519/private" >/dev/null 2>&1; then 98 | gum log --level info "SSH keys already exist for ${MACHINE}, using existing keys" 99 | else 100 | gum log --level info "Generating new ssh keys for ${MACHINE}" 101 | ssh-keygen -t ed25519 -C "solomon@${MACHINE}" -f "${temp}/etc/ssh/ssh_host_ed25519_key" -N "" 102 | ssh-keygen -t rsa -C "solomon@${MACHINE}" -f "${temp}/etc/ssh/ssh_host_rsa_key" -N "" 103 | 104 | # Store new keys in pass 105 | cat "${temp}/etc/ssh/ssh_host_ed25519_key" | pass insert --echo "machine/${MACHINE}/ssh-host-key/ed25519/private" 106 | cat "${temp}/etc/ssh/ssh_host_ed25519_key.pub" | pass insert --echo "machine/${MACHINE}/ssh-host-key/ed25519/public" 107 | cat "${temp}/etc/ssh/ssh_host_rsa_key" | pass insert --echo "machine/${MACHINE}/ssh-host-key/rsa/private" 108 | cat "${temp}/etc/ssh/ssh_host_rsa_key.pub" | pass insert --echo "machine/${MACHINE}/ssh-host-key/rsa/public" 109 | fi 110 | 111 | # Retrieve keys from pass for deployment (whether new or existing) 112 | gum log --level info "Retrieving SSH keys from pass for deployment" 113 | pass "machine/${MACHINE}/ssh-host-key/ed25519/private" > "$temp/etc/ssh/ssh_host_ed25519_key" 114 | pass "machine/${MACHINE}/ssh-host-key/ed25519/public" > "$temp/etc/ssh/ssh_host_ed25519_key.pub" 115 | pass "machine/${MACHINE}/ssh-host-key/rsa/private" > "$temp/etc/ssh/ssh_host_rsa_key" 116 | pass "machine/${MACHINE}/ssh-host-key/rsa/public" > "$temp/etc/ssh/ssh_host_rsa_key.pub" 117 | 118 | # Set the correct permissions so sshd will accept the keys 119 | chmod 600 "$temp/etc/ssh/ssh_host_ed25519_key" 120 | chmod 644 "$temp/etc/ssh/ssh_host_ed25519_key.pub" 121 | chmod 600 "$temp/etc/ssh/ssh_host_rsa_key" 122 | chmod 644 "$temp/etc/ssh/ssh_host_rsa_key.pub" 123 | 124 | # Install NixOS to the host system with our secrets 125 | nix run github:numtide/nixos-anywhere -- --extra-files "$temp" --flake ".#${MACHINE}" "root@${IP}" 126 | } 127 | 128 | main 129 | --------------------------------------------------------------------------------