231 Commits

Author SHA1 Message Date
jackos1998 805590a705 nixos/routing-common: Fix mstpd missing bridge-stp script
CI / Check, build and cache nixfiles (push) Successful in 46m37s
2026-07-12 18:40:38 +01:00
jackos1998 f8dbd99a7b nixos/routing-common: Add missing ttl param to DDNS update 2026-07-12 17:15:57 +01:00
jackos1998 90cc2d53f1 Update CI workflow to Ubuntu 26.04
CI / Check, build and cache nixfiles (push) Successful in 46m23s
2026-06-14 20:37:53 +01:00
jackos1998 b044504938 nixos/git: Update Gitea Actions runner
Bump runner labels to node 24 / Trixie and Ubuntu 26.04. The upstream
module now generates the runner config from the `settings` option and
wires `ExecStart` itself, so drop the hand-written config file and
`ExecStart` override.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 20:37:36 +01:00
jackos1998 98ccc23ef5 devshell: Add check-system and ssh-machine utilities
`check-system` evaluates a NixOS config without building it; `ssh-machine`
SSHs to a system or home by name, resolving the target and ssh options
from its `deploy-rs` node. Document both in `AGENTS.md`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 20:36:15 +01:00
jackos1998 d7e8ca52a0 Add AGENTS.md
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 19:31:59 +01:00
jackos1998 f1cc0fa25c nixos/estuary: Add Meta NL-ix peering
CI / Check, build and cache nixfiles (push) Has been cancelled
2026-06-14 03:15:28 +01:00
jackos1998 43828ad34c nixos/hass: Add West Wood integration
CI / Check, build and cache nixfiles (push) Has been cancelled
2026-06-14 03:08:33 +01:00
jackos1998 36d7e4a7e3 home-manager/gui: Add Claude status line 2026-06-14 03:08:00 +01:00
jackos1998 2f7d5d3908 nixos/castle: Add claude... 2026-06-14 00:24:05 +01:00
jackos1998 f3a0ca3f57 devshell: Allow deploy-multi without options
CI / Check, build and cache nixfiles (push) Has been cancelled
2026-06-13 22:11:23 +01:00
jackos1998 023b3f6605 nixos/tmproot: Add manpage cache 2026-06-13 21:53:29 +01:00
jackos1998 ecd3a46591 home-manager/gui: Add easyeffects
CI / Check, build and cache nixfiles (push) Failing after 1h52m48s
2026-06-13 20:42:12 +01:00
jackos1998 6bcca599e2 devshell: Fix json2nix warning 2026-06-13 20:36:46 +01:00
jackos1998 93529c578b "Release" 26.06 Irritating
CI / Check, build and cache nixfiles (push) Successful in 2h20m51s
2026-06-13 16:00:18 +01:00
jackos1998 bb32784962 nixos/jackflix: Update external port
CI / Check, build and cache nixfiles (push) Successful in 1h11m55s
2026-06-06 19:48:02 +01:00
jackos1998 6ffa35c414 nixos/chatterbox: Bump mautrix bridges
CI / Check, build and cache nixfiles (push) Successful in 1h14m35s
2026-06-01 20:19:51 +01:00
jackos1998 3cc5448410 nixos/user: Add to group disk
CI / Check, build and cache nixfiles (push) Successful in 1h4m36s
2026-04-18 21:50:36 +01:00
jackos1998 ee4178ee2d nixos/chatterbox: Update mautrix-whatsapp to 26.03
CI / Check, build and cache nixfiles (push) Successful in 1h7m26s
2026-04-12 18:36:06 +01:00
jackos1998 2bf18319c9 nixos/routing-common: Fix keepalived link-local addresses
CI / Check, build and cache nixfiles (push) Successful in 1h17m1s
2026-03-16 15:12:46 +00:00
jackos1998 a394b9124a nixos/routing-common: Fix Cloudflare 2026-03-16 13:37:16 +00:00
jackos1998 5bc48d33a3 nixos: Add tcpdump on all machines 2026-03-16 13:33:08 +00:00
jackos1998 365ef5d49d Update nixpkgs for terraria-server
CI / Check, build and cache nixfiles (push) Successful in 1h3m49s
2026-03-10 21:27:14 +00:00
jackos1998 0206d52fa2 nixos/netboot: Remove pinned iPXE
CI / Check, build and cache nixfiles (push) Successful in 1h15m47s
2026-03-08 14:36:07 +00:00
jackos1998 5526e07e65 Update harmonia
CI / Check, build and cache nixfiles (push) Failing after 2h25m26s
2026-03-07 17:09:50 +00:00
jackos1998 dde682390f nixos/castle: Add lan-lo
CI / Check, build and cache nixfiles (push) Failing after 6m27s
2026-03-04 21:30:53 +00:00
jackos1998 4ec59a64ce nixos/home/routing-common: Add DHCP pool for untrusted LAN 2026-03-03 20:15:45 +00:00
jackos1998 c9c788e261 nixos/estuary: Add john-valorant
CI / Check, build and cache nixfiles (push) Failing after 6m15s
2026-03-01 22:57:03 +00:00
jackos1998 21c24216b4 nixos/whale2: Update Graeme difficulty
CI / Check, build and cache nixfiles (push) Failing after 6m28s
2026-02-17 22:47:25 +00:00
jackos1998 2ecd350fcc nixos/whale2: Update graeme whitelist 2026-02-17 21:54:02 +00:00
jackos1998 3aa873d52a nixos/whale2: Add graeme
CI / Check, build and cache nixfiles (push) Failing after 6m31s
2026-02-16 22:49:22 +00:00
jackos1998 0a8ad18de8 nixos/jackflix: Add ffmpeg
CI / Check, build and cache nixfiles (push) Failing after 6m16s
2026-02-15 19:56:54 +00:00
jackos1998 b6b94fea5a Update nixpkgs-mine for Terraria 1.4.5.5
CI / Check, build and cache nixfiles (push) Failing after 6m18s
2026-02-11 22:16:07 +00:00
jackos1998 de9762b272 shill: Add Terraria server
CI / Check, build and cache nixfiles (push) Failing after 7m4s
2026-02-09 00:06:13 +00:00
jackos1998 8d1f4d51d0 nixos/deploy-rs: Fix systemd-util shellcheck 2026-02-08 21:17:00 +00:00
jackos1998 f1dc04ec4b Update inputs
CI / Check, build and cache nixfiles (push) Failing after 6m18s
2026-01-20 15:13:27 +00:00
jackos1998 7951c777cb nixos/sfh: Disable unifi
CI / Check, build and cache nixfiles (push) Successful in 1h13m56s
2025-12-06 21:59:34 +00:00
jackos1998 b031840f81 nixos/object: Fix harmonia
CI / Check, build and cache nixfiles (push) Failing after 3h13m40s
2025-12-06 15:39:47 +00:00
jackos1998 45861bef08 "Release" 25.11 Hooray 2025-12-06 15:18:36 +00:00
jackos1998 4433395125 nixos/chatterbox: Restore old WhatsApp display name template
CI / Check, build and cache nixfiles (push) Failing after 19s
2025-12-04 10:06:06 +00:00
jackos1998 3306aaab5e nixos/chatterbox: Upgrade mautrix bridges to v25.11 2025-12-04 10:06:06 +00:00
jackos1998 4480302c65 nixos/estuary: Remove old Frys-IX subnet
CI / Check, build and cache nixfiles (push) Failing after 21s
2025-12-01 22:41:13 +00:00
jackos1998 be7fb5a243 home-manager/common: Fix overlays
CI / Check, build and cache nixfiles (push) Successful in 2h39m0s
2025-10-06 10:54:12 +01:00
jackos1998 25758bae08 nixos/middleman: SNAT IPv6 to assigned address
CI / Check, build and cache nixfiles (push) Successful in 2h38m2s
2025-10-01 11:10:41 +01:00
jackos1998 7bfe9ad697 nixos/estuary: Add hillcrest WireGuard
CI / Check, build and cache nixfiles (push) Successful in 2h27m17s
2025-09-27 02:24:35 +01:00
jackos1998 7db5e18974 nixos/jackflix: copyparty: Move /pub to / and put stuff at /priv
CI / Check, build and cache nixfiles (push) Successful in 2h25m8s
2025-09-09 14:34:54 +01:00
jackos1998 20b7da74bf nixos/jackflix: Remove unnecessary insecure packages exception
CI / Check, build and cache nixfiles (push) Successful in 2h28m59s
2025-09-08 23:29:51 +01:00
jackos1998 adaf8b6a83 nixos/jackflix: Add copyparty 2025-09-08 23:28:31 +01:00
jackos1998 1f145334f3 nixos/britway: Disable override_local_dns for headscale
CI / Check, build and cache nixfiles (push) Failing after 12m38s
2025-09-08 21:29:20 +01:00
jackos1998 abf9f1b465 home-manager/gui: Add ffmpeg
CI / Check, build and cache nixfiles (push) Successful in 2h30m16s
2025-09-06 21:56:19 +01:00
jackos1998 f0740741a4 nixos/home/routing-common: Fix mstpd shellcheck
CI / Check, build and cache nixfiles (push) Successful in 2h44m21s
2025-09-06 21:40:05 +01:00
jackos1998 0c0b66b8db nixos/jackflix: Add FlareSolverr 2025-09-06 19:49:12 +01:00
jackos1998 bdf3c04595 nixos/git: Add NAT rules 2025-09-06 19:35:33 +01:00
jackos1998 02795a6ee4 nixos/nvme: Specify Host NQN on command line
CI / Check, build and cache nixfiles (push) Failing after 2h24m47s
2025-09-06 18:02:18 +01:00
jackos1998 8fa4a7ee60 "Release" 25.09 Giving
CI / Check, build and cache nixfiles (push) Failing after 31m46s
2025-09-06 17:14:09 +01:00
jackos1998 773674d879 nixos/chatterbox: Add adzerq to Instagram bridge
CI / Check, build and cache nixfiles (push) Failing after 29m47s
2025-07-23 19:42:35 +01:00
jackos1998 12c5ca126d nixos/middleman: kinkcraft Bluemap
CI / Check, build and cache nixfiles (push) Failing after 30m2s
2025-06-07 23:28:20 +01:00
jackos1998 b38a2a07e2 nixos/estuary: Update FrysIX BGP config to new /23
CI / Check, build and cache nixfiles (push) Failing after 30m16s
2025-06-03 11:06:58 +01:00
jackos1998 0dc474887f Add kinkcraft Minecraft server
CI / Check, build and cache nixfiles (push) Failing after 30m20s
2025-05-29 20:51:56 +01:00
jackos1998 c8bd63ec3e nixos: Add nixlight static IP and WLED hass integration
CI / Check, build and cache nixfiles (push) Failing after 29m45s
2025-05-26 23:25:05 +01:00
jackos1998 d7522f3f97 nixos/whale2: Op kev in kevcraft
CI / Check, build and cache nixfiles (push) Failing after 30m31s
2025-04-24 22:04:45 +01:00
jackos1998 58c76f822f home-manager/gui: Use tmux kill-session in brainrot screensavers
CI / Check, build and cache nixfiles (push) Failing after 29m19s
2025-04-14 13:27:01 +01:00
jackos1998 31bcde23b8 nixos/gui: Enable udisks2
CI / Check, build and cache nixfiles (push) Failing after 5m13s
2025-04-07 23:18:29 +01:00
jackos1998 fc2fa0666e nixos/middleman: Increase worker_processes
CI / Check, build and cache nixfiles (push) Failing after 5m10s
2025-03-28 16:42:54 +00:00
jackos1998 854cc48479 home-manager/gui: Add Brainrot story mode screensaver
CI / Check, build and cache nixfiles (push) Failing after 29m6s
2025-03-28 11:01:46 +00:00
jackos1998 85a4b124e5 pkgs: Remove own terminaltexteffects
CI / Check, build and cache nixfiles (push) Failing after 5m7s
2025-03-27 11:58:36 +00:00
jackos1998 f322f3ebac home-manager/gui: Longer and looping brainrot screensavers
CI / Check, build and cache nixfiles (push) Failing after 29m7s
2025-03-25 10:56:08 +00:00
jackos1998 bc74fb4968 home-manager/gui: Add brainrot screensavers
CI / Check, build and cache nixfiles (push) Failing after 30m15s
2025-03-24 15:09:46 +00:00
jackos1998 584abd4991 nixos/home/hass: Add USB webcam
CI / Check, build and cache nixfiles (push) Successful in 1h12m19s
2025-03-15 01:43:44 +00:00
jackos1998 05074a1fd9 nixos/home/hass: Basic Reolink camera setup
CI / Check, build and cache nixfiles (push) Has been cancelled
2025-03-15 01:07:12 +00:00
jackos1998 69060dfbff nixos/home/routing-common: Add static lease for hass-panel 2025-03-14 22:53:36 +00:00
jackos1998 8e288a9e2a nixos/home/hass: Include scenes.yaml
CI / Check, build and cache nixfiles (push) Successful in 1h17m11s
2025-03-14 17:48:18 +00:00
jackos1998 bb03b6fa76 nixos/home/hass: Add HEOS
CI / Check, build and cache nixfiles (push) Successful in 1h0m24s
2025-03-12 01:55:46 +00:00
jackos1998 fd92cfae6e nixos/home/hass: Include scripts.yaml
CI / Check, build and cache nixfiles (push) Successful in 1h1m12s
2025-03-11 14:35:10 +00:00
jackos1998 25267d09a2 nixos/home/hass: Add androidtv_remote and alarmo
CI / Check, build and cache nixfiles (push) Successful in 59m46s
2025-03-11 02:12:16 +00:00
jackos1998 f02f538ab2 nixos/home/routing-common: Add media DHCP reservations 2025-03-10 22:33:48 +00:00
jackos1998 d319657680 nixos/netboot: Use older iPXE with patch
CI / Check, build and cache nixfiles (push) Successful in 1h3m31s
2025-03-10 22:23:08 +00:00
jackos1998 dff5a4e6d8 nixos/home/hass: Add Irish Rail integration
CI / Check, build and cache nixfiles (push) Successful in 1h9m21s
2025-03-10 14:04:22 +00:00
jackos1998 2a8ced0fec nixos/home/routing-common: Add DNS blocklist
CI / Check, build and cache nixfiles (push) Successful in 1h6m52s
2025-03-10 10:46:21 +00:00
jackos1998 36c7096120 nixos/home/hass: Home Assistant CLI and automation fix
CI / Check, build and cache nixfiles (push) Successful in 2h47m3s
2025-03-10 01:28:14 +00:00
jackos1998 adfcf2f848 nixos/home/hass: Initial Home Assistant setup
CI / Check, build and cache nixfiles (push) Has been cancelled
2025-03-09 22:59:59 +00:00
jackos1998 a3870a4293 nixos/home/sfh: Introduce hass container
CI / Check, build and cache nixfiles (push) Has been cancelled
2025-03-09 20:07:28 +00:00
jackos1998 8f4b61fc2b Update inputs 2025-03-09 20:00:35 +00:00
jackos1998 44e3a3011a nixos/stream: Disable octoprint for now
CI / Check, build and cache nixfiles (push) Failing after 3m16s
2025-03-02 14:21:31 +00:00
jackos1998 45c972cca9 lib: Update public IPs 2025-03-02 13:40:22 +00:00
jackos1998 7bd5b8cbdf nixos/whale2: Add kevcraft
CI / Check, build and cache nixfiles (push) Failing after 2m33s
2025-02-18 17:15:03 +00:00
jackos1998 d1eb9cc981 nixos/toot: Add BlueSky PDS
CI / Check, build and cache nixfiles (push) Failing after 3m4s
2025-01-31 14:54:40 +00:00
jackos1998 7a2ebf6872 nixos: Add ADB stuff
CI / Check, build and cache nixfiles (push) Successful in 1h3m46s
2025-01-26 18:33:04 +00:00
jackos1998 72b8bd089c nixos/uk: Add WireGuard VPN for access
CI / Check, build and cache nixfiles (push) Successful in 1h15m33s
2025-01-22 19:19:03 +00:00
jackos1998 cff229f487 nixos: Add britway
CI / Check, build and cache nixfiles (push) Successful in 1h3m58s
2025-01-19 23:58:51 +00:00
jackos1998 f3ac3cd67f nixos/middleman: Add pubkey and HTTP access to p.nul.ie
CI / Check, build and cache nixfiles (push) Successful in 51m34s
2025-01-16 15:20:57 +00:00
jackos1998 820bb2de5b lib: River IP update
CI / Check, build and cache nixfiles (push) Successful in 1h4m34s
Installer / Build installer (push) Successful in 5m54s
2025-01-01 19:14:04 +00:00
jackos1998 7d3ad52a44 devshell: Add git config safe.directory for build-n-switch
CI / Check, build and cache nixfiles (push) Successful in 1h2m7s
2024-12-23 10:32:13 +00:00
jackos1998 2cdb98e898 nixos/common: Disable channels
CI / Check, build and cache nixfiles (push) Successful in 53m0s
2024-12-12 12:38:01 +00:00
jackos1998 b717b1ceb4 nixos/gui: Add /dev/player0 VID
CI / Check, build and cache nixfiles (push) Successful in 1h1m16s
2024-12-11 17:17:33 +00:00
jackos1998 f31ce61c2b Update borgthin
CI / Check, build and cache nixfiles (push) Successful in 2h31m34s
2024-11-30 19:31:58 +00:00
jackos1998 aec22942f7 Update latest Linux kernel to 6.12 2024-11-30 19:31:43 +00:00
jackos1998 fc8676c3bb devshell: Remove deprecated Nix command stuff 2024-11-30 19:19:23 +00:00
jackos1998 2915e42a1d ci: Group CI jobs
CI / Check, build and cache nixfiles (push) Failing after 33m39s
2024-11-30 18:05:22 +00:00
jackos1998 5783d3a51e Update nixpkgs-stable to 24.11 2024-11-30 17:45:59 +00:00
jackos1998 2fe94bba23 nixos/git: Add longer timeout for Gitea actions runner
CI / Check, build and cache Nix flake (push) Successful in 2h29m25s
2024-11-27 12:29:04 +00:00
jackos1998 4b42960d26 home-manager/gui: Update alacritty import setting to new section
CI / Check, build and cache Nix flake (push) Failing after 3h1m26s
2024-11-26 23:19:58 +00:00
jackos1998 56e9abf945 ci: Build and grab path for jobs in separate calls
CI / Check, build and cache Nix flake (push) Has been cancelled
The old build-n-parse seemed to output null sometimes.....
2024-11-26 22:45:19 +00:00
jackos1998 4e2c2f92f0 nixos/middleman: Remove config for Matrix sliding sync proxy
CI / Check, build and cache Nix flake (push) Failing after 6m29s
2024-11-26 22:15:53 +00:00
jackos1998 caa208b288 nixos/netboot: Use older version of iPXE for now
CI / Check, build and cache Nix flake (push) Failing after 6m33s
2024-11-26 22:01:42 +00:00
jackos1998 9e6f885c17 ci: Tweak log messages 2024-11-26 22:00:17 +00:00
jackos1998 d8ca87bfd8 pkgs: Remove glfw-wayland-minecraft
CI / Check, build and cache Nix flake (push) Failing after 6m15s
2024-11-26 21:23:50 +00:00
jackos1998 e9467e0cc7 ci: Build and cache CI jobs individually
CI / Check, build and cache Nix flake (push) Failing after 6m27s
2024-11-26 12:37:47 +00:00
jackos1998 6c98ef8944 Revert "nixos/home/routing-common: Move Tailscale to home routers"
CI / Check, build and cache Nix flake (push) Failing after 1h15m14s
This reverts commit 7c05b6158f.
2024-11-26 00:04:43 +00:00
jackos1998 18981e240b nixos/nvme: Update to libnvme v1.11.1 to fix LTS kernels 2024-11-25 23:58:15 +00:00
jackos1998 df7e5953eb Update nixpkgs-unstable (and other inputs)
CI / Check, build and cache Nix flake (push) Has been cancelled
2024-11-25 23:10:24 +00:00
jackos1998 71d1c3f9c2 Ensure borgbackup cache / config is persisted
CI / Check, build and cache Nix flake (push) Successful in 32m28s
2024-11-16 18:46:09 +00:00
jackos1998 1453a755c3 Add (now unused) Enshrouded server 2024-11-16 18:08:10 +00:00
jackos1998 970af805e9 home-manager/gui: Swap swaysome container binds
CI / Check, build and cache Nix flake (push) Successful in 23m18s
2024-11-08 11:47:35 +00:00
jackos1998 383e9a9b1e home-manager/gui: Add swaysome
CI / Check, build and cache Nix flake (push) Successful in 23m43s
2024-11-07 18:21:09 +00:00
jackos1998 26a16d0629 home-manager/gui: Disable ligatures in kitty
CI / Check, build and cache Nix flake (push) Successful in 29m30s
2024-11-06 13:16:56 +00:00
jackos1998 208de7654e home-manager/gui: Use rofi-wayland 2024-11-06 13:16:51 +00:00
jackos1998 f577e7d58a nixos/routing-common: Increase bandwidth for CAKE
CI / Check, build and cache Nix flake (push) Successful in 28m44s
2024-10-31 22:27:55 +00:00
jackos1998 6130ee73be nixos/tower: Add brightness keybinds
CI / Check, build and cache Nix flake (push) Successful in 29m59s
2024-10-26 20:24:27 +01:00
jackos1998 5d827aa00c home-manager/gui: Add xdg-utils to home.packages 2024-10-26 18:32:24 +01:00
jackos1998 173ffc0044 home-manager/gui: Add "Activate Linux" watermark
CI / Check, build and cache Nix flake (push) Successful in 30m5s
2024-10-16 11:32:14 +01:00
jackos1998 b113f2f48d home-manager/gui: Add Git LFS
CI / Check, build and cache Nix flake (push) Successful in 28m47s
2024-09-04 21:53:26 +01:00
jackos1998 7c67eaff21 nixos/colony: Add qclk management container
CI / Check, build and cache Nix flake (push) Successful in 34m42s
2024-09-01 19:22:03 +01:00
jackos1998 d1f1b84e82 Use fork of ragenix 2024-09-01 14:03:27 +01:00
jackos1998 e3cb2adbb6 nixos/castle: Add recursive-nix feature 2024-09-01 14:03:05 +01:00
jackos1998 736c406eb5 Update nixpkgs-mine for mautrix-whatsapp 0.10.9
CI / Check, build and cache Nix flake (push) Successful in 27m54s
2024-08-26 11:59:28 +01:00
jackos1998 8e9b750ac8 nixos: Set up remote printing
CI / Check, build and cache Nix flake (push) Successful in 27m42s
2024-08-20 10:36:21 +01:00
jackos1998 51c5578840 nixos/stream: Add OctoPrint
CI / Check, build and cache Nix flake (push) Failing after 2m16s
2024-08-20 01:03:49 +01:00
jackos1998 e174af45f6 nixos/castle: Emulate ARM 2024-08-17 12:39:36 +01:00
jackos1998 198e7188bd home-manager/gui: Use upstream unstable nixpkgs' chromium
CI / Check, build and cache Nix flake (push) Successful in 27m18s
2024-08-13 12:15:33 +01:00
jackos1998 571f8f1504 home-manager/gui: Add xournalpp
CI / Check, build and cache Nix flake (push) Successful in 18m42s
2024-07-31 11:28:24 +01:00
jackos1998 64c3fe682c nixos/home/routing-common: Only run Tailscale on active router
CI / Check, build and cache Nix flake (push) Successful in 26m33s
2024-07-31 10:20:19 +01:00
jackos1998 7c05b6158f nixos/home/routing-common: Move Tailscale to home routers
CI / Check, build and cache Nix flake (push) Successful in 18m12s
2024-07-22 16:22:08 +01:00
jackos1998 c9ab90547f Fix installer workflow short rev
Installer / Build installer (push) Successful in 4m23s
CI / Check, build and cache Nix flake (push) Has been cancelled
2024-07-21 13:01:03 +01:00
jackos1998 63d929c8e8 nixos: Include mutable flake in every system
CI / Check, build and cache Nix flake (push) Successful in 17m41s
Installer / Build installer (push) Successful in 4m20s
2024-07-21 12:37:32 +01:00
jackos1998 bbb87a2d69 devshell: Add deploy-multi command
CI / Check, build and cache Nix flake (push) Successful in 17m26s
2024-07-21 00:33:16 +01:00
jackos1998 e5d5847b89 nixos/middleman: Disable zstd in nginx for now
CI / Check, build and cache Nix flake (push) Successful in 25m3s
2024-07-20 22:41:01 +01:00
jackos1998 9e7294e871 nixos/shill: Rename atticd mount to harmonia 2024-07-20 21:19:25 +01:00
jackos1998 69216c6b4c Use harmonia instead of attic for binary cache
CI / Check, build and cache Nix flake (push) Successful in 2h1m7s
2024-07-20 19:04:51 +01:00
jackos1998 1ea172e690 nixos/vaultwarden: Use non-privileged port
CI / Check, build and cache Nix flake (push) Successful in 18m41s
2024-07-19 18:06:14 +01:00
jackos1998 b7be45715e nixos/britway: Update headscale config
CI / Check, build and cache Nix flake (push) Failing after 14m11s
2024-07-15 22:58:49 +01:00
jackos1998 3522a7078b Re-update nixpkgs 2024-07-15 22:58:42 +01:00
jackos1998 b44f0e74e8 Disable modrinth-app 2024-07-15 00:25:05 +01:00
jackos1998 7c57f00b27 nixos/britway: Update Headscale
CI / Check, build and cache Nix flake (push) Failing after 2h14m36s
2024-07-14 22:04:03 +01:00
jackos1998 c9d36ec65b home-manager/gui: New cursor theme
CI / Check, build and cache Nix flake (push) Has been cancelled
2024-07-14 21:49:37 +01:00
jackos1998 d8f97b9316 Update inputs 2024-07-14 21:21:43 +01:00
jackos1998 d5bb2f6787 nixos/routing-common: Start / stop radvd only for IPv6
CI / Check, build and cache Nix flake (push) Successful in 16m56s
2024-07-11 00:16:33 +01:00
jackos1998 ced82fc002 Update river public IP
CI / Check, build and cache Nix flake (push) Successful in 17m47s
2024-07-10 20:26:38 +01:00
jackos1998 3535d2fd90 nixos/shill: Use MemoryMax instead of MemoryMin
CI / Check, build and cache Nix flake (push) Successful in 17m4s
2024-07-07 12:02:50 +01:00
jackos1998 4e207c3397 nixos/colony: Disable KSM for now 2024-07-07 11:57:45 +01:00
jackos1998 bc4e75a6a5 nixos/middleman: Fix Element config.json
CI / Check, build and cache Nix flake (push) Successful in 17m37s
2024-07-05 16:30:34 +01:00
jackos1998 2ae922f3e8 home-manager/gui: Adjust brightness for wallpapers
CI / Check, build and cache Nix flake (push) Successful in 17m51s
2024-07-04 18:07:41 +01:00
jackos1998 f263fdca3e nixos/castle: Add left monitor wallpaper
CI / Check, build and cache Nix flake (push) Successful in 18m6s
2024-07-02 22:22:10 +01:00
jackos1998 1232e9cb30 Update nixpkgs
CI / Check, build and cache Nix flake (push) Successful in 49m21s
2024-07-02 12:07:52 +01:00
jackos1998 fbb29162ca nixos/colony: Enable KSM 2024-07-01 14:43:07 +01:00
jackos1998 7ab57a12b7 Reduce core count for CI
CI / Check, build and cache Nix flake (push) Successful in 1h27m30s
Installer / Build installer (push) Successful in 4m44s
2024-06-30 15:02:21 +01:00
jackos1998 4e947d4b1e nixos/unifi: Set up UniFi controller
CI / Check, build and cache Nix flake (push) Failing after 40m53s
2024-06-30 12:21:21 +01:00
jackos1998 b68e82ae03 nixos: Move castle to home
CI / Check, build and cache Nix flake (push) Successful in 42m12s
2024-06-30 04:01:56 +01:00
jackos1998 91489551b9 nixos: Working castle NVMe-oF root 2024-06-30 03:59:46 +01:00
jackos1998 86c99c2cbb nixos/build: Add Intel NIC drivers and increased timeout 2024-06-30 03:38:48 +01:00
jackos1998 7e2dfc21c6 nixos/sfh: Working containers
CI / Check, build and cache Nix flake (push) Successful in 44m19s
2024-06-30 01:52:52 +01:00
jackos1998 9ac63220d5 nixos/installer: Add NFS client
CI / Check, build and cache Nix flake (push) Successful in 42m53s
Installer / Build installer (push) Successful in 5m15s
2024-06-30 00:07:12 +01:00
jackos1998 ffa5d19854 home-manager/common: Add nix-tree package
Installer / Build installer (push) Has been cancelled
CI / Check, build and cache Nix flake (push) Has been cancelled
2024-06-29 23:42:31 +01:00
jackos1998 19fb29213e nixos/netboot: Mount /boot via kernel command line 2024-06-29 23:41:01 +01:00
jackos1998 f9870abc9e Working "shill From Home" full network boot 2024-06-29 23:12:21 +01:00
jackos1998 84ca556c47 nixos: Initial netbooting installer
CI / Check, build and cache Nix flake (push) Successful in 34m36s
Installer / Build installer (push) Successful in 4m34s
2024-06-24 00:42:26 +01:00
jackos1998 9f2651e352 nixos/installer: Disable GUI stuff 2024-06-23 19:00:29 +01:00
jackos1998 bce876ec42 Add release token to installer workflow
CI / Check, build and cache Nix flake (push) Has been cancelled
Installer / Build installer (push) Successful in 6m0s
2024-06-22 16:24:30 +01:00
jackos1998 bc8adcecad Update river public IP 2024-06-22 15:26:48 +01:00
jackos1998 8878ce56c4 nixos/kelder: MTU fix + disable all local redirects
CI / Check, build and cache Nix flake (push) Successful in 33m1s
2024-06-21 21:42:17 +01:00
jackos1998 dd9439b7fa nixos/shill: Add jam container
CI / Check, build and cache Nix flake (push) Successful in 33m30s
2024-06-20 23:43:07 +01:00
jackos1998 bc9f266ef0 nixos/containers: Remove workaround for systemd-nspawn@ drop-in 2024-06-20 23:37:43 +01:00
jackos1998 1b083d298b nixos/castle: Re-add boardie
CI / Check, build and cache Nix flake (push) Successful in 32m55s
2024-06-18 23:29:48 +01:00
jackos1998 83ba26735e nixos/colony: Add extra disk to darts VM
CI / Check, build and cache Nix flake (push) Failing after 6m28s
2024-06-15 20:05:27 +01:00
jackos1998 50bd96ccdf nixos/chatterbox: Add ffmpeg to mautrix bridges
CI / Check, build and cache Nix flake (push) Successful in 32m8s
2024-06-10 13:27:02 +01:00
jackos1998 a133cfb189 nixos/installer: Use my nixpkgs
CI / Check, build and cache Nix flake (push) Successful in 33m39s
2024-06-09 01:49:18 +01:00
jackos1998 051e68254e nixos/chatterbox: Add Instagram bridge 2024-06-09 01:46:00 +01:00
jackos1998 3fa8ab43ef nixos/chatterbox: Add Messenger bridge 2024-06-08 23:08:27 +01:00
jackos1998 c6720f87c1 nixos/chatterbox: Add WhatsApp bridge 2024-06-08 22:16:02 +01:00
jackos1998 55ecdddadb home-manager/gui: Fix screensaver top output, add cowsay to fortune
CI / Check, build and cache Nix flake (push) Failing after 1m20s
2024-06-07 16:45:35 +01:00
jackos1998 9b5173a587 home-manager/gui: More screensavers and options
CI / Check, build and cache Nix flake (push) Failing after 1m5s
2024-06-07 15:27:20 +01:00
jackos1998 73f5a690bb home-manager/gui: Add cmatrix and TTE screensavers
CI / Check, build and cache Nix flake (push) Failing after 1m23s
2024-06-07 03:11:55 +01:00
jackos1998 54db751e23 pkgs: Add terminaltexteffects 2024-06-07 02:36:12 +01:00
jackos1998 85299b65dc home-manager/gui: Working randomised doomsaver 2024-06-07 02:26:27 +01:00
jackos1998 45bda5b588 pkgs/chocolate-doom2xx: Add -demoloopi flag
CI / Check, build and cache Nix flake (push) Successful in 32m58s
2024-06-06 14:39:09 +01:00
jackos1998 9114f5ce74 home-manager/gui: Add Doom lock screen
CI / Check, build and cache Nix flake (push) Successful in 33m7s
2024-06-06 02:30:22 +01:00
jackos1998 3925c1090e pkgs: Add window2layer and swaylock-plugin 2024-06-06 02:08:35 +01:00
jackos1998 e74538a1a9 pkgs: Add chocolate-doom2xx 2024-06-06 02:02:28 +01:00
jackos1998 b8ee21b6e8 nixos/home/routing-common: More Disney+ IPv6 workarounds 2024-06-05 20:21:48 +01:00
jackos1998 41fd54cfad nixos/whale2: Update to netavark backend
CI / Check, build and cache Nix flake (push) Successful in 31m25s
2024-06-03 18:29:36 +01:00
jackos1998 1df34e0515 lib: Update latest kernel
CI / Check, build and cache Nix flake (push) Successful in 33m0s
Installer / Build installer (push) Failing after 5m31s
2024-06-01 17:21:27 +01:00
jackos1998 ce0c194761 Update all inputs (JackOS 24.06 "Carbrain")
CI / Check, build and cache Nix flake (push) Successful in 32m36s
2024-06-01 14:30:35 +01:00
jackos1998 a5e51ddd6b nixos/home: Filter out Disney+ IPv6 DNS queries
CI / Check, build and cache Nix flake (push) Successful in 36m4s
2024-05-16 21:05:48 +01:00
jackos1998 746e0b9dc4 nixos/castle: Fix PipeWire latency config
CI / Check, build and cache Nix flake (push) Failing after 58s
2024-05-11 16:10:15 +01:00
jackos1998 77600a64fc nixos/gui: Add udev rules for FT
CI / Check, build and cache Nix flake (push) Successful in 34m43s
2024-05-11 00:46:13 +01:00
jackos1998 c6d5705097 nixos/jackflix: Add PhotoPrism
CI / Check, build and cache Nix flake (push) Successful in 34m59s
2024-05-06 00:57:52 +01:00
jackos1998 6eefe97764 lib: Update river public IP
CI / Check, build and cache Nix flake (push) Successful in 35m59s
2024-04-13 15:04:52 +01:00
jackos1998 4bc4fe3ee8 Update my nixpkgs (spdk 24.01) 2024-04-13 15:02:54 +01:00
jackos1998 57ec2bfc1b nixos/home/routing-common: Make keepalived ping more resilient
CI / Check, build and cache Nix flake (push) Successful in 33m37s
2024-04-05 15:22:10 +01:00
jackos1998 d9d1150feb Update nixpkgs and home-manager
CI / Check, build and cache Nix flake (push) Successful in 1h21m12s
2024-04-04 19:08:12 +01:00
jackos1998 92896d8e52 nixos/stream: Un-hardcode deploy host
CI / Check, build and cache Nix flake (push) Successful in 35m24s
2024-03-25 11:20:58 +00:00
jackos1998 477ffca33e nixos/common: Update registry to point to nixpkgs flake 2024-03-25 11:17:30 +00:00
jackos1998 fdc65c544e nixos/home/routing-common: Add ping test to keepalived
CI / Check, build and cache Nix flake (push) Successful in 35m51s
2024-03-24 13:32:03 +00:00
jackos1998 945302b7c0 lib: Update river IP 2024-03-24 12:45:43 +00:00
jackos1998 5ccf19cab8 nixos/colony: Fix LVM activatio dependency cycle
CI / Check, build and cache Nix flake (push) Successful in 35m28s
2024-03-23 13:25:32 +00:00
jackos1998 7b61dd7f03 nixos/colony: Enable PCIe AER 2024-03-23 12:45:59 +00:00
jackos1998 682865a0e1 nixos/l2mesh: Add option to enable UDP encapsulation 2024-03-23 12:14:26 +00:00
jackos1998 a0e4cf2479 lib: Bump JackOS version
CI / Check, build and cache Nix flake (push) Successful in 24m28s
2024-03-22 21:23:18 +00:00
jackos1998 a5880d66f4 home-manager/gui: Use python3Packages instead of python310Packages
CI / Check, build and cache Nix flake (push) Successful in 1h12m52s
2024-03-21 23:32:01 +00:00
jackos1998 27a4583879 pkgs/modrinth-app: Fix deps hash
CI / Check, build and cache Nix flake (push) Has been cancelled
2024-03-21 23:10:42 +00:00
jackos1998 fdbf5f8aca lib: Update river IP 2024-03-21 21:16:01 +00:00
jackos1998 40c491aa14 nixos/home/routing-common: Add MSS clamping to work around PMTUD
CI / Check, build and cache Nix flake (push) Failing after 1h4m53s
2024-03-21 20:42:06 +00:00
jackos1998 1a8740fb9c nixos/home/routing-common: Increase RTT for CAKE 2024-03-21 20:41:28 +00:00
jackos1998 f857e751b5 nixos/home/routing-common: Restart kea on failure 2024-03-21 20:40:38 +00:00
jackos1998 b420f2377c Use fork of sharry for now
CI / Check, build and cache Nix flake (push) Failing after 1h2m37s
2024-03-18 21:22:52 +00:00
jackos1998 7d90b5ecb8 Fix API changes from updates
CI / Check, build and cache Nix flake (push) Failing after 16m14s
2024-03-18 20:23:52 +00:00
jackos1998 ace979c226 Update inputs
CI / Check, build and cache Nix flake (push) Failing after 6m13s
2024-03-18 17:41:41 +00:00
jackos1998 f540edb361 nixos/routing-common: Clear IPv6 local default route
CI / Check, build and cache Nix flake (push) Successful in 25m16s
2024-03-13 21:52:09 +00:00
jackos1998 6bc5cd79da lib: Update river IP 2024-03-13 21:31:05 +00:00
jackos1998 5ec77dfde6 nixos/routing-common: Add DNS for Shytzel and wave
CI / Check, build and cache Nix flake (push) Successful in 25m32s
2024-03-13 21:00:20 +00:00
jackos1998 52623d458e nixos/simpcraft: Update to Simpcraft 0.2.1
CI / Check, build and cache Nix flake (push) Successful in 26m17s
2024-01-27 14:36:40 +00:00
jackos1998 23b29f0707 nixos/acquisition: Add transmission workaround
CI / Check, build and cache Nix flake (push) Successful in 26m39s
2024-01-21 23:27:27 +00:00
jackos1998 338902497f nixos/simpcraft: Increase memory allocation to 8GiB
CI / Check, build and cache Nix flake (push) Successful in 26m19s
2024-01-20 12:07:43 +00:00
jackos1998 977846991a nixos/simpcraft: Disable autosave during backup
CI / Check, build and cache Nix flake (push) Successful in 26m20s
2024-01-19 20:06:23 +00:00
jackos1998 0e8aec58fb nixos/simpcraft: Update at odd intervals
CI / Check, build and cache Nix flake (push) Successful in 25m52s
2024-01-19 00:46:55 +00:00
jackos1998 0f1de58917 nixos/simpcraft: Add backup
CI / Check, build and cache Nix flake (push) Successful in 25m52s
2024-01-18 12:12:06 +00:00
jackos1998 32183bd331 devshell/commands: Overwrite home symlink if needed
CI / Check, build and cache Nix flake (push) Successful in 26m29s
2024-01-15 14:59:01 +00:00
jackos1998 1813ca1927 nixos/simpcraft: Add missing environment file
CI / Check, build and cache Nix flake (push) Successful in 25m59s
2024-01-15 13:21:25 +00:00
jackos1998 51d44e472a pkgs: Add working Minecraft on Wayland GLFW
CI / Check, build and cache Nix flake (push) Failing after 12m18s
2024-01-12 14:00:50 +00:00
jackos1998 44e87aa387 Add wastebin
CI / Check, build and cache Nix flake (push) Successful in 27m3s
2024-01-10 15:21:40 +00:00
jackos1998 f90deabb50 nixos/whale2: Update Simpcraft to 0.2.0
CI / Check, build and cache Nix flake (push) Successful in 25m49s
2024-01-10 01:10:13 +00:00
181 changed files with 8492 additions and 3546 deletions
+26 -18
View File
@@ -6,11 +6,11 @@ on:
jobs: jobs:
check: check:
name: Check, build and cache Nix flake name: Check, build and cache nixfiles
runs-on: ubuntu-22.04 runs-on: ubuntu-26.04
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v6
- uses: cachix/install-nix-action@v23 - uses: cachix/install-nix-action@v31
with: with:
# Gitea will supply a token in GITHUB_TOKEN, which this action will # Gitea will supply a token in GITHUB_TOKEN, which this action will
# try to pass to Nix when downloading from GitHub # try to pass to Nix when downloading from GitHub
@@ -18,22 +18,30 @@ jobs:
extra_nix_config: | extra_nix_config: |
# Make sure we're using sandbox # Make sure we're using sandbox
sandbox-fallback = false sandbox-fallback = false
# Big C++ projects fill up memory...
cores = 6
extra-substituters = https://nix-cache.nul.ie/main extra-substituters = https://nix-cache.nul.ie
extra-trusted-public-keys = main:mMChkG8LwXrFirVfudqjSHasK1jV31OVElYD3eImYl8= extra-trusted-public-keys = nix-cache.nul.ie-1:BzH5yMfF4HbzY1C977XzOxoPhEc9Zbu39ftPkUbH+m4=
- name: Set up attic
run: |
nix run .#nixpkgs.mine.x86_64-linux.attic-client -- \
login --set-default colony https://nix-cache.nul.ie "${{ secrets.NIX_CACHE_TOKEN }}"
- name: Check flake - name: Check flake
run: nix flake check run: nix flake check --no-build
- name: Build the world
- name: Build (and cache) the world
id: build id: build
env:
HARMONIA_SSH_KEY: ${{ secrets.HARMONIA_SSH_KEY }}
run: | run: |
path=$(nix build --no-link .#ci.x86_64-linux --json | jq -r .[0].outputs.out) nix eval --json --apply "builtins.attrNames" .#ci.x86_64-linux | jq -cr '.[]' | while read job; do
echo "path=$path" >> "$GITHUB_OUTPUT" echo "::group::Build $job"
- name: Push to cache nix build --no-link .#ci.x86_64-linux."$job"
run: | echo "::endgroup::"
nix run .#nixpkgs.mine.x86_64-linux.attic-client -- \
push main ${{ steps.build.outputs.path }} echo "::group::Cache $job"
ci/push-to-cache.sh "$(nix eval --raw .#ci.x86_64-linux."$job")"
echo "::endgroup::"
done
echo "Building and caching CI derivation"
nix build --no-link .#ciDrv.x86_64-linux
UPDATE_PROFILE=1 ci/push-to-cache.sh "$(nix eval --raw .#ciDrv.x86_64-linux)"
+8 -9
View File
@@ -14,22 +14,20 @@ jobs:
uses: https://github.com/actions/setup-go@v4 uses: https://github.com/actions/setup-go@v4
with: with:
go-version: '>=1.20.1' go-version: '>=1.20.1'
- uses: cachix/install-nix-action@v23 - uses: cachix/install-nix-action@v27
with: with:
github_access_token: ${{ secrets.GH_PULL_TOKEN }} github_access_token: ${{ secrets.GH_PULL_TOKEN }}
extra_nix_config: | extra_nix_config: |
# Make sure we're using sandbox # Make sure we're using sandbox
sandbox-fallback = false sandbox-fallback = false
extra-substituters = https://nix-cache.nul.ie/main extra-substituters = https://nix-cache.nul.ie
extra-trusted-public-keys = main:mMChkG8LwXrFirVfudqjSHasK1jV31OVElYD3eImYl8= extra-trusted-public-keys = nix-cache.nul.ie-1:BzH5yMfF4HbzY1C977XzOxoPhEc9Zbu39ftPkUbH+m4=
- name: Set up attic
- name: Set up vars
id: setup id: setup
run: | run: |
nix run .#nixpkgs.mine.x86_64-linux.attic-client -- \
login --set-default colony https://nix-cache.nul.ie "${{ secrets.NIX_CACHE_TOKEN }}"
echo "short_rev=$(git rev-parse --short HEAD)" >> "$GITHUB_OUTPUT" echo "short_rev=$(git rev-parse --short HEAD)" >> "$GITHUB_OUTPUT"
- name: Build installer ISO - name: Build installer ISO
run: | run: |
nix build .#nixfiles.config.nixos.systems.installer.configuration.config.my.buildAs.iso nix build .#nixfiles.config.nixos.systems.installer.configuration.config.my.buildAs.iso
@@ -39,12 +37,13 @@ jobs:
run: | run: |
nix build .#nixfiles.config.nixos.systems.installer.configuration.config.my.buildAs.netbootArchive nix build .#nixfiles.config.nixos.systems.installer.configuration.config.my.buildAs.netbootArchive
ln -s "$(readlink result)" \ ln -s "$(readlink result)" \
jackos-installer-netboot-${{ steps.setup.outputs.short_rev }}.tar jackos-installer-netboot-${{ steps.setup.outputs.short_rev }}.tar.zst
- name: Create release - name: Create release
uses: https://gitea.com/actions/release-action@main uses: https://gitea.com/actions/release-action@main
with: with:
title: Latest installer title: Latest installer
api_key: '${{ secrets.RELEASE_TOKEN }}'
files: | files: |
jackos-installer-${{ steps.setup.outputs.short_rev }}.iso jackos-installer-${{ steps.setup.outputs.short_rev }}.iso
jackos-installer-netboot-${{ steps.setup.outputs.short_rev }}.tar jackos-installer-netboot-${{ steps.setup.outputs.short_rev }}.tar.zst
+1
View File
@@ -0,0 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKXRXkYnBf2opIjN+bXE7HmhUpa4hyXJUGmBT+MRccT4 harmonia
+113
View File
@@ -0,0 +1,113 @@
# AGENTS.md
This file provides guidance to coding agents when working with code in this repository.
## Overview
Personal Nix flake managing NixOS systems and home-manager configurations for a fleet of
machines (servers, home boxes, routers). It is built around a **custom module system** layered
on top of NixOS/home-manager, not the stock flake `nixosConfigurations` pattern.
## Commands
This repo provides a `numtide/devshell` (entered via `direnv` / `use flake`). The shell defines
named commands — prefer them over raw `nix` invocations. Run a bare command name with no args to
see its help, or browse `devshell/commands.nix` / `devshell/install.nix` / `devshell/vm-tasks.nix`.
Common ones:
- `fmt` — format Nix with `nixpkgs-fmt` (the canonical formatter here).
- `build-system <host> [nix args]` — build a NixOS system's `toplevel`.
- `build-n-switch <args>` — wraps `doas nixos-rebuild --flake .`.
- `build-home <name>` / `home-switch` — build / switch a home-manager config.
- `run-vm <host>` — build & boot a system as a dev VM (installs `.keys/dev.key` into the VM).
- `build-iso` / `build-kexec` / `build-netboot <host>` — alternate build outputs via `config.my.buildAs.*`.
- `check-system <host> [nix args]` — evaluate a system (catches eval errors without a full build).
**Prefer this over `build-system` to validate a config change** — evaluation surfaces module/option
errors quickly and cheaply; only do a full build when you specifically need the built artifact.
- `deploy <host>` and `deploy-multi <hosts...>` — deploy-rs deployment (uses `.keys/deploy.key`, `--skip-checks`).
Pass the flake-qualified node, e.g. `deploy .#git`. The deploy node name is **always** the system
name (`deploy-rs.nix` keys nodes directly off `nixos.systems` / `home-manager.homes`); a system is
only a deploy target when `config.my.deploy.enable` is true (defaults true; auto-disabled for dev
VMs and containers).
- `ssh-machine <name> [cmd]` — SSH to a NixOS system or home-manager config by name. Resolves the
target and ssh options (identity, port) from its deploy-rs node, so it needs `my.deploy.enable`
(same gate as `deploy`).
- `ragenix` — edit age secrets using `.keys/dev.key` as identity (see Secrets).
- `repl``nix repl .#`.
- `update-nixpkgs` / `update-home-manager` — bump pinned inputs.
Check everything (what CI runs): `nix flake check --no-build`.
CI builds each attr of `.#ci.x86_64-linux` (systems, homes, packages, shell) and pushes to the
Harmonia binary cache; see `.gitea/workflows/ci.yaml` and `ci/push-to-cache.sh`.
## Architecture
### The custom module system
`flake.nix` does **not** call `nixosSystem` per host directly. Instead it `evalModules` over
`./nixos`, `./home-manager`, `./deploy-rs.nix`, and the per-host files listed in the `configs`
list in `flake.nix`. That evaluation produces a top-level config (`self.nixfiles`) from which the
real flake outputs are derived:
- `nixos.systems.<name>``nixosConfigurations.<name>`
- `home-manager.homes.<name>``homeConfigurations.<name>`
- `nixos.modules` / `home-manager.modules``nixosModules` / `homeModules`
- `deploy-rs.rendered``deploy`
`nixos/default.nix` and `home-manager/default.nix` define the `systemOpts` / `homeOpts` submodules
and the `mkSystem` / `mkHome` functions that actually invoke `eval-config.nix` /
`homeManagerConfiguration`. **To add a new host:** create a box file that sets
`nixos.systems.<name> = { ... }`, then add its path to the `configs` list in `flake.nix`.
### Multiple nixpkgs channels
Four pkgs sets are threaded everywhere as `pkgsFlakes` / `pkgs'` (and `hmFlakes` for home-manager):
`unstable`, `stable`, `mine` (a personal nixpkgs fork), `mine-stable`. Each system/home picks its
channel via `nixpkgs` / `home-manager` / `hmNixpkgs` options (e.g. `nixpkgs = "mine-stable"`).
Modules receive `pkgs'` = an attrset of all channels for the current system.
### `lib.my` and the `my` option namespace
`lib/default.nix` extends `lib` with a `my` attrset (helpers like `mkOpt'`, `mkBoolOpt'`,
`mkDefault'`, `inlineModule'`, `mkDefaultSystemsPkgs`, `homeStateVersion`). It also pulls in:
- `lib.my.net` — network/CIDR helpers from the `libnetRepo` input. Used heavily for IP math.
- `lib.my.c` — shared constants from `lib/constants.nix` (UIDs/GIDs, kernel package selection,
nginx snippets, per-network domains/prefixes, etc.). Reuse these rather than hardcoding.
- `lib.my.dns` — DNS helpers (`lib/dns.nix`).
Custom modules add options under the `my.*` namespace (e.g. `my.secrets`, `my.build`,
`my.tmproot`, `my.server`). Use `mkOpt'`/`mkBoolOpt'` for option declarations to match style.
### Modules and module lists
Module sets are registered in `nixos/modules/_list.nix` and `home-manager/modules/_list.nix`
(name → path), which become `nixos.modules` / `home-manager.modules` and are applied to every
system/home. To add a shared module, drop the file in `nixos/modules/` (or `home-manager/modules/`)
and add an entry to the relevant `_list.nix`.
### Network assignments
Each system declares `assignments.<name>` (in its `nixos.systems.<host>` block) with IPv4/IPv6
addresses, gateways, domains, MTU, etc. These are aggregated into `allAssignments` (passed to every
module) and there is an assertion that fails on duplicate IPs. Host networking
(`networking.hostName`, `domain`) defaults from the `internal` assignment.
### Hosts / "boxes"
Per-host configs live under `nixos/boxes/<host>` (some are single `.nix` files, some directories
with nested VMs/containers under e.g. `colony/vms`). Many "systems" are VMs or containers managed
via the `vms` / `containers` modules and the `l2mesh` VXLAN module.
## Secrets
age-encrypted secrets in `secrets/`, managed with **ragenix**. Each module declares
`my.secrets.files.<name>` and `my.secrets.key` (the host pubkey to encrypt for). `secrets.nix`
(the ragenix rules file) is generated by reading every system's declared secrets and computing the
recipient key list (always including `.keys/dev.pub`). Edit secrets with the `ragenix` devshell
command, which supplies `.keys/dev.key` as the identity. The `.keys/` directory (dev + deploy
private keys) is required for editing secrets, deploying, and running dev VMs.
## Conventions
- Format with `nixpkgs-fmt` (`fmt`). 2-space indent, `inherit (...)` blocks at the top of `let`.
- Prefer `lib.my` helpers (`mkOpt'`, `mkBoolOpt'`, `mkDefault'`) and `lib.my.c` constants over
reimplementing.
- New shared functionality → a module in `*/modules/` + entry in `_list.nix`, options under `my.*`.
- New host → box file under `nixos/boxes/` + entry in the `configs` list in `flake.nix`.
- Custom packages live in `pkgs/` and are registered in `pkgs/default.nix`; the overlay is exposed
as `overlays.default`.
- In prose and commit messages, quote code-like identifiers (commands, options, paths, package and
attribute names) in backticks.
+1
View File
@@ -0,0 +1 @@
object-ctr.ams1.int.nul.ie ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFdHbZErWLmTPO/aEWB1Fup/aGMf31Un5Wk66FJwTz/8
+31
View File
@@ -0,0 +1,31 @@
#!/bin/sh
set -e
REMOTE_STORE=/var/lib/harmonia
SSH_HOST="harmonia@object-ctr.ams1.int.nul.ie"
SSH_KEY=/tmp/harmonia.key
STORE_URI="ssh-ng://$SSH_HOST?ssh-key=$SSH_KEY&remote-store=$REMOTE_STORE"
remote_cmd() {
ssh -i "$SSH_KEY" "$SSH_HOST" env HOME=/run/harmonia NIX_REMOTE="$REMOTE_STORE" "$@"
}
umask_old=$(umask)
umask 0066
echo "$HARMONIA_SSH_KEY" | base64 -d > "$SSH_KEY"
umask $umask_old
mkdir -p ~/.ssh
cp ci/known_hosts ~/.ssh/
path="$1"
echo "Pushing $path to cache..."
nix copy --no-check-sigs --to "$STORE_URI" "$path"
if [ -n "$UPDATE_PROFILE" ]; then
echo "Updating profile..."
remote_cmd nix-env -p "$REMOTE_STORE"/nix/var/nix/profiles/nixfiles --set "$path"
echo "Collecting garbage..."
remote_cmd nix-collect-garbage --delete-older-than 60d
fi
+51 -19
View File
@@ -20,7 +20,7 @@ in
[ -e "${homeFlake}" ] && echo "${homeFlake} already exists" && exit 1 [ -e "${homeFlake}" ] && echo "${homeFlake} already exists" && exit 1
mkdir -p "$(dirname "${homeFlake}")" mkdir -p "$(dirname "${homeFlake}")"
ln -s "$(pwd)/flake.nix" "${homeFlake}" ln -sf "$(pwd)/flake.nix" "${homeFlake}"
echo "Installed link to $(pwd)/flake.nix at ${homeFlake}" echo "Installed link to $(pwd)/flake.nix at ${homeFlake}"
''; '';
} }
@@ -48,11 +48,30 @@ in
help = "Print the ed25519 pubkey for a host"; help = "Print the ed25519 pubkey for a host";
command = "${pkgs.openssh}/bin/ssh-keyscan -t ed25519 \"$1\" 2> /dev/null | awk '{ print $2 \" \" $3 }'"; command = "${pkgs.openssh}/bin/ssh-keyscan -t ed25519 \"$1\" 2> /dev/null | awk '{ print $2 \" \" $3 }'";
} }
{
name = "ssh-machine";
category = "utilities";
help = "SSH to a machine by NixOS system or home-manager config name";
command = ''
name="$1"
shift
# Run from the project root so deploy's relative identity path (.keys/deploy.key) resolves
cd "$PRJ_ROOT"
# deploy-rs node names are the system/home name, with `@` mangled to `-at-`
node="''${name//@/-at-}"
# Single eval: resolve `user@host` plus merged (global + node) deploy-rs ssh options
info="$(nix eval --raw .#deploy --apply 'd: let n = d.nodes."'"$node"'"; in "''${n.sshUser}@''${n.hostname} ''${builtins.concatStringsSep " " (d.sshOpts ++ n.sshOpts)}"')"
target="''${info%% *}"
opts="''${info#* }"
# shellcheck disable=SC2086
exec ${pkgs.openssh}/bin/ssh $opts "$target" "$@"
'';
}
{ {
name = "json2nix"; name = "json2nix";
category = "utilities"; category = "utilities";
help = "Convert JSON to formatted Nix"; help = "Convert JSON to formatted Nix";
command = "nix eval --impure --expr 'builtins.fromJSON (builtins.readFile /dev/stdin)' | ${pkgs.nixfmt}/bin/nixfmt"; command = "nix eval --impure --expr 'builtins.fromJSON (builtins.readFile /dev/stdin)' | ${pkgs.nixfmt}/bin/nixfmt -";
} }
{ {
@@ -73,11 +92,22 @@ in
help = "Build NixOS configuration"; help = "Build NixOS configuration";
command = ''nix build "''${@:2}" ".#nixosConfigurations.\"$1\".config.system.build.toplevel"''; command = ''nix build "''${@:2}" ".#nixosConfigurations.\"$1\".config.system.build.toplevel"'';
} }
{
name = "check-system";
category = "utilities";
help = "Evaluate NixOS configuration (check validity without building)";
command = ''nix eval "''${@:2}" ".#nixosConfigurations.\"$1\".config.system.build.toplevel.drvPath"'';
}
{ {
name = "build-n-switch"; name = "build-n-switch";
category = "tasks"; category = "tasks";
help = "Shortcut to nixos-rebuild for this flake"; help = "Shortcut to nixos-rebuild for this flake";
command = ''doas nixos-rebuild --flake . "$@"''; command = ''
# HACK: Upstream changes in Git + Nix makes this necessary
# https://github.com/NixOS/nix/issues/10202
doas git config --global --add safe.directory "$PWD"
doas nixos-rebuild --flake . "$@"
'';
} }
{ {
name = "run-vm"; name = "run-vm";
@@ -106,8 +136,8 @@ in
{ {
name = "build-netboot"; name = "build-netboot";
category = "tasks"; category = "tasks";
help = "Build NixOS configuration as netboot archive"; help = "Build NixOS configuration as netboot tree";
command = ''nix build "''${@:2}" ".#nixfiles.config.nixos.systems.\"$1\".configuration.config.my.buildAs.netbootArchive"''; command = ''nix build "''${@:2}" ".#nixfiles.config.nixos.systems.\"$1\".configuration.config.my.buildAs.netbootTree"'';
} }
{ {
name = "build-home"; name = "build-home";
@@ -115,29 +145,17 @@ in
help = "Build home-manager configuration"; help = "Build home-manager configuration";
command = ''nix build "''${@:2}" ".#homeConfigurations.\"$1\".activationPackage"''; command = ''nix build "''${@:2}" ".#homeConfigurations.\"$1\".activationPackage"'';
} }
{
name = "update-inputs";
category = "tasks";
help = "Update flake inputs";
command = ''
args=()
for f in "$@"; do
args+=(--update-input "$f")
done
nix flake lock "''${args[@]}"
'';
}
{ {
name = "update-nixpkgs"; name = "update-nixpkgs";
category = "tasks"; category = "tasks";
help = "Update nixpkgs flake inputs"; help = "Update nixpkgs flake inputs";
command = ''update-inputs nixpkgs-{unstable,stable,mine,mine-stable}''; command = ''nix flake update nixpkgs-{unstable,stable,mine,mine-stable}'';
} }
{ {
name = "update-home-manager"; name = "update-home-manager";
category = "tasks"; category = "tasks";
help = "Update home-manager flake inputs"; help = "Update home-manager flake inputs";
command = ''update-inputs home-manager-{unstable,stable}''; command = ''nix flake update home-manager-{unstable,stable}'';
} }
{ {
name = "update-installer"; name = "update-installer";
@@ -145,5 +163,19 @@ in
help = "Update installer tag (to trigger new release)"; help = "Update installer tag (to trigger new release)";
command = ''git tag -f installer && git push -f origin installer''; command = ''git tag -f installer && git push -f origin installer'';
} }
{
name = "deploy-multi";
category = "tasks";
help = "Deploy multiple flakes at once";
command = ''
for f in $@; do
if [ -n "''${O:-}" ]; then
deploy "$O" $f
else
deploy $f
fi
done
'';
}
]; ];
} }
+2 -2
View File
@@ -11,7 +11,7 @@ in
NIX_USER_CONF_FILES = toString (pkgs.writeText "nix.conf" NIX_USER_CONF_FILES = toString (pkgs.writeText "nix.conf"
'' ''
experimental-features = nix-command flakes ca-derivations repl-flake experimental-features = nix-command flakes ca-derivations
connect-timeout = 5 connect-timeout = 5
fallback = true fallback = true
${lib.my.c.nix.cache.conf} ${lib.my.c.nix.cache.conf}
@@ -24,10 +24,10 @@ in
coreutils coreutils
nixVersions.stable nixVersions.stable
rage rage
wireguard-tools
(pkgs.writeShellScriptBin "deploy" '' (pkgs.writeShellScriptBin "deploy" ''
exec ${deploy-rs.deploy-rs}/bin/deploy --skip-checks "$@" exec ${deploy-rs.deploy-rs}/bin/deploy --skip-checks "$@"
'') '')
home-manager home-manager
attic-client
]; ];
} }
Generated
+579 -322
View File
File diff suppressed because it is too large Load Diff
+40 -15
View File
@@ -3,40 +3,52 @@
inputs = { inputs = {
flake-utils.url = "github:numtide/flake-utils"; flake-utils.url = "github:numtide/flake-utils";
# libnet.url = "github:reo101/nix-lib-net";
libnetRepo = {
url = "github:oddlama/nixos-extra-modules";
flake = false;
};
devshell.url = "github:numtide/devshell"; devshell.url = "github:numtide/devshell";
devshell.inputs.nixpkgs.follows = "nixpkgs-unstable"; devshell.inputs.nixpkgs.follows = "nixpkgs-unstable";
nixpkgs-unstable.url = "nixpkgs/nixos-unstable"; nixpkgs-unstable.url = "nixpkgs/nixos-unstable";
nixpkgs-stable.url = "nixpkgs/nixos-23.11"; nixpkgs-stable.url = "nixpkgs/nixos-26.05";
nixpkgs-mine.url = "github:devplayer0/nixpkgs/devplayer0"; nixpkgs-mine.url = "github:devplayer0/nixpkgs/devplayer0";
nixpkgs-mine-stable.url = "github:devplayer0/nixpkgs/devplayer0-stable"; nixpkgs-mine-stable.url = "github:devplayer0/nixpkgs/devplayer0-stable";
home-manager-unstable.url = "home-manager"; home-manager-unstable.url = "home-manager";
home-manager-unstable.inputs.nixpkgs.follows = "nixpkgs-unstable"; home-manager-unstable.inputs.nixpkgs.follows = "nixpkgs-unstable";
home-manager-stable.url = "home-manager/release-23.11"; home-manager-stable.url = "home-manager/release-26.05";
home-manager-stable.inputs.nixpkgs.follows = "nixpkgs-stable"; home-manager-stable.inputs.nixpkgs.follows = "nixpkgs-stable";
# Stuff used by the flake for build / deployment # Stuff used by the flake for build / deployment
ragenix.url = "github:yaxitech/ragenix"; # ragenix.url = "github:yaxitech/ragenix";
ragenix.url = "github:devplayer0/ragenix/add-rekey-one-flag";
ragenix.inputs.nixpkgs.follows = "nixpkgs-unstable"; ragenix.inputs.nixpkgs.follows = "nixpkgs-unstable";
deploy-rs.url = "github:serokell/deploy-rs"; deploy-rs.url = "github:serokell/deploy-rs";
deploy-rs.inputs.nixpkgs.follows = "nixpkgs-unstable"; deploy-rs.inputs.nixpkgs.follows = "nixpkgs-unstable";
# Stuff used by systems # Stuff used by systems
impermanence.url = "github:nix-community/impermanence"; impermanence.url = "github:nix-community/impermanence";
impermanence.inputs.home-manager.follows = "home-manager-unstable";
boardie.url = "github:devplayer0/boardie"; boardie.url = "github:devplayer0/boardie";
boardie.inputs.nixpkgs.follows = "nixpkgs-unstable"; boardie.inputs.nixpkgs.follows = "nixpkgs-unstable";
nixGL.url = "github:nix-community/nixGL"; nixGL.url = "github:nix-community/nixGL";
nixGL.inputs.nixpkgs.follows = "nixpkgs-unstable"; nixGL.inputs.nixpkgs.follows = "nixpkgs-unstable";
harmonia.url = "github:nix-community/harmonia";
# harmonia.url = "github:devplayer0/harmonia/cache-config-daemon-store";
harmonia.inputs.nixpkgs.follows = "nixpkgs-unstable";
# Packages not in nixpkgs # Packages not in nixpkgs
sharry.url = "github:eikek/sharry"; sharry.url = "github:eikek/sharry";
sharry.inputs.nixpkgs.follows = "nixpkgs-unstable"; sharry.inputs.nixpkgs.follows = "nixpkgs-unstable";
borgthin.url = "github:devplayer0/borg"; borgthin.url = "github:devplayer0/borg";
borgthin.inputs.nixpkgs.follows = "nixpkgs-mine"; # TODO: Update borgthin so this works
attic.url = "github:zhaofengli/attic"; # borgthin.inputs.nixpkgs.follows = "nixpkgs-mine";
attic.inputs.nixpkgs.follows = "nixpkgs-unstable"; copyparty.url = "github:9001/copyparty";
attic.inputs.nixpkgs-stable.follows = "nixpkgs-stable"; copyparty.inputs.nixpkgs.follows = "nixpkgs-unstable";
hass-west-wood.url = "github:devplayer0/hass-west-wood";
hass-west-wood.inputs.nixpkgs.follows = "nixpkgs-unstable";
}; };
outputs = outputs =
@@ -51,7 +63,7 @@
... ...
}: }:
let let
inherit (builtins) mapAttrs replaceStrings; inherit (builtins) mapAttrs replaceStrings elem;
inherit (lib) mapAttrs' filterAttrs nameValuePair recurseIntoAttrs evalModules; inherit (lib) mapAttrs' filterAttrs nameValuePair recurseIntoAttrs evalModules;
inherit (lib.flake) flattenTree eachDefaultSystem; inherit (lib.flake) flattenTree eachDefaultSystem;
inherit (lib.my) mkDefaultSystemsPkgs flakePackageOverlay; inherit (lib.my) mkDefaultSystemsPkgs flakePackageOverlay;
@@ -59,7 +71,7 @@
# Extend a lib with extras that _must not_ internally reference private nixpkgs. flake-utils doesn't, but many # Extend a lib with extras that _must not_ internally reference private nixpkgs. flake-utils doesn't, but many
# other flakes (e.g. home-manager) probably do internally. # other flakes (e.g. home-manager) probably do internally.
libOverlay = final: prev: { libOverlay = final: prev: {
my = import ./lib { lib = final; }; my = import ./lib { inherit inputs; lib = final; };
flake = flake-utils.lib; flake = flake-utils.lib;
}; };
pkgsLibOverlay = final: prev: { lib = prev.lib.extend libOverlay; }; pkgsLibOverlay = final: prev: { lib = prev.lib.extend libOverlay; };
@@ -90,12 +102,12 @@
(_: path: mkDefaultSystemsPkgs path (system: { (_: path: mkDefaultSystemsPkgs path (system: {
overlays = [ overlays = [
pkgsLibOverlay pkgsLibOverlay
myPkgsOverlay myPkgsOverlay
inputs.devshell.overlays.default inputs.devshell.overlays.default
inputs.ragenix.overlays.default inputs.ragenix.overlays.default
inputs.deploy-rs.overlay inputs.deploy-rs.overlays.default
(flakePackageOverlay inputs.home-manager-unstable system) (flakePackageOverlay inputs.home-manager-unstable system)
inputs.attic.overlays.default
]; ];
})) }))
pkgsFlakes; pkgsFlakes;
@@ -105,8 +117,19 @@
(_: path: mkDefaultSystemsPkgs path (_: { (_: path: mkDefaultSystemsPkgs path (_: {
overlays = [ overlays = [
pkgsLibOverlay pkgsLibOverlay
myPkgsOverlay myPkgsOverlay
]; ];
config = {
# RMS forgive me...
# Normally this is set modularly, but sometimes we need to use other pkgs
allowUnfreePredicate = p: elem (lib.getName p) [
"widevine-cdm"
"chromium-unwrapped"
"chromium"
];
};
})) }))
pkgsFlakes; pkgsFlakes;
@@ -115,10 +138,11 @@
nixos/installer.nix nixos/installer.nix
nixos/boxes/colony nixos/boxes/colony
nixos/boxes/tower nixos/boxes/tower
nixos/boxes/castle
nixos/boxes/home/stream.nix nixos/boxes/home/stream.nix
nixos/boxes/home/palace nixos/boxes/home/palace
nixos/boxes/home/castle
nixos/boxes/britway nixos/boxes/britway
nixos/boxes/britnet.nix
nixos/boxes/kelder nixos/boxes/kelder
# Homes # Homes
@@ -149,7 +173,7 @@
# Platform independent stuff # Platform independent stuff
{ {
nixpkgs = pkgs'; nixpkgs = pkgs';
inherit lib nixfiles; inherit inputs lib nixfiles;
overlays.default = myPkgsOverlay; overlays.default = myPkgsOverlay;
@@ -197,8 +221,9 @@
systems' = mapAttrs' (n: v: nameValuePair "system-${n}" v) systems; systems' = mapAttrs' (n: v: nameValuePair "system-${n}" v) systems;
packages' = mapAttrs' (n: v: nameValuePair "package-${n}" v) packages; packages' = mapAttrs' (n: v: nameValuePair "package-${n}" v) packages;
in in
pkgs.linkFarm "ci" (homes' // systems' // packages' // { homes' // systems' // packages' // {
inherit shell; inherit shell;
}); };
ciDrv = pkgs.linkFarm "ci" ci;
})); }));
} }
+26 -23
View File
@@ -66,7 +66,7 @@ in
lsd = { lsd = {
enable = mkDefault true; enable = mkDefault true;
enableAliases = mkDefault true; enableFishIntegration = mkDefault true;
}; };
starship = { starship = {
@@ -132,38 +132,38 @@ in
ssh = { ssh = {
enable = mkDefault true; enable = mkDefault true;
matchBlocks = { enableDefaultConfig = false;
settings = {
nix-dev-vm = { nix-dev-vm = {
user = "dev"; User = "dev";
hostname = "localhost"; HostName = "localhost";
port = 2222; Port = 2222;
extraOptions = {
StrictHostKeyChecking = "no"; StrictHostKeyChecking = "no";
UserKnownHostsFile = "/dev/null"; UserKnownHostsFile = "/dev/null";
}; };
};
"rsync.net" = { "rsync.net" = {
host = "rsyncnet"; Host = "rsyncnet";
user = "16413"; User = "16413";
hostname = "ch-s010.rsync"; HostName = "ch-s010.rsync";
}; };
shoe = { shoe = {
host = "shoe.netsoc.tcd.ie shoe"; Host = "shoe.netsoc.tcd.ie shoe";
user = "netsoc"; User = "netsoc";
}; };
netsocBoxes = { netsocBoxes = {
host = "cube spoon napalm gandalf saruman"; Host = "cube spoon napalm gandalf saruman";
user = "root"; User = "root";
};
"*" = {
IdentityFile = [
"~/.ssh/id_rsa"
"~/.ssh/borg"
];
}; };
}; };
extraConfig =
''
IdentityFile ~/.ssh/id_rsa
IdentityFile ~/.ssh/netsoc
IdentityFile ~/.ssh/borg
'';
}; };
direnv = { direnv = {
@@ -199,17 +199,20 @@ in
file file
tree tree
pwgen pwgen
minicom
iperf3 iperf3
mosh mosh
wget wget
hyx
whois whois
ldns ldns
minicom
mtr mtr
hyx
ncdu ncdu
jq jq
yq-go yq-go
nix-tree
]; ];
sessionVariables = { sessionVariables = {
@@ -223,7 +226,7 @@ in
# Note: If globalPkgs mode is on, then these will be overridden by the NixOS equivalents of these options # Note: If globalPkgs mode is on, then these will be overridden by the NixOS equivalents of these options
nixpkgs = { nixpkgs = {
overlays = [ overlays = [
inputs.deploy-rs.overlay inputs.deploy-rs.overlays.default
inputs.boardie.overlays.default inputs.boardie.overlays.default
inputs.nixGL.overlays.default inputs.nixGL.overlays.default
]; ];
@@ -0,0 +1,28 @@
# XTerm's default colors
# Default colors
[colors.primary]
background = '#000000'
foreground = '#ffffff'
# Normal colors
[colors.normal]
black = '#000000'
red = '#cd0000'
green = '#00cd00'
yellow = '#cdcd00'
blue = '#0000ee'
magenta = '#cd00cd'
cyan = '#00cdcd'
white = '#e5e5e5'
# Bright colors
[colors.bright]
black = '#7f7f7f'
red = '#ff0000'
green = '#00ff00'
yellow = '#ffff00'
blue = '#5c5cff'
magenta = '#ff00ff'
cyan = '#00ffff'
white = '#ffffff'
+520
View File
@@ -0,0 +1,520 @@
#!/usr/bin/env bash
# Source: https://github.com/daniel3303/ClaudeCodeStatusLine
# Single line: Model | tokens | %used | %remain | think | 5h bar @reset | 7d bar @reset | extra
set -f # disable globbing
VERSION="1.4.4"
input=$(cat)
if [ -z "$input" ]; then
printf "Claude"
exit 0
fi
# ANSI colors matching oh-my-posh theme
blue='\033[38;2;0;153;255m'
orange='\033[38;2;255;176;85m'
green='\033[38;2;0;160;0m'
cyan='\033[38;2;46;149;153m'
red='\033[38;2;255;85;85m'
yellow='\033[38;2;230;200;0m'
purple='\033[38;2;167;139;250m'
white='\033[38;2;220;220;220m'
dim='\033[2m'
reset='\033[0m'
# Format token counts (e.g., 50k / 200k)
format_tokens() {
local num=$1
if [ "$num" -ge 1000000 ]; then
awk "BEGIN {v=sprintf(\"%.1f\",$num/1000000)+0; if(v==int(v)) printf \"%dm\",v; else printf \"%.1fm\",v}"
elif [ "$num" -ge 1000 ]; then
awk "BEGIN {printf \"%.0fk\", $num / 1000}"
else
printf "%d" "$num"
fi
}
# Format number with commas (e.g., 134,938)
format_commas() {
printf "%'d" "$1"
}
# Return color escape based on usage percentage
# Usage: usage_color <pct>
usage_color() {
local pct=$1
if [ "$pct" -ge 90 ]; then echo "$red"
elif [ "$pct" -ge 70 ]; then echo "$orange"
elif [ "$pct" -ge 50 ]; then echo "$yellow"
else echo "$green"
fi
}
# Resolve config directory: CLAUDE_CONFIG_DIR (set by alias) or default ~/.claude
claude_config_dir="${CLAUDE_CONFIG_DIR:-$HOME/.claude}"
# Return 0 (true) if $1 > $2 using semantic versioning
version_gt() {
local a="${1#v}" b="${2#v}"
local IFS='.'
read -r a1 a2 a3 <<< "$a"
read -r b1 b2 b3 <<< "$b"
a1=${a1:-0}; a2=${a2:-0}; a3=${a3:-0}
b1=${b1:-0}; b2=${b2:-0}; b3=${b3:-0}
[ "$a1" -gt "$b1" ] 2>/dev/null && return 0
[ "$a1" -lt "$b1" ] 2>/dev/null && return 1
[ "$a2" -gt "$b2" ] 2>/dev/null && return 0
[ "$a2" -lt "$b2" ] 2>/dev/null && return 1
[ "$a3" -gt "$b3" ] 2>/dev/null && return 0
return 1
}
# ===== Extract data from JSON =====
model_name=$(echo "$input" | jq -r '.model.display_name // "Claude"')
model_name=$(echo "$model_name" | sed 's/ *(\([0-9.]*[kKmM]*\) context)/ \1/') # "(1M context)" → "1M"
# Context window
size=$(echo "$input" | jq -r '.context_window.context_window_size // 200000')
[ "$size" -eq 0 ] 2>/dev/null && size=200000
# Token usage
input_tokens=$(echo "$input" | jq -r '.context_window.current_usage.input_tokens // 0')
cache_create=$(echo "$input" | jq -r '.context_window.current_usage.cache_creation_input_tokens // 0')
cache_read=$(echo "$input" | jq -r '.context_window.current_usage.cache_read_input_tokens // 0')
current=$(( input_tokens + cache_create + cache_read ))
used_tokens=$(format_tokens $current)
total_tokens=$(format_tokens $size)
if [ "$size" -gt 0 ]; then
pct_used=$(( current * 100 / size ))
else
pct_used=0
fi
pct_remain=$(( 100 - pct_used ))
used_comma=$(format_commas $current)
remain_comma=$(format_commas $(( size - current )))
settings_path="$claude_config_dir/settings.json"
effort_level=""
stdin_effort=$(echo "$input" | jq -r '.effort.level // empty' 2>/dev/null)
if [ -n "$stdin_effort" ]; then
effort_level="$stdin_effort"
elif [ -n "$CLAUDE_CODE_EFFORT_LEVEL" ]; then
effort_level="$CLAUDE_CODE_EFFORT_LEVEL"
elif [ -f "$settings_path" ]; then
effort_val=$(jq -r '.effortLevel // empty' "$settings_path" 2>/dev/null)
[ -n "$effort_val" ] && effort_level="$effort_val"
fi
[ -z "$effort_level" ] && effort_level="medium"
# ===== Claude CLI version (cached, 1h TTL) =====
cli_version_cache="/tmp/claude/statusline-cli-version"
cli_version=""
cli_version_max_age=3600
if [ -f "$cli_version_cache" ]; then
cv_mtime=$(stat -c %Y "$cli_version_cache" 2>/dev/null || stat -f %m "$cli_version_cache" 2>/dev/null)
cv_now=$(date +%s)
cv_age=$(( cv_now - cv_mtime ))
if [ "$cv_age" -lt "$cli_version_max_age" ]; then
cli_version=$(cat "$cli_version_cache" 2>/dev/null)
fi
fi
if [ -z "$cli_version" ]; then
cli_version=$(claude --version 2>/dev/null | awk '{print $1}')
if [ -n "$cli_version" ]; then
mkdir -p /tmp/claude 2>/dev/null
echo "$cli_version" > "$cli_version_cache"
fi
fi
# ===== Build single-line output =====
out=""
out+="${blue}${model_name}${reset}"
# Current working directory
cwd=$(echo "$input" | jq -r '.cwd // empty')
if [ -n "$cwd" ]; then
display_dir="${cwd##*/}"
git_branch=$(git -C "${cwd}" rev-parse --abbrev-ref HEAD 2>/dev/null)
out+=" ${dim}|${reset} "
out+="${cyan}${display_dir}${reset}"
if [ -n "$git_branch" ]; then
out+="${dim}@${reset}${green}${git_branch}${reset}"
git_stat=$(git -C "${cwd}" diff --numstat 2>/dev/null | awk '{a+=$1; d+=$2} END {if (a+d>0) printf "+%d -%d", a, d}')
[ -n "$git_stat" ] && out+=" ${dim}(${reset}${green}${git_stat%% *}${reset} ${red}${git_stat##* }${reset}${dim})${reset}"
fi
fi
out+=" ${dim}|${reset} "
out+="${orange}${used_tokens}/${total_tokens}${reset} ${dim}(${reset}${green}${pct_used}%${reset}${dim})${reset}"
out+=" ${dim}|${reset} "
out+="effort: "
case "$effort_level" in
low) out+="${dim}${effort_level}${reset}" ;;
medium) out+="${orange}med${reset}" ;;
high) out+="${green}${effort_level}${reset}" ;;
xhigh) out+="${purple}${effort_level}${reset}" ;;
max) out+="${red}${effort_level}${reset}" ;;
*) out+="${green}${effort_level}${reset}" ;;
esac
# ===== Cross-platform OAuth token resolution (from statusline.sh) =====
# Tries credential sources in order: env var → macOS Keychain → Linux creds file → GNOME Keyring
get_oauth_token() {
local token=""
# 1. Explicit env var override
if [ -n "$CLAUDE_CODE_OAUTH_TOKEN" ]; then
echo "$CLAUDE_CODE_OAUTH_TOKEN"
return 0
fi
# 2. macOS Keychain (Claude Code appends a SHA256 hash of CLAUDE_CONFIG_DIR to the service name)
if command -v security >/dev/null 2>&1; then
local keychain_svc="Claude Code-credentials"
if [ -n "$CLAUDE_CONFIG_DIR" ]; then
local dir_hash
dir_hash=$(echo -n "$CLAUDE_CONFIG_DIR" | shasum -a 256 | cut -c1-8)
keychain_svc="Claude Code-credentials-${dir_hash}"
fi
local blob
blob=$(security find-generic-password -s "$keychain_svc" -w 2>/dev/null)
if [ -n "$blob" ]; then
token=$(echo "$blob" | jq -r '.claudeAiOauth.accessToken // empty' 2>/dev/null)
if [ -n "$token" ] && [ "$token" != "null" ]; then
echo "$token"
return 0
fi
fi
fi
# 3. Linux credentials file
local creds_file="${claude_config_dir}/.credentials.json"
if [ -f "$creds_file" ]; then
token=$(jq -r '.claudeAiOauth.accessToken // empty' "$creds_file" 2>/dev/null)
if [ -n "$token" ] && [ "$token" != "null" ]; then
echo "$token"
return 0
fi
fi
# 4. GNOME Keyring via secret-tool
if command -v secret-tool >/dev/null 2>&1; then
local blob
blob=$(timeout 2 secret-tool lookup service "Claude Code-credentials" 2>/dev/null)
if [ -n "$blob" ]; then
token=$(echo "$blob" | jq -r '.claudeAiOauth.accessToken // empty' 2>/dev/null)
if [ -n "$token" ] && [ "$token" != "null" ]; then
echo "$token"
return 0
fi
fi
fi
echo ""
}
# ===== LINE 2 & 3: Usage limits with progress bars =====
# First, try to use rate_limits data provided directly by Claude Code in the JSON input.
# This is the most reliable source — no OAuth token or API call required.
builtin_five_hour_pct=$(echo "$input" | jq -r '.rate_limits.five_hour.used_percentage // empty')
builtin_five_hour_reset=$(echo "$input" | jq -r '.rate_limits.five_hour.resets_at // empty')
builtin_seven_day_pct=$(echo "$input" | jq -r '.rate_limits.seven_day.used_percentage // empty')
builtin_seven_day_reset=$(echo "$input" | jq -r '.rate_limits.seven_day.resets_at // empty')
use_builtin=false
if [ -n "$builtin_five_hour_pct" ] || [ -n "$builtin_seven_day_pct" ]; then
use_builtin=true
fi
# Cache setup — shared across all Claude Code instances to avoid rate limits
claude_config_dir_hash=$(echo -n "$claude_config_dir" | shasum -a 256 2>/dev/null || echo -n "$claude_config_dir" | sha256sum 2>/dev/null)
claude_config_dir_hash=$(echo "$claude_config_dir_hash" | cut -c1-8)
cache_file="/tmp/claude/statusline-usage-cache-${claude_config_dir_hash}.json"
cache_max_age=60 # seconds between API calls
mkdir -p /tmp/claude
needs_refresh=true
usage_data=""
# Always load cache — used as primary source for API path, and as fallback when builtin reports zero
if [ -f "$cache_file" ] && [ -s "$cache_file" ]; then
cache_mtime=$(stat -c %Y "$cache_file" 2>/dev/null || stat -f %m "$cache_file" 2>/dev/null)
now=$(date +%s)
cache_age=$(( now - cache_mtime ))
if [ "$cache_age" -lt "$cache_max_age" ]; then
needs_refresh=false
fi
usage_data=$(cat "$cache_file" 2>/dev/null)
fi
# When builtin values are all zero AND reset timestamps are missing, it likely indicates
# an API failure on Claude's side — fall through to cached data instead of displaying
# misleading 0%. Genuine zero responses (after a billing reset) still include valid
# resets_at timestamps, so we trust those.
effective_builtin=false
if $use_builtin; then
# Trust builtin if any percentage is non-zero
if { [ -n "$builtin_five_hour_pct" ] && [ "$(printf '%.0f' "$builtin_five_hour_pct" 2>/dev/null)" != "0" ]; } || \
{ [ -n "$builtin_seven_day_pct" ] && [ "$(printf '%.0f' "$builtin_seven_day_pct" 2>/dev/null)" != "0" ]; }; then
effective_builtin=true
fi
# Also trust if reset timestamps are present — genuine zero responses include valid reset times
if ! $effective_builtin; then
if { [ -n "$builtin_five_hour_reset" ] && [ "$builtin_five_hour_reset" != "null" ] && [ "$builtin_five_hour_reset" != "0" ]; } || \
{ [ -n "$builtin_seven_day_reset" ] && [ "$builtin_seven_day_reset" != "null" ] && [ "$builtin_seven_day_reset" != "0" ]; }; then
effective_builtin=true
fi
fi
fi
# Refresh API cache when stale — runs regardless of builtin rate_limits because
# extra_usage is only exposed through the OAuth usage endpoint (not stdin JSON).
# Throttled to cache_max_age and stampede-locked via touch for shared panes.
if $needs_refresh; then
touch "$cache_file" # stampede lock: prevent parallel panes from fetching simultaneously
token=$(get_oauth_token)
if [ -n "$token" ] && [ "$token" != "null" ]; then
response=$(curl -s --max-time 10 \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $token" \
-H "anthropic-beta: oauth-2025-04-20" \
-H "User-Agent: claude-code/2.1.34" \
"https://api.anthropic.com/api/oauth/usage" 2>/dev/null)
# Only cache valid usage responses (not error/rate-limit JSON)
if [ -n "$response" ] && echo "$response" | jq -e '.five_hour' >/dev/null 2>&1; then
usage_data="$response"
echo "$response" > "$cache_file"
fi
fi
# Remove the stampede sentinel if the fetch failed to produce valid JSON —
# otherwise an empty cache file would suppress retries for a full cache_max_age window.
[ -f "$cache_file" ] && [ ! -s "$cache_file" ] && rm -f "$cache_file"
fi
# Cross-platform ISO to epoch conversion
# Converts ISO 8601 timestamp (e.g. "2025-06-15T12:30:00Z" or "2025-06-15T12:30:00.123+00:00") to epoch seconds.
# Properly handles UTC timestamps and converts to local time.
iso_to_epoch() {
local iso_str="$1"
# Try GNU date first (Linux) — handles ISO 8601 format automatically
local epoch
epoch=$(date -d "${iso_str}" +%s 2>/dev/null)
if [ -n "$epoch" ]; then
echo "$epoch"
return 0
fi
# BSD date (macOS) - handle various ISO 8601 formats
local stripped="${iso_str%%.*}" # Remove fractional seconds (.123456)
stripped="${stripped%%Z}" # Remove trailing Z
stripped="${stripped%%+*}" # Remove timezone offset (+00:00)
stripped="${stripped%%-[0-9][0-9]:[0-9][0-9]}" # Remove negative timezone offset
# Check if timestamp is UTC (has Z or +00:00 or -00:00)
if [[ "$iso_str" == *"Z"* ]] || [[ "$iso_str" == *"+00:00"* ]] || [[ "$iso_str" == *"-00:00"* ]]; then
# For UTC timestamps, parse with timezone set to UTC
epoch=$(env TZ=UTC date -j -f "%Y-%m-%dT%H:%M:%S" "$stripped" +%s 2>/dev/null)
else
epoch=$(date -j -f "%Y-%m-%dT%H:%M:%S" "$stripped" +%s 2>/dev/null)
fi
if [ -n "$epoch" ]; then
echo "$epoch"
return 0
fi
return 1
}
# Format ISO reset time to compact local time
# Usage: format_reset_time <iso_string> <style: time|datetime|date>
format_reset_time() {
local iso_str="$1"
local style="$2"
{ [ -z "$iso_str" ] || [ "$iso_str" = "null" ]; } && return
# Parse ISO datetime and convert to local time (cross-platform)
local epoch
epoch=$(iso_to_epoch "$iso_str")
[ -z "$epoch" ] && return
# Format based on style
# Try GNU date first (Linux), then BSD date (macOS)
# Previous implementation piped BSD date through sed/tr, which always returned
# exit code 0 from the last pipe stage, preventing the GNU date fallback from
# ever executing on Linux.
local formatted=""
case "$style" in
time)
formatted=$(date -d "@$epoch" +"%H:%M" 2>/dev/null) || \
formatted=$(date -j -r "$epoch" +"%H:%M" 2>/dev/null)
;;
datetime)
formatted=$(date -d "@$epoch" +"%a %b %-d, %H:%M" 2>/dev/null) || \
formatted=$(date -j -r "$epoch" +"%a %b %-d, %H:%M" 2>/dev/null)
;;
*)
formatted=$(date -d "@$epoch" +"%b %-d" 2>/dev/null) || \
formatted=$(date -j -r "$epoch" +"%b %-d" 2>/dev/null)
;;
esac
[ -n "$formatted" ] && echo "$formatted"
}
sep=" ${dim}|${reset} "
# Render extra_usage segment from API usage data (not available via stdin rate_limits).
# Appends to the global $out. No-op when data is missing or is_enabled is false.
render_extra_usage() {
local data="$1"
[ -z "$data" ] && return
local enabled
enabled=$(echo "$data" | jq -r '.extra_usage.is_enabled // false' 2>/dev/null)
[ "$enabled" != "true" ] && return
local pct used limit
pct=$(echo "$data" | jq -r '.extra_usage.utilization // 0' | awk '{printf "%.0f", $1}')
used=$(echo "$data" | jq -r '.extra_usage.used_credits // 0' | LC_NUMERIC=C awk '{printf "%.2f", $1/100}')
limit=$(echo "$data" | jq -r '.extra_usage.monthly_limit // 0' | LC_NUMERIC=C awk '{printf "%.2f", $1/100}')
if [ -n "$used" ] && [ -n "$limit" ] && [[ "$used" != *'$'* ]] && [[ "$limit" != *'$'* ]]; then
local color
color=$(usage_color "$pct")
out+="${sep}${white}extra${reset} ${color}\$${used}/\$${limit}${reset}"
else
out+="${sep}${white}extra${reset} ${green}enabled${reset}"
fi
}
if $effective_builtin; then
# ---- Use rate_limits data provided directly by Claude Code in JSON input ----
# resets_at values are Unix epoch integers in this source
if [ -n "$builtin_five_hour_pct" ]; then
five_hour_pct=$(printf "%.0f" "$builtin_five_hour_pct")
five_hour_color=$(usage_color "$five_hour_pct")
out+="${sep}${white}5h${reset} ${five_hour_color}${five_hour_pct}%${reset}"
if [ -n "$builtin_five_hour_reset" ] && [ "$builtin_five_hour_reset" != "null" ]; then
five_hour_reset=$(date -j -r "$builtin_five_hour_reset" +"%H:%M" 2>/dev/null || date -d "@$builtin_five_hour_reset" +"%H:%M" 2>/dev/null)
[ -n "$five_hour_reset" ] && out+=" ${dim}@${five_hour_reset}${reset}"
fi
fi
if [ -n "$builtin_seven_day_pct" ]; then
seven_day_pct=$(printf "%.0f" "$builtin_seven_day_pct")
seven_day_color=$(usage_color "$seven_day_pct")
out+="${sep}${white}7d${reset} ${seven_day_color}${seven_day_pct}%${reset}"
if [ -n "$builtin_seven_day_reset" ] && [ "$builtin_seven_day_reset" != "null" ]; then
seven_day_reset=$(date -j -r "$builtin_seven_day_reset" +"%a %b %-d, %H:%M" 2>/dev/null || date -d "@$builtin_seven_day_reset" +"%a %b %-d, %H:%M" 2>/dev/null)
[ -n "$seven_day_reset" ] && out+=" ${dim}@${seven_day_reset}${reset}"
fi
fi
# Render extra_usage from API cache (stdin rate_limits doesn't expose it)
render_extra_usage "$usage_data"
# Cache builtin values so they're available as fallback when API is unavailable.
# Convert epoch resets_at to ISO 8601 for compatibility with the API-format cache parser.
# Preserve extra_usage from prior API response so we don't clobber it.
_fh_reset_json="null"
if [ -n "$builtin_five_hour_reset" ] && [ "$builtin_five_hour_reset" != "null" ] && [ "$builtin_five_hour_reset" != "0" ]; then
_fh_iso=$(date -u -r "$builtin_five_hour_reset" +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null || \
date -u -d "@$builtin_five_hour_reset" +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null)
[ -n "$_fh_iso" ] && _fh_reset_json="\"$_fh_iso\""
fi
_sd_reset_json="null"
if [ -n "$builtin_seven_day_reset" ] && [ "$builtin_seven_day_reset" != "null" ] && [ "$builtin_seven_day_reset" != "0" ]; then
_sd_iso=$(date -u -r "$builtin_seven_day_reset" +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null || \
date -u -d "@$builtin_seven_day_reset" +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null)
[ -n "$_sd_iso" ] && _sd_reset_json="\"$_sd_iso\""
fi
_extra_json=$(echo "$usage_data" | jq -c '.extra_usage // null' 2>/dev/null)
[ -z "$_extra_json" ] && _extra_json="null"
printf '{"five_hour":{"utilization":%s,"resets_at":%s},"seven_day":{"utilization":%s,"resets_at":%s},"extra_usage":%s}' \
"${builtin_five_hour_pct:-0}" "$_fh_reset_json" \
"${builtin_seven_day_pct:-0}" "$_sd_reset_json" \
"$_extra_json" > "$cache_file" 2>/dev/null
elif [ -n "$usage_data" ] && echo "$usage_data" | jq -e '.five_hour' >/dev/null 2>&1; then
# ---- Fall back: API-fetched usage data ----
# ---- 5-hour (current) ----
five_hour_pct=$(echo "$usage_data" | jq -r '.five_hour.utilization // 0' | awk '{printf "%.0f", $1}')
five_hour_reset_iso=$(echo "$usage_data" | jq -r '.five_hour.resets_at // empty')
five_hour_reset=$(format_reset_time "$five_hour_reset_iso" "time")
five_hour_color=$(usage_color "$five_hour_pct")
out+="${sep}${white}5h${reset} ${five_hour_color}${five_hour_pct}%${reset}"
[ -n "$five_hour_reset" ] && out+=" ${dim}@${five_hour_reset}${reset}"
# ---- 7-day (weekly) ----
seven_day_pct=$(echo "$usage_data" | jq -r '.seven_day.utilization // 0' | awk '{printf "%.0f", $1}')
seven_day_reset_iso=$(echo "$usage_data" | jq -r '.seven_day.resets_at // empty')
seven_day_reset=$(format_reset_time "$seven_day_reset_iso" "datetime")
seven_day_color=$(usage_color "$seven_day_pct")
out+="${sep}${white}7d${reset} ${seven_day_color}${seven_day_pct}%${reset}"
[ -n "$seven_day_reset" ] && out+=" ${dim}@${seven_day_reset}${reset}"
render_extra_usage "$usage_data"
else
# No valid usage data — show placeholders
out+="${sep}${white}5h${reset} ${dim}-${reset}"
out+="${sep}${white}7d${reset} ${dim}-${reset}"
fi
# ===== Update check (cached, 24h TTL) =====
# Set STATUSLINE_CHECK_UPDATES=false to disable the update check (no network calls).
update_line=""
if [ "${STATUSLINE_CHECK_UPDATES:-true}" != "false" ]; then
version_cache_file="/tmp/claude/statusline-version-cache.json"
version_cache_max_age=86400 # 24 hours
version_needs_refresh=true
version_data=""
if [ -f "$version_cache_file" ]; then
vc_mtime=$(stat -c %Y "$version_cache_file" 2>/dev/null || stat -f %m "$version_cache_file" 2>/dev/null)
vc_now=$(date +%s)
vc_age=$(( vc_now - vc_mtime ))
if [ "$vc_age" -lt "$version_cache_max_age" ]; then
version_needs_refresh=false
fi
version_data=$(cat "$version_cache_file" 2>/dev/null)
fi
if $version_needs_refresh; then
touch "$version_cache_file" 2>/dev/null
vc_response=$(curl -s --max-time 5 \
-H "Accept: application/vnd.github+json" \
"https://api.github.com/repos/daniel3303/ClaudeCodeStatusLine/releases/latest" 2>/dev/null)
if [ -n "$vc_response" ] && echo "$vc_response" | jq -e '.tag_name' >/dev/null 2>&1; then
version_data="$vc_response"
echo "$vc_response" > "$version_cache_file"
elif [ ! -s "$version_cache_file" ]; then
rm -f "$version_cache_file" 2>/dev/null
fi
fi
if [ -n "$version_data" ]; then
latest_tag=$(echo "$version_data" | jq -r '.tag_name // empty')
if [ -n "$latest_tag" ] && version_gt "$latest_tag" "$VERSION"; then
update_line="\n${dim}Update available: ${latest_tag} → Tell Claude: \"Find my installed status bar and update it\"${reset}"
fi
fi
fi
# Append CLI version as last segment
if [ -n "$cli_version" ]; then
out+=" ${dim}|${reset} ${orange}v${cli_version}${reset}"
fi
# Output
printf "%b" "$out$update_line"
exit 0
+266 -40
View File
@@ -1,7 +1,8 @@
{ lib, pkgs, config, ... }: { lib, pkgs', pkgs, config, ... }:
let let
inherit (lib) genAttrs mkIf mkMerge mkForce; inherit (lib) genAttrs mkIf mkMerge mkForce mapAttrs mkOptionDefault mkDefault;
inherit (lib.my) mkBoolOpt'; inherit (lib.my) mkOpt' mkBoolOpt';
inherit (lib.my.c) pubDomain;
cfg = config.my.gui; cfg = config.my.gui;
@@ -10,33 +11,80 @@ let
name = "Monocraft"; name = "Monocraft";
size = 10; size = 10;
}; };
doomWad = pkgs.fetchurl {
url = "https://distro.ibiblio.org/slitaz/sources/packages/d/doom1.wad";
hash = "sha256-HX1DvlAeZ9kn5BXguPPinDvzMHXoWXIYFvZSpSbKx3E=";
};
subwaySurfers = pkgs.fetchurl {
url = "https://p.${pubDomain}/video/subway-surfers-smol.mkv";
hash = "sha256-fMe7TDRNTymRHIJOi7qG3trzu4GP8a3gCDz+FMkX1dY=";
};
minecraftParkour = pkgs.fetchurl {
url = "https://p.${pubDomain}/video/minecraft-parkour-smol.mkv";
hash = "sha256-723pRm4AsIjY/WFUyAHzTJp+JvH4Pn5hvzF9wHTnOPA=";
};
genLipsum = pkgs.writeScript "lipsum" ''
#!${pkgs.python3.withPackages (ps: [ ps.python-lorem ])}/bin/python
import lorem
print(lorem.get_paragraph(count=5, sep='\n\n'))
'';
doomsaver' = brainrotTextCommand: pkgs.runCommand "doomsaver" {
inherit (pkgs) windowtolayer tmux terminaltexteffects;
chocoDoom = pkgs.chocolate-doom2xx;
ffmpeg = pkgs.ffmpeg-full;
python = pkgs.python3.withPackages (ps: [ ps.filelock ]);
inherit doomWad;
enojy = ./enojy.jpg;
inherit brainrotTextCommand subwaySurfers minecraftParkour;
} ''
mkdir -p "$out"/bin
substituteAll ${./screensaver.py} "$out"/bin/doomsaver
chmod +x "$out"/bin/doomsaver
'';
doomsaver = doomsaver' cfg.screensaver.brainrotTextCommand;
in in
{ {
options.my.gui = { options.my.gui = with lib.types; {
enable = mkBoolOpt' true "Enable settings and packages meant for graphical systems"; enable = mkBoolOpt' true "Enable settings and packages meant for graphical systems";
manageGraphical = mkBoolOpt' false "Configure the graphical session"; manageGraphical = mkBoolOpt' false "Configure the graphical session";
standalone = mkBoolOpt' false "Enable settings for fully Nix managed systems"; standalone = mkBoolOpt' false "Enable settings for fully Nix managed systems";
screensaver.brainrotTextCommand = mkOpt' (either path str) genLipsum "Command to generate brainrot text.";
}; };
config = mkIf cfg.enable (mkMerge [ config = mkIf cfg.enable (mkMerge [
{ {
home = { home = {
packages = with pkgs; [ packages = with pkgs; [
xdg-utils
font.package font.package
(nerdfonts.override { nerd-fonts.sauce-code-pro
fonts = [ "DroidSansMono" "SourceCodePro" ]; nerd-fonts.droid-sans-mono
}) noto-fonts-color-emoji
noto-fonts-emoji
grim grim
slurp slurp
swappy swappy
python310Packages.python-lsp-server python3Packages.python-lsp-server
nil # nix language server nil # nix language server
zls # zig language server zls # zig language server
rust-analyzer rust-analyzer
neofetch
cowsay
fortune
jp2a
terminaltexteffects
screenfetch
fastfetch
cmatrix
doomsaver
ffmpeg-full
xournalpp
]; ];
}; };
@@ -51,7 +99,15 @@ in
alacritty = { alacritty = {
enable = true; enable = true;
settings = { settings = {
font.normal.family = font.name; general.import = [ ./alacritty-xterm.toml ];
font = {
size = font.size;
normal = {
family = font.name;
style = "Regular";
};
};
}; };
}; };
@@ -59,9 +115,23 @@ in
enable = true; enable = true;
inherit font; inherit font;
settings = { settings = {
background_opacity = "0.8"; background_opacity = "0.65";
tab_bar_edge = "top"; tab_bar_edge = "top";
shell_integration = "no-sudo"; shell_integration = "no-sudo";
font_features = "${font.name} -liga";
};
};
foot = {
enable = true;
settings = {
main = {
font = "${font.name}:size=${toString font.size}";
};
colors = {
alpha = 0.8;
background = "000000";
};
}; };
}; };
@@ -85,6 +155,19 @@ in
}; };
}; };
}; };
claude-code = {
enable = true;
settings = {
model = "opus";
theme = "auto";
statusLine = {
type = "command";
command = ./claude-statusline.sh;
padding = 0;
};
};
};
}; };
} }
@@ -108,6 +191,19 @@ in
}; };
Install.RequiredBy = [ "sway-session.target" ]; Install.RequiredBy = [ "sway-session.target" ];
}; };
activate-linux = {
Unit = {
Description = "Linux activation watermark";
After = "graphical-session.target";
PartOf = "graphical-session.target";
};
Service = {
Type = "simple";
ExecStart = "${pkgs.activate-linux}/bin/activate-linux";
};
Install.RequiredBy = [ "graphical-session.target" ];
};
}; };
}; };
@@ -117,6 +213,7 @@ in
wl-clipboard wl-clipboard
wev wev
wdisplays wdisplays
swaysome
pavucontrol pavucontrol
libsecret libsecret
@@ -126,10 +223,11 @@ in
]; ];
pointerCursor = { pointerCursor = {
package = pkgs.vanilla-dmz; package = pkgs.posy-cursors;
name = "Vanilla-DMZ"; name = "Posy_Cursor";
size = 16; size = 32;
gtk.enable = true; gtk.enable = true;
x11.enable = true;
}; };
}; };
@@ -138,9 +236,36 @@ in
xsession.preferStatusNotifierItems = true; xsession.preferStatusNotifierItems = true;
wayland = { wayland = {
windowManager = { windowManager = {
sway = { sway =
let
cfg = config.wayland.windowManager.sway.config;
mod = cfg.modifier;
renameWs = pkgs.writeShellScript "sway-rename-ws" ''
focused_ws="$(swaymsg -t get_workspaces | jq ".[] | select(.focused)")"
focused_num="$(jq -r ".num" <<< "$focused_ws")"
focused_name="$(jq -r ".name" <<< "$focused_ws")"
placeholder="$(sed -E 's/[0-9]+: //' <<< "$focused_name")"
name="$(rofi -dmenu -p "rename ws $focused_num" -theme+entry+placeholder "\"$placeholder\"")"
if [ -n "$name" ]; then
swaymsg rename workspace "$focused_name" to "$focused_num: $name"
fi
'';
clearWsName = pkgs.writeShellScript "sway-clear-ws-name" ''
focused_ws="$(swaymsg -t get_workspaces | jq ".[] | select(.focused)")"
focused_num="$(jq -r ".num" <<< "$focused_ws")"
focused_name="$(jq -r ".name" <<< "$focused_ws")"
swaymsg rename workspace "$focused_name" to "$focused_num"
'';
in
{
enable = true; enable = true;
xwayland = true; xwayland = true;
extraConfigEarly = ''
set $mod ${mod}
'';
config = { config = {
input = { input = {
"type:touchpad" = { "type:touchpad" = {
@@ -155,23 +280,87 @@ in
modifier = "Mod4"; modifier = "Mod4";
terminal = "kitty"; terminal = "kitty";
keybindings = keybindings = mapAttrs (k: mkOptionDefault) {
let "${mod}+Left" = "focus left";
cfg = config.wayland.windowManager.sway.config; "${mod}+Down" = "focus down";
mod = cfg.modifier; "${mod}+Up" = "focus up";
in "${mod}+Right" = "focus right";
lib.mkOptionDefault {
"${mod}+Shift+Left" = "move left";
"${mod}+Shift+Down" = "move down";
"${mod}+Shift+Up" = "move up";
"${mod}+Shift+Right" = "move right";
"${mod}+b" = "splith";
"${mod}+v" = "splitv";
"${mod}+f" = "fullscreen toggle";
"${mod}+a" = "focus parent";
"${mod}+s" = "layout stacking";
"${mod}+w" = "layout tabbed";
"${mod}+e" = "layout toggle split";
"${mod}+Shift+space" = "floating toggle";
"${mod}+space" = "focus mode_toggle";
"${mod}+1" = "workspace number 1";
"${mod}+2" = "workspace number 2";
"${mod}+3" = "workspace number 3";
"${mod}+4" = "workspace number 4";
"${mod}+5" = "workspace number 5";
"${mod}+6" = "workspace number 6";
"${mod}+7" = "workspace number 7";
"${mod}+8" = "workspace number 8";
"${mod}+9" = "workspace number 9";
"${mod}+0" = "workspace number 10";
"${mod}+Shift+1" =
"move container to workspace number 1";
"${mod}+Shift+2" =
"move container to workspace number 2";
"${mod}+Shift+3" =
"move container to workspace number 3";
"${mod}+Shift+4" =
"move container to workspace number 4";
"${mod}+Shift+5" =
"move container to workspace number 5";
"${mod}+Shift+6" =
"move container to workspace number 6";
"${mod}+Shift+7" =
"move container to workspace number 7";
"${mod}+Shift+8" =
"move container to workspace number 8";
"${mod}+Shift+9" =
"move container to workspace number 9";
"${mod}+Shift+0" =
"move container to workspace number 10";
"${mod}+Shift+minus" = "move scratchpad";
"${mod}+minus" = "scratchpad show";
"${mod}+Return" = "exec ${cfg.terminal}";
"${mod}+r" = "mode resize";
"${mod}+d" = null; "${mod}+d" = null;
"${mod}+l" = "exec swaylock -i ${./lock.png} -s stretch"; "${mod}+l" = "exec ${doomsaver}/bin/doomsaver";
"${mod}+q" = "kill";
"${mod}+Shift+c" = "reload";
"${mod}+Shift+q" = "exec swaynag -t warning -m 'bruh you really wanna kill sway?' -b 'ye' 'systemctl --user stop graphical-session.target && swaymsg exit'";
# rofi
"${mod}+x" = "exec ${cfg.menu}"; "${mod}+x" = "exec ${cfg.menu}";
"${mod}+Shift+x" = "exec rofi -show drun"; "${mod}+Shift+x" = "exec rofi -show drun";
"${mod}+q" = "kill";
"${mod}+Shift+q" = "exec swaynag -t warning -m 'bruh you really wanna kill sway?' -b 'ye' 'systemctl --user stop graphical-session.target && swaymsg exit'";
"${mod}+Shift+d" = ''exec grim - | swappy -f -'';
"${mod}+Shift+s" = ''exec grim -g "$(slurp)" - | swappy -f -'';
"${mod}+Shift+e" = "exec rofi -show emoji"; "${mod}+Shift+e" = "exec rofi -show emoji";
# Config for this doesn't seem to work :/ # Config for this doesn't seem to work :/
"${mod}+c" = ''exec rofi -show calc -calc-command "echo -n '{result}' | ${pkgs.wl-clipboard}/bin/wl-copy"''; "${mod}+c" = ''exec rofi -show calc -calc-command "echo -n '{result}' | ${pkgs.wl-clipboard}/bin/wl-copy"'';
"${mod}+Shift+r" = "exec ${renameWs}";
"${mod}+Shift+n" = "exec ${clearWsName}";
# Screenshots
"${mod}+Shift+d" = ''exec grim - | swappy -f -'';
"${mod}+Shift+s" = ''exec grim -g "$(slurp)" - | swappy -f -'';
"XF86MonBrightnessDown" = "exec ${pkgs.brightnessctl}/bin/brightnessctl set 5%-";
"XF86MonBrightnessUp" = "exec ${pkgs.brightnessctl}/bin/brightnessctl set +5%";
"XF86AudioRaiseVolume" = "exec ${pkgs.pamixer}/bin/pamixer -i 5"; "XF86AudioRaiseVolume" = "exec ${pkgs.pamixer}/bin/pamixer -i 5";
"XF86AudioLowerVolume" = "exec ${pkgs.pamixer}/bin/pamixer -d 5"; "XF86AudioLowerVolume" = "exec ${pkgs.pamixer}/bin/pamixer -d 5";
@@ -188,6 +377,9 @@ in
menu = "rofi -show run"; menu = "rofi -show run";
bars = mkForce [ ]; bars = mkForce [ ];
}; };
extraConfig = ''
include ${./swaysome.conf}
'';
swaynag = { swaynag = {
enable = true; enable = true;
@@ -202,6 +394,10 @@ in
name = "Numix"; name = "Numix";
package = pkgs.numix-gtk-theme; package = pkgs.numix-gtk-theme;
}; };
gtk4.theme = {
name = "Numix";
package = pkgs.numix-gtk-theme;
};
iconTheme = { iconTheme = {
name = "Numix"; name = "Numix";
package = pkgs.numix-icon-theme; package = pkgs.numix-icon-theme;
@@ -210,17 +406,10 @@ in
}; };
qt = { qt = {
enable = true; enable = true;
platformTheme = "gtk"; platformTheme.name = "gtk";
}; };
services = { services = {
swaync = {
enable = true;
settings = {
widgets = [ "title" "dnd" "mpris" "notifications" ];
};
};
playerctld.enable = true; playerctld.enable = true;
spotifyd = { spotifyd = {
enable = false; enable = false;
@@ -237,18 +426,45 @@ in
device_type = "computer"; device_type = "computer";
}; };
}; };
easyeffects = {
enable = mkDefault false;
preset = mkDefault "moar-bass";
extraPresets = {
moar-bass = {
output = {
"bass_enhancer#0" = {
amount = 3;
blend = 0;
bypass = false;
floor = 20;
floor-active = false;
harmonics = 8.5;
input-gain = 0;
output-gain = 0;
scope = 100;
};
blocklist = [ ];
plugins_order = [ "bass_enhancer#0" ];
};
};
};
};
}; };
programs = { programs = {
git = { git = {
enable = true; enable = true;
diff-so-fancy.enable = true; settings = {
userEmail = "jackos1998@gmail.com"; user = {
userName = "Jack O'Sullivan"; email = "jackos1998@gmail.com";
extraConfig = { name = "Jack O'Sullivan";
};
pull.rebase = true; pull.rebase = true;
}; };
lfs.enable = true;
}; };
diff-so-fancy.enable = true;
waybar = import ./waybar.nix { inherit lib pkgs config font; }; waybar = import ./waybar.nix { inherit lib pkgs config font; };
rofi = { rofi = {
@@ -271,7 +487,7 @@ in
chromium = { chromium = {
enable = true; enable = true;
package = (pkgs.chromium.override { enableWideVine = true; }).overrideAttrs (old: { package = (pkgs'.unstable.chromium.override { enableWideVine = true; }).overrideAttrs (old: {
buildCommand = '' buildCommand = ''
${old.buildCommand} ${old.buildCommand}
@@ -297,6 +513,15 @@ in
] (_: "chromium-browser.desktop"); ] (_: "chromium-browser.desktop");
}; };
}; };
my = {
swaync = {
enable = true;
settings = {
widgets = [ "title" "dnd" "mpris" "notifications" ];
};
};
};
}) })
(mkIf (cfg.standalone && !pkgs.stdenv.isDarwin) { (mkIf (cfg.standalone && !pkgs.stdenv.isDarwin) {
@@ -304,6 +529,7 @@ in
userDirs = { userDirs = {
enable = true; enable = true;
createDirectories = true; createDirectories = true;
setSessionVariables = true;
desktop = "$HOME/desktop"; desktop = "$HOME/desktop";
documents = "$HOME/documents"; documents = "$HOME/documents";
download = "$HOME/downloads"; download = "$HOME/downloads";
Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

+257
View File
@@ -0,0 +1,257 @@
#!@python@/bin/python
import argparse
import json
import os
import random
import signal
import subprocess
import sys
import filelock
class Screensaver:
def __init__(self, cmd, env=None, weight=1):
self.cmd = cmd
self.weight = weight
if env is not None:
self.env = os.environ.copy()
for k, v in env.items():
self.env[k] = v
else:
self.env = None
self.proc = None
def start(self):
assert self.proc is None
self.proc = subprocess.Popen(self.cmd, env=self.env)
def wait(self):
assert self.proc is not None
self.proc.wait()
def stop(self, kill=False):
assert self.proc is not None
if kill:
self.proc.kill()
else:
self.proc.terminate()
class DoomSaver(Screensaver):
wad = '@doomWad@'
def __init__(self, demo_index, weight=1.5):
super().__init__(
['@chocoDoom@/bin/chocolate-doom',
'-iwad', self.wad,
'-demoloopi', str(demo_index)],
env={
'SDL_AUDIODRIVER': 'null',
'SDL_VIDEODRIVER': 'caca',
'CACA_DRIVER': 'ncurses',
},
weight=weight,
)
def stop(self):
super().stop(kill=True)
class TTESaver(Screensaver):
effects = (
'beams,binarypath,blackhole,bouncyballs,bubbles,burn,colorshift,crumble,'
'decrypt,errorcorrect,expand,fireworks,middleout,orbittingvolley,overflow,'
'pour,print,rain,randomsequence,rings,scattered,slice,slide,spotlights,'
'spray,swarm,synthgrid,unstable,vhstape,waves,wipe'
).split(',')
def __init__(self, cmd, env=None, weight=1):
super().__init__(cmd, env=env, weight=weight)
self.running = False
def start(self):
self.running = True
def wait(self):
while self.running:
effect_cmd = ['@terminaltexteffects@/bin/tte', random.choice(self.effects)]
print(f"$ {self.cmd} | {' '.join(effect_cmd)}")
content = subprocess.check_output(self.cmd, shell=True, env=self.env, stderr=subprocess.DEVNULL)
self.proc = subprocess.Popen(effect_cmd, stdin=subprocess.PIPE)
self.proc.stdin.write(content)
self.proc.stdin.close()
self.proc.wait()
def stop(self):
self.running = False
self.proc.terminate()
class FFmpegCACASaver(Screensaver):
@staticmethod
def command(video, size):
return ['@ffmpeg@/bin/ffmpeg', '-hide_banner', '-loglevel', 'error',
'-stream_loop', '-1', '-i', video,
'-pix_fmt', 'rgb24', '-window_size', f'{size}x{size}',
'-f', 'caca', '-']
def __init__(self, video, weight=2):
cols, lines = os.get_terminal_size()
# IDK if it's reasonable to do this as "1:1"
size = lines - 4
super().__init__(
self.command(video, size),
env={'CACA_DRIVER': 'ncurses'},
weight=weight,
)
def stop(self):
super().stop(kill=True)
class BrainrotStorySaver(Screensaver):
def __init__(self, video, text_command, weight=2):
cols, lines = os.get_terminal_size()
video_size = lines - 1
video_command = ' '.join(FFmpegCACASaver.command(video, video_size))
text_command = (
f'while true; do {text_command} | '
f'@terminaltexteffects@/bin/tte --wrap-text --canvas-width=80 --canvas-height={video_size//2} --anchor-canvas=c '
'print --final-gradient-stops=ffffff; clear; done' )
self.tmux_session = f'screensaver-{os.urandom(4).hex()}'
super().__init__(
['@tmux@/bin/tmux', 'new-session', '-s', self.tmux_session, '-n', 'brainrot',
text_command, ';', 'split-window', '-hbl', str(lines), video_command],
# ['sh', '-c', text_command],
env={
'CACA_DRIVER': 'ncurses',
'SHELL': '/bin/sh',
},
weight=weight,
)
def stop(self):
subprocess.check_call(['@tmux@/bin/tmux', 'kill-session', '-t', self.tmux_session])
class MultiSaver:
savers = [
DoomSaver(0),
DoomSaver(1),
DoomSaver(2),
Screensaver(['cmatrix']),
TTESaver('screenfetch -N'),
TTESaver('fortune | cowsay'),
TTESaver('top -bn1 | head -n50'),
TTESaver('ss -nltu'),
TTESaver('ss -ntu'),
TTESaver('jp2a --width=100 @enojy@'),
BrainrotStorySaver('@subwaySurfers@', '@brainrotTextCommand@'),
BrainrotStorySaver('@minecraftParkour@', '@brainrotTextCommand@'),
]
state_filename = 'screensaver.json'
def __init__(self, select=None):
self.state_path = os.path.join(f'/run/user/{os.geteuid()}', self.state_filename)
self.lock = filelock.FileLock(f'{self.state_path}.lock')
if select is not None:
assert select >= 0 and select < len(self.savers), 'Invalid screensaver index'
self.selected = self.savers[select]
else:
self.selected = None
self.cleaned_up = False
def select(self):
with self.lock:
if not os.path.exists(self.state_path):
state = {'instances': []}
else:
with open(self.state_path) as f:
state = json.load(f)
if self.selected is None:
available = set(range(len(self.savers)))
new_instances = []
for instance in state['instances']:
if not os.path.exists(f"/proc/{instance['pid']}"):
continue
new_instances.append(instance)
i = instance['saver']
assert i in available
available.remove(i)
assert available, 'No screensavers left'
available = list(available)
weights = []
for i in available:
weights.append(self.savers[i].weight)
selected_i = random.choices(available, weights=weights)[0]
new_instances.append({'pid': os.getpid(), 'saver': selected_i})
state['instances'] = new_instances
# print(f'Selected saver {selected_i}')
self.selected = self.savers[selected_i]
with open(self.state_path, 'w') as f:
json.dump(state, f)
def cleanup(self):
if self.cleaned_up:
return
self.cleaned_up = True
with self.lock:
with open(self.state_path) as f:
state = json.load(f)
for i, instance in enumerate(state['instances']):
if instance['pid'] == os.getpid():
del state['instances'][i]
with open(self.state_path, 'w') as f:
json.dump(state, f)
def run(self):
assert self.selected is not None
self.selected.start()
signal.signal(signal.SIGINT, self._sighandler)
signal.signal(signal.SIGTERM, self._sighandler)
signal.signal(signal.SIGHUP, self._sighandler)
self.selected.wait()
self.cleanup()
def stop(self):
assert self.selected is not None
print('Shutting down')
self.selected.stop()
self.cleanup()
def _sighandler(self, signum, frame):
self.stop()
def main():
parser = argparse.ArgumentParser(description='Wayland terminal-based lock screen')
parser.add_argument('-l', '--locker-cmd', default='swaylock-plugin', help='swaylock-plugin command to use')
parser.add_argument('-t', '--terminal', default='alacritty', help='Terminal emulator to use')
parser.add_argument('-i', '--instance', action='store_true', help='Run as instance')
parser.add_argument('-s', '--screensaver', type=int, help='Force use of specific screensaver')
args = parser.parse_args()
if not args.instance:
cmd = [
args.locker_cmd, '--command-each',
f'@windowtolayer@/bin/windowtolayer -- {args.terminal} -e {sys.argv[0]} --instance']
if args.screensaver is not None:
cmd[-1] += f' --screensaver {args.screensaver}'
subprocess.check_call(cmd)
return
ms = MultiSaver(select=args.screensaver)
ms.select()
ms.run()
if __name__ == '__main__':
main()
Binary file not shown.

Before

Width:  |  Height:  |  Size: 249 KiB

After

Width:  |  Height:  |  Size: 251 KiB

+66
View File
@@ -0,0 +1,66 @@
# Use (un)bindcode or (un)bindsym, depending on what you used in your main sway config file.
# The `--no-warn` setting is only added to shortcuts that exist in the default config. You may want to add or remove
# that flag on some bindings depending on your config.
# Change focus between workspaces
bindsym $mod+Alt+1 exec "swaysome focus 1"
bindsym $mod+Alt+2 exec "swaysome focus 2"
bindsym $mod+Alt+3 exec "swaysome focus 3"
bindsym $mod+Alt+4 exec "swaysome focus 4"
bindsym $mod+Alt+5 exec "swaysome focus 5"
bindsym $mod+Alt+6 exec "swaysome focus 6"
bindsym $mod+Alt+7 exec "swaysome focus 7"
bindsym $mod+Alt+8 exec "swaysome focus 8"
bindsym $mod+Alt+9 exec "swaysome focus 9"
bindsym $mod+Alt+0 exec "swaysome focus 0"
# Focus workspace groups
bindsym --no-warn $mod+1 exec "swaysome focus-group 1"
bindsym --no-warn $mod+2 exec "swaysome focus-group 2"
bindsym --no-warn $mod+3 exec "swaysome focus-group 3"
bindsym --no-warn $mod+4 exec "swaysome focus-group 4"
bindsym --no-warn $mod+5 exec "swaysome focus-group 5"
bindsym --no-warn $mod+6 exec "swaysome focus-group 6"
bindsym --no-warn $mod+7 exec "swaysome focus-group 7"
bindsym --no-warn $mod+8 exec "swaysome focus-group 8"
bindsym --no-warn $mod+9 exec "swaysome focus-group 9"
bindsym --no-warn $mod+0 exec "swaysome focus-group 0"
# Move containers between workspaces
bindsym $mod+Alt+Shift+1 exec "swaysome move 1"
bindsym $mod+Alt+Shift+2 exec "swaysome move 2"
bindsym $mod+Alt+Shift+3 exec "swaysome move 3"
bindsym $mod+Alt+Shift+4 exec "swaysome move 4"
bindsym $mod+Alt+Shift+5 exec "swaysome move 5"
bindsym $mod+Alt+Shift+6 exec "swaysome move 6"
bindsym $mod+Alt+Shift+7 exec "swaysome move 7"
bindsym $mod+Alt+Shift+8 exec "swaysome move 8"
bindsym $mod+Alt+Shift+9 exec "swaysome move 9"
bindsym $mod+Alt+Shift+0 exec "swaysome move 0"
# Move containers to other workspace groups
bindsym --no-warn $mod+Shift+1 exec "swaysome move-to-group 1"
bindsym --no-warn $mod+Shift+2 exec "swaysome move-to-group 2"
bindsym --no-warn $mod+Shift+3 exec "swaysome move-to-group 3"
bindsym --no-warn $mod+Shift+4 exec "swaysome move-to-group 4"
bindsym --no-warn $mod+Shift+5 exec "swaysome move-to-group 5"
bindsym --no-warn $mod+Shift+6 exec "swaysome move-to-group 6"
bindsym --no-warn $mod+Shift+7 exec "swaysome move-to-group 7"
bindsym --no-warn $mod+Shift+8 exec "swaysome move-to-group 8"
bindsym --no-warn $mod+Shift+9 exec "swaysome move-to-group 9"
bindsym --no-warn $mod+Shift+0 exec "swaysome move-to-group 0"
# Move focused container to next output
bindsym $mod+Alt+Right exec "swaysome next-output"
# Move focused container to previous output
bindsym $mod+Alt+Left exec "swaysome prev-output"
# Move focused workspace group to next output
bindsym $mod+Shift+Alt+Right exec "swaysome workspace-group-next-output"
# Move focused workspace group to previous output
bindsym $mod+Shift+Alt+Left exec "swaysome workspace-group-prev-output"
# Init workspaces for every screen
exec "swaysome init 1"
+3 -3
View File
@@ -146,9 +146,9 @@ in
dnd-none = ""; dnd-none = "";
}; };
return-type = "json"; return-type = "json";
exec = "${config.services.swaync.package}/bin/swaync-client -swb"; exec = "${config.my.swaync.package}/bin/swaync-client -swb";
on-click = "${config.services.swaync.package}/bin/swaync-client -t -sw"; on-click = "${config.my.swaync.package}/bin/swaync-client -t -sw";
on-click-right = "${config.services.swaync.package}/bin/swaync-client -d -sw"; on-click-right = "${config.my.swaync.package}/bin/swaync-client -d -sw";
escape = true; escape = true;
}; };
}; };
+2 -2
View File
@@ -19,10 +19,10 @@ let
}; };
}; };
cfg = config.services.swaync; cfg = config.my.swaync;
in in
{ {
options.services.swaync = with lib.types; { options.my.swaync = with lib.types; {
enable = mkEnableOption "Sway Notification Center"; enable = mkEnableOption "Sway Notification Center";
package = mkOption { package = mkOption {
type = package; type = package;
+128 -9
View File
@@ -11,6 +11,9 @@ rec {
jellyseerr = 402; jellyseerr = 402;
atticd = 403; atticd = 403;
kea = 404; kea = 404;
keepalived_script = 405;
photoprism = 406;
copyparty = 408;
}; };
gids = { gids = {
matrix-syncv3 = 400; matrix-syncv3 = 400;
@@ -18,12 +21,16 @@ rec {
jellyseerr = 402; jellyseerr = 402;
atticd = 403; atticd = 403;
kea = 404; kea = 404;
keepalived_script = 405;
photoprism = 406;
adbusers = 407;
copyparty = 408;
}; };
}; };
kernel = { kernel = {
lts = pkgs: pkgs.linuxKernel.packages.linux_6_1; lts = pkgs: pkgs.linuxKernel.packages.linux_6_18;
latest = pkgs: pkgs.linuxKernel.packages.linux_6_6; latest = pkgs: pkgs.linuxKernel.packages.linux_7_0;
}; };
nginx = rec { nginx = rec {
@@ -94,10 +101,10 @@ rec {
nix = { nix = {
cache = rec { cache = rec {
substituters = [ substituters = [
"https://nix-cache.${pubDomain}/main" "https://nix-cache.${pubDomain}"
]; ];
keys = [ keys = [
"main:mMChkG8LwXrFirVfudqjSHasK1jV31OVElYD3eImYl8=" "nix-cache.nul.ie-1:BzH5yMfF4HbzY1C977XzOxoPhEc9Zbu39ftPkUbH+m4="
]; ];
conf = '' conf = ''
extra-substituters = ${concatStringsSep " " substituters} extra-substituters = ${concatStringsSep " " substituters}
@@ -107,7 +114,7 @@ rec {
}; };
pubDomain = "nul.ie"; pubDomain = "nul.ie";
colony = { colony = rec {
domain = "ams1.int.${pubDomain}"; domain = "ams1.int.${pubDomain}";
pubV4 = "94.142.240.44"; pubV4 = "94.142.240.44";
prefixes = with lib.my.net.cidr; rec { prefixes = with lib.my.net.cidr; rec {
@@ -131,6 +138,19 @@ rec {
v4 = subnet 8 3 all.v4; v4 = subnet 8 3 all.v4;
v6 = subnet 4 3 all.v6; v6 = subnet 4 3 all.v6;
}; };
qclk = {
v4 = subnet 8 4 all.v4;
};
p2pTunnels = {
v4 = subnet 8 5 all.v4;
};
hillcrest = {
v4 = subnet 6 0 p2pTunnels.v4;
};
john-valorant = {
v4 = subnet 6 1 p2pTunnels.v4;
};
cust = { cust = {
v4 = subnet 8 100 all.v4; # single ip for routing only v4 = subnet 8 100 all.v4; # single ip for routing only
@@ -144,6 +164,10 @@ rec {
v4 = "94.142.242.255/32"; v4 = "94.142.242.255/32";
v6 = subnet 8 1 cust.v6; v6 = subnet 8 1 cust.v6;
}; };
jam = {
v4 = subnet 8 4 cust.v4;
v6 = subnet 8 2 cust.v6;
};
vip1 = "94.142.241.224/30"; vip1 = "94.142.241.224/30";
vip2 = "94.142.242.254/31"; vip2 = "94.142.242.254/31";
@@ -156,6 +180,16 @@ rec {
home.v6 = "2a0e:97c0:4d0::/48"; home.v6 = "2a0e:97c0:4d0::/48";
}; };
custRouting = with lib.my.net.cidr; {
mail-vm = host 1 prefixes.cust.v4;
darts-vm = host 2 prefixes.cust.v4;
jam-ctr = host 3 prefixes.cust.v4;
};
qclk = {
wgPort = 51821;
};
firewallForwards = aa: [ firewallForwards = aa: [
{ {
port = "http"; port = "http";
@@ -169,6 +203,7 @@ rec {
port = 8448; port = 8448;
dst = aa.middleman.internal.ipv4.address; dst = aa.middleman.internal.ipv4.address;
} }
{ {
port = 25565; port = 25565;
dst = aa.simpcraft-oci.internal.ipv4.address; dst = aa.simpcraft-oci.internal.ipv4.address;
@@ -178,8 +213,27 @@ rec {
dst = aa.simpcraft-staging-oci.internal.ipv4.address; dst = aa.simpcraft-staging-oci.internal.ipv4.address;
} }
{ {
port = 25575; port = 25567;
dst = aa.simpcraft-oci.internal.ipv4.address; dst = aa.kevcraft-oci.internal.ipv4.address;
}
{
port = 25568;
dst = aa.kinkcraft-oci.internal.ipv4.address;
}
{
port = 25569;
dst = aa.graeme-oci.internal.ipv4.address;
}
# RCON... unsafe?
# {
# port = 25575;
# dst = aa.simpcraft-oci.internal.ipv4.address;
# }
{
port = 7777;
dst = aa.gam.internal.ipv4.address;
} }
{ {
@@ -204,6 +258,44 @@ rec {
dst = aa.simpcraft-oci.internal.ipv4.address; dst = aa.simpcraft-oci.internal.ipv4.address;
proto = "udp"; proto = "udp";
} }
{
port = 25567;
dst = aa.kevcraft-oci.internal.ipv4.address;
proto = "udp";
}
{
port = 25568;
dst = aa.kinkcraft-oci.internal.ipv4.address;
proto = "udp";
}
{
port = 25569;
dst = aa.graeme-oci.internal.ipv4.address;
proto = "udp";
}
{
port = 15636;
dst = aa.enshrouded-oci.internal.ipv4.address;
proto = "udp";
}
{
port = 15637;
dst = aa.enshrouded-oci.internal.ipv4.address;
proto = "udp";
}
{
port = qclk.wgPort;
dst = aa.qclk.internal.ipv4.address;
proto = "udp";
}
{
port = 7777;
dst = aa.gam.internal.ipv4.address;
proto = "udp";
}
]; ];
fstrimConfig = { fstrimConfig = {
@@ -227,8 +319,8 @@ rec {
"stream" "stream"
]; ];
routersPubV4 = [ routersPubV4 = [
"109.255.1.246" "109.255.108.88"
"109.255.252.63" "109.255.108.121"
]; ];
prefixes = with lib.my.net.cidr; rec { prefixes = with lib.my.net.cidr; rec {
@@ -277,6 +369,8 @@ rec {
v6 = host ((1*65536*65536*65536) + 65535) prefixes.as211024.v6; v6 = host ((1*65536*65536*65536) + 65535) prefixes.as211024.v6;
}; };
}; };
roceBootModules = [ "ib_core" "ib_uverbs" "mlx5_core" "mlx5_ib" ];
}; };
britway = { britway = {
@@ -292,6 +386,20 @@ rec {
assignedV6 = "2001:19f0:7402:128b:5400:04ff:feac:6e06"; assignedV6 = "2001:19f0:7402:128b:5400:04ff:feac:6e06";
}; };
britnet = {
domain = "bhx1.int.${pubDomain}";
pubV4 = "77.74.199.67";
vpn = {
port = 51820;
};
prefixes = with lib.my.net.cidr; rec {
vpn = {
v4 = "10.200.0.0/24";
v6 = "fdfb:5ebf:6e84::/64";
};
};
};
tailscale = { tailscale = {
prefix = { prefix = {
v4 = "100.64.0.0/10"; v4 = "100.64.0.0/10";
@@ -327,6 +435,7 @@ rec {
}; };
domain = "hentai.engineer"; domain = "hentai.engineer";
ipv4MTU = 1460;
vpn = { vpn = {
port = 51820; port = 51820;
}; };
@@ -335,11 +444,21 @@ rec {
ctrs.v4 = subnet 4 0 all.v4; ctrs.v4 = subnet 4 0 all.v4;
}; };
}; };
hillcrest = {
vpn.port = 51822;
};
john-valorant = {
vpn.port = 51823;
};
sshKeyFiles = { sshKeyFiles = {
me = ../.keys/me.pub; me = ../.keys/me.pub;
deploy = ../.keys/deploy.pub; deploy = ../.keys/deploy.pub;
rsyncNet = ../.keys/zh2855.rsync.net.pub; rsyncNet = ../.keys/zh2855.rsync.net.pub;
mailcowAcme = ../.keys/mailcow-acme.pub; mailcowAcme = ../.keys/mailcow-acme.pub;
harmonia = ../.keys/harmonia.pub;
}; };
sshHostKeys = { sshHostKeys = {
mail-vm = ../.keys/mail-vm-host.pub; mail-vm = ../.keys/mail-vm-host.pub;
+34 -6
View File
@@ -1,11 +1,11 @@
{ lib }: { inputs, lib }:
let let
inherit (builtins) length match elemAt filter replaceStrings substring; inherit (builtins) length match elemAt filter replaceStrings substring;
inherit (lib) inherit (lib)
genAttrs mapAttrsToList filterAttrsRecursive nameValuePair types genAttrs mapAttrsToList filterAttrsRecursive nameValuePair types
mkOption mkOverride mkForce mkIf mergeEqualOption optional mkOption mkOverride mkForce mkIf mergeEqualOption optional
showWarnings concatStringsSep flatten unique optionalAttrs showWarnings concatStringsSep flatten unique optionalAttrs
mkBefore; mkBefore toLower splitString last;
inherit (lib.flake) defaultSystems; inherit (lib.flake) defaultSystems;
in in
rec { rec {
@@ -23,7 +23,7 @@ rec {
attrsToNVList = mapAttrsToList nameValuePair; attrsToNVList = mapAttrsToList nameValuePair;
inherit (import ./net.nix { inherit lib; }) net; inherit ((import "${inputs.libnetRepo}/lib/netu.nix" { inherit lib; }).lib) net;
dns = import ./dns.nix { inherit lib; }; dns = import ./dns.nix { inherit lib; };
c = import ./constants.nix { inherit lib; }; c = import ./constants.nix { inherit lib; };
@@ -53,7 +53,7 @@ rec {
in mkApp "${app}/bin/${app.meta.mainProgram}"; in mkApp "${app}/bin/${app.meta.mainProgram}";
flakePackageOverlay' = flake: pkg: system: (final: prev: flakePackageOverlay' = flake: pkg: system: (final: prev:
let let
pkg' = if pkg != null then flake.packages.${system}.${pkg} else flake.defaultPackage.${system}; pkg' = if pkg != null then flake.packages.${system}.${pkg} else flake.packages.${system}.default;
name = if pkg != null then pkg else pkg'.name; name = if pkg != null then pkg else pkg'.name;
in in
{ {
@@ -248,10 +248,38 @@ rec {
in in
{ {
trivial = prev.trivial // { trivial = prev.trivial // {
release = "23.12:u-${prev.trivial.release}"; release = "26.06:u-${prev.trivial.release}";
codeName = "Amogus"; codeName = "Irritating";
revisionWithDefault = default: self.rev or default; revisionWithDefault = default: self.rev or default;
versionSuffix = ".${date}.${revCode self}:u-${revCode pkgsFlake}"; versionSuffix = ".${date}.${revCode self}:u-${revCode pkgsFlake}";
}; };
}; };
upstreamRelease = last (splitString "-" lib.trivial.release);
netbootKeaClientClasses = { tftpIP, hostname, systems }:
let
testIPXE = "substring(option[user-class].hex, 0, 4) == 'iPXE'";
in
(mapAttrsToList (name: mac: {
name = "nixos-${name}";
test = "(${testIPXE}) and (hexstring(pkt4.mac, ':') == '${toLower mac}')";
next-server = tftpIP;
server-hostname = hostname;
boot-file-name = "http://${hostname}/systems/${name}/menu.ipxe";
}) systems) ++ [
{
name = "ipxe";
test = testIPXE;
next-server = tftpIP;
server-hostname = hostname;
boot-file-name = "http://${hostname}/boot.ipxe";
}
{
name = "efi-x86_64";
test = "option[client-system].hex == 0x0007";
next-server = tftpIP;
server-hostname = hostname;
boot-file-name = "ipxe-x86_64.efi";
}
];
} }
-1322
View File
File diff suppressed because it is too large Load Diff
+191
View File
@@ -0,0 +1,191 @@
{ lib, ... }:
let
inherit (lib.my) net;
inherit (lib.my.c) pubDomain;
inherit (lib.my.c.britnet) domain pubV4 prefixes;
in
{
nixos.systems.britnet = {
system = "x86_64-linux";
nixpkgs = "mine";
assignments = {
allhost = {
inherit domain;
ipv4 = {
address = pubV4;
mask = 24;
gateway = "77.74.199.1";
};
ipv6 = {
address = "2a12:ab46:5344:99::a";
gateway = "2a12:ab46:5344::1";
};
};
vpn = {
ipv4 = {
address = net.cidr.host 1 prefixes.vpn.v4;
gateway = null;
};
ipv6.address = net.cidr.host 1 prefixes.vpn.v6;
};
};
configuration = { lib, pkgs, modulesPath, config, assignments, allAssignments, ... }:
let
inherit (lib) mkMerge mkForce;
inherit (lib.my) networkdAssignment;
in
{
imports = [
"${modulesPath}/profiles/qemu-guest.nix"
];
config = mkMerge [
{
boot = {
initrd.availableKernelModules = [
"ata_piix" "uhci_hcd" "virtio_pci" "virtio_scsi" "ahci" "sr_mod" "virtio_blk"
];
loader = {
systemd-boot.enable = false;
grub = {
enable = true;
device = "/dev/vda";
};
};
};
fileSystems = {
"/boot" = {
device = "/dev/disk/by-uuid/457444a1-81dd-4934-960c-650ad16c92b5";
fsType = "ext4";
};
"/nix" = {
device = "/dev/disk/by-uuid/992c0c79-5be6-45b6-bc30-dc82e3ec082a";
fsType = "ext4";
};
"/persist" = {
device = "/dev/disk/by-uuid/f020a955-54d5-4098-98ba-d3615781d96a";
fsType = "ext4";
neededForBoot = true;
};
};
environment = {
systemPackages = with pkgs; [
wireguard-tools
];
};
services = {
iperf3 = {
enable = true;
openFirewall = true;
};
tailscale = {
enable = true;
authKeyFile = config.age.secrets."tailscale-auth.key".path;
openFirewall = true;
interfaceName = "tailscale0";
extraUpFlags = [
"--operator=${config.my.user.config.name}"
"--login-server=https://hs.nul.ie"
"--netfilter-mode=off"
"--advertise-exit-node"
"--accept-routes=false"
];
};
};
networking = { inherit domain; };
systemd.network = {
netdevs = {
"30-wg0" = {
netdevConfig = {
Name = "wg0";
Kind = "wireguard";
};
wireguardConfig = {
PrivateKeyFile = config.age.secrets."britnet/wg.key".path;
ListenPort = lib.my.c.britnet.vpn.port;
};
wireguardPeers = [
{
PublicKey = "EfPwREfZ/q3ogHXBIqFZh4k/1NRJRyq4gBkBXtegNkE=";
AllowedIPs = [
(net.cidr.host 10 prefixes.vpn.v4)
(net.cidr.host 10 prefixes.vpn.v6)
];
}
];
};
};
links = {
"10-veth0" = {
matchConfig.PermanentMACAddress = "00:db:d9:62:68:1a";
linkConfig.Name = "veth0";
};
};
networks = {
"20-veth0" = mkMerge [
(networkdAssignment "veth0" assignments.allhost)
{
dns = [ "1.1.1.1" "1.0.0.1" ];
routes = [
{
# Gateway is on a different network for some reason...
Destination = "2a12:ab46:5344::1";
Scope = "link";
}
];
}
];
"30-wg0" = mkMerge [
(networkdAssignment "wg0" assignments.vpn)
{
networkConfig.IPv6AcceptRA = mkForce false;
}
];
};
};
my = {
server.enable = true;
secrets = {
key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJIEx+1EC/lN6WKIaOB+O5LJgVHRK962YpZEPQg/m78O";
files = {
"tailscale-auth.key" = {};
"britnet/wg.key" = {
owner = "systemd-network";
};
};
};
firewall = {
udp.allowed = [ lib.my.c.britnet.vpn.port ];
trustedInterfaces = [ "tailscale0" ];
extraRules = ''
table inet filter {
chain forward {
iifname wg0 oifname veth0 accept
}
}
table inet nat {
chain postrouting {
iifname { tailscale0, wg0 } oifname veth0 snat ip to ${assignments.allhost.ipv4.address}
iifname { tailscale0, wg0 } oifname veth0 snat ip6 to ${assignments.allhost.ipv6.address}
}
}
'';
};
};
}
];
};
};
}
+5 -4
View File
@@ -11,23 +11,24 @@ in
config = { config = {
my = { my = {
secrets.files."britway/bgp-password-vultr.conf" = { secrets.files."britway/bgp-password-vultr.conf" = {
owner = "bird2"; owner = "bird";
group = "bird2"; group = "bird";
}; };
}; };
environment.etc."bird/vultr-password.conf".source = config.age.secrets."britway/bgp-password-vultr.conf".path; environment.etc."bird/vultr-password.conf".source = config.age.secrets."britway/bgp-password-vultr.conf".path;
systemd = { systemd = {
services.bird2.after = [ "systemd-networkd-wait-online@veth0.service" ]; services.bird.after = [ "systemd-networkd-wait-online@veth0.service" ];
network = { network = {
config.networkConfig.ManageForeignRoutes = false; config.networkConfig.ManageForeignRoutes = false;
}; };
}; };
services = { services = {
bird2 = { bird = {
enable = true; enable = true;
package = pkgs.bird2;
preCheckConfig = '' preCheckConfig = ''
echo '"dummy"' > vultr-password.conf echo '"dummy"' > vultr-password.conf
''; '';
+2 -2
View File
@@ -106,7 +106,7 @@ in
{ {
matchConfig.Name = "as211024"; matchConfig.Name = "as211024";
networkConfig.IPv6AcceptRA = mkForce false; networkConfig.IPv6AcceptRA = mkForce false;
routes = map (r: { routeConfig = r; }) [ routes = [
{ {
Destination = lib.my.c.colony.prefixes.all.v4; Destination = lib.my.c.colony.prefixes.all.v4;
Gateway = allAssignments.estuary.as211024.ipv4.address; Gateway = allAssignments.estuary.as211024.ipv4.address;
@@ -123,7 +123,7 @@ in
Table = "ts-extra"; Table = "ts-extra";
} }
]; ];
routingPolicyRules = map (r: { routingPolicyRuleConfig = r; }) [ routingPolicyRules = [
{ {
IncomingInterface = "tailscale0"; IncomingInterface = "tailscale0";
To = lib.my.c.colony.prefixes.all.v6; To = lib.my.c.colony.prefixes.all.v6;
+2 -8
View File
@@ -9,11 +9,6 @@ in
config = { config = {
my = { my = {
secrets.files = { secrets.files = {
"dhparams.pem" = {
owner = "acme";
group = "acme";
mode = "440";
};
"britway/cloudflare-credentials.conf" = { "britway/cloudflare-credentials.conf" = {
owner = "acme"; owner = "acme";
group = "acme"; group = "acme";
@@ -45,7 +40,7 @@ in
"*.${pubDomain}" "*.${pubDomain}"
]; ];
dnsProvider = "cloudflare"; dnsProvider = "cloudflare";
credentialsFile = config.age.secrets."britway/cloudflare-credentials.conf".path; environmentFile = config.age.secrets."britway/cloudflare-credentials.conf".path;
}; };
}; };
}; };
@@ -58,7 +53,6 @@ in
logError = "stderr info"; logError = "stderr info";
recommendedTlsSettings = true; recommendedTlsSettings = true;
serverTokens = true; serverTokens = true;
sslDhparam = config.age.secrets."dhparams.pem".path;
# Based on recommended*Settings, but probably better to be explicit about these # Based on recommended*Settings, but probably better to be explicit about these
appendHttpConfig = '' appendHttpConfig = ''
@@ -80,7 +74,7 @@ in
}; };
}; };
"ts.${pubDomain}" = { "hs.${pubDomain}" = {
locations."/" = { locations."/" = {
proxyPass = "http://localhost:${toString config.services.headscale.port}"; proxyPass = "http://localhost:${toString config.services.headscale.port}";
proxyWebsockets = true; proxyWebsockets = true;
+10 -23
View File
@@ -4,20 +4,6 @@ let
inherit (lib.my.c) pubDomain; inherit (lib.my.c) pubDomain;
inherit (lib.my.c.britway) prefixes domain; inherit (lib.my.c.britway) prefixes domain;
# Can't use overrideAttrs because we need to override `vendorHash` within `buildGoModule`
headscale = pkgs.headscale.override {
buildGoModule = args: pkgs.buildGoModule (args // rec {
version = "0.23.0-alpha2";
src = pkgs.fetchFromGitHub {
owner = "juanfont";
repo = "headscale";
rev = "v${version}";
hash = "sha256-sz+uQyyq/5YYDe5I44x5x2nvd48swAhNlInB8KZYvDo=";
};
vendorHash = "sha256-u9AmJguQ5dnJpfhOeLN43apvMHuraOrJhvlEIp9RoIc=";
});
};
advRoutes = concatStringsSep "," [ advRoutes = concatStringsSep "," [
lib.my.c.home.prefixes.all.v4 lib.my.c.home.prefixes.all.v4
lib.my.c.home.prefixes.all.v6 lib.my.c.home.prefixes.all.v6
@@ -39,19 +25,21 @@ in
services = { services = {
headscale = { headscale = {
enable = true; enable = true;
package = headscale;
settings = { settings = {
disable_check_updates = true; disable_check_updates = true;
unix_socket_permission = "0770"; unix_socket_permission = "0770";
server_url = "https://ts.${pubDomain}"; server_url = "https://hs.${pubDomain}";
db_type = "sqlite3"; database = {
db_path = "/var/lib/headscale/db.sqlite3"; type = "sqlite3";
sqlite.path = "/var/lib/headscale/db.sqlite3";
};
noise.private_key_path = "/var/lib/headscale/noise_private.key"; noise.private_key_path = "/var/lib/headscale/noise_private.key";
ip_prefixes = with lib.my.c.tailscale.prefix; [ v4 v6 ]; prefixes = with lib.my.c.tailscale.prefix; { inherit v4 v6; };
dns_config = { dns = {
override_local_dns = false;
# Use IPs that will route inside the VPN to prevent interception # Use IPs that will route inside the VPN to prevent interception
# (e.g. DNS rebinding filtering) # (e.g. DNS rebinding filtering)
restricted_nameservers = { nameservers.split = {
"${domain}" = pubNameservers; "${domain}" = pubNameservers;
"${lib.my.c.colony.domain}" = with allAssignments.estuary.base; [ "${lib.my.c.colony.domain}" = with allAssignments.estuary.base; [
ipv4.address ipv6.address ipv4.address ipv6.address
@@ -65,7 +53,6 @@ in
}; };
magic_dns = true; magic_dns = true;
base_domain = "ts.${pubDomain}"; base_domain = "ts.${pubDomain}";
override_local_dns = false;
}; };
oidc = { oidc = {
only_start_if_oidc_is_available = true; only_start_if_oidc_is_available = true;
@@ -85,7 +72,7 @@ in
interfaceName = "tailscale0"; interfaceName = "tailscale0";
extraUpFlags = [ extraUpFlags = [
"--operator=${config.my.user.config.name}" "--operator=${config.my.user.config.name}"
"--login-server=https://ts.nul.ie" "--login-server=https://hs.nul.ie"
"--netfilter-mode=off" "--netfilter-mode=off"
"--advertise-exit-node" "--advertise-exit-node"
"--advertise-routes=${advRoutes}" "--advertise-routes=${advRoutes}"
+24 -13
View File
@@ -1,7 +1,7 @@
{ lib, ... }: { lib, ... }:
let let
inherit (lib.my) net; inherit (lib.my) net;
inherit (lib.my.c.colony) domain prefixes firewallForwards; inherit (lib.my.c.colony) domain prefixes custRouting firewallForwards;
in in
{ {
imports = [ ./vms ]; imports = [ ./vms ];
@@ -60,8 +60,8 @@ in
kernelPackages = (lib.my.c.kernel.lts pkgs).extend (self: super: { kernelPackages = (lib.my.c.kernel.lts pkgs).extend (self: super: {
kernel = super.kernel.override { kernel = super.kernel.override {
structuredExtraConfig = with lib.kernel; { structuredExtraConfig = with lib.kernel; {
#SOME_OPT = yes; ACPI_APEI_PCIEAER = yes;
#A_MOD = module; PCIEAER = yes;
}; };
}; };
}); });
@@ -150,12 +150,12 @@ in
"serial-getty@ttyS1".enable = true; "serial-getty@ttyS1".enable = true;
lvm-activate-main = { lvm-activate-main = {
description = "Activate remaining LVs"; description = "Activate remaining LVs";
before = [ "local-fs-pre.target" ]; unitConfig.DefaultDependencies = false;
serviceConfig = { serviceConfig = {
Type = "oneshot"; Type = "oneshot";
ExecStart = "${pkgs.lvm2.bin}/bin/vgchange -aay main"; ExecStart = "${pkgs.lvm2.bin}/bin/vgchange -aay main";
}; };
wantedBy = [ "sysinit.target" ]; wantedBy = [ "local-fs-pre.target" ];
}; };
rsync-lvm-meta = { rsync-lvm-meta = {
@@ -252,10 +252,10 @@ in
}; };
ipv6Prefixes = [ ipv6Prefixes = [
{ {
ipv6PrefixConfig.Prefix = prefixes.vms.v6; Prefix = prefixes.vms.v6;
} }
]; ];
routes = map (r: { routeConfig = r; }) [ routes = [
{ {
Destination = prefixes.ctrs.v4; Destination = prefixes.ctrs.v4;
Gateway = allAssignments.shill.routing.ipv4.address; Gateway = allAssignments.shill.routing.ipv4.address;
@@ -264,10 +264,12 @@ in
Destination = prefixes.ctrs.v6; Destination = prefixes.ctrs.v6;
Gateway = allAssignments.shill.internal.ipv6.address; Gateway = allAssignments.shill.internal.ipv6.address;
} }
{ {
Destination = allAssignments.shill.internal.ipv4.address; Destination = allAssignments.shill.internal.ipv4.address;
Gateway = allAssignments.shill.routing.ipv4.address; Gateway = allAssignments.shill.routing.ipv4.address;
} }
{ {
Destination = lib.my.c.tailscale.prefix.v4; Destination = lib.my.c.tailscale.prefix.v4;
Gateway = allAssignments.shill.routing.ipv4.address; Gateway = allAssignments.shill.routing.ipv4.address;
@@ -276,6 +278,15 @@ in
Destination = lib.my.c.tailscale.prefix.v6; Destination = lib.my.c.tailscale.prefix.v6;
Gateway = allAssignments.shill.internal.ipv6.address; Gateway = allAssignments.shill.internal.ipv6.address;
} }
{
Destination = prefixes.qclk.v4;
Gateway = allAssignments.shill.routing.ipv4.address;
}
{
Destination = prefixes.jam.v6;
Gateway = allAssignments.shill.internal.ipv6.address;
}
{ {
Destination = prefixes.oci.v4; Destination = prefixes.oci.v4;
@@ -307,7 +318,7 @@ in
"90-vm-mail" = { "90-vm-mail" = {
matchConfig.Name = "vm-mail"; matchConfig.Name = "vm-mail";
address = [ address = [
(net.cidr.subnet 8 1 prefixes.cust.v4) "${custRouting.mail-vm}/32"
prefixes.mail.v6 prefixes.mail.v6
]; ];
networkConfig = { networkConfig = {
@@ -316,10 +327,10 @@ in
}; };
ipv6Prefixes = [ ipv6Prefixes = [
{ {
ipv6PrefixConfig.Prefix = prefixes.mail.v6; Prefix = prefixes.mail.v6;
} }
]; ];
routes = map (r: { routeConfig = r; }) [ routes = [
{ {
Destination = prefixes.mail.v4; Destination = prefixes.mail.v4;
Scope = "link"; Scope = "link";
@@ -330,7 +341,7 @@ in
"90-vm-darts" = { "90-vm-darts" = {
matchConfig.Name = "vm-darts"; matchConfig.Name = "vm-darts";
address = [ address = [
(net.cidr.subnet 8 2 prefixes.cust.v4) "${custRouting.darts-vm}/32"
prefixes.darts.v6 prefixes.darts.v6
]; ];
networkConfig = { networkConfig = {
@@ -339,10 +350,10 @@ in
}; };
ipv6Prefixes = [ ipv6Prefixes = [
{ {
ipv6PrefixConfig.Prefix = prefixes.darts.v6; Prefix = prefixes.darts.v6;
} }
]; ];
routes = map (r: { routeConfig = r; }) [ routes = [
{ {
Destination = prefixes.darts.v4; Destination = prefixes.darts.v4;
Scope = "link"; Scope = "link";
+6 -1
View File
@@ -29,6 +29,9 @@
}; };
in in
{ {
# Kernel Same-Page Merging to attempt memory usage reduction
hardware.ksm.enable = false;
systemd = { systemd = {
network = { network = {
links = { links = {
@@ -130,7 +133,8 @@
(vm.lvmDisk "media") (vm.lvmDisk "media")
(vm.lvmDisk "minio") (vm.lvmDisk "minio")
(vm.lvmDisk "nix-atticd") (vm.lvmDisk "nix-cache")
(vm.lvmDisk "jam")
]); ]);
}; };
@@ -209,6 +213,7 @@
drives = [ drives = [
(mkMerge [ (vm.disk "darts" "root") { frontendOpts.bootindex = 0; } ]) (mkMerge [ (vm.disk "darts" "root") { frontendOpts.bootindex = 0; } ])
(vm.lvmDisk' "media" "darts-media") (vm.lvmDisk' "media" "darts-media")
(vm.lvmDisk' "ext" "darts-ext")
]; ];
}; };
}; };
+83 -53
View File
@@ -8,8 +8,9 @@ in
{ {
config = { config = {
services = { services = {
bird2 = { bird = {
enable = true; enable = true;
package = pkgs.bird2;
# TODO: Clean up and modularise # TODO: Clean up and modularise
config = '' config = ''
define OWNAS = 211024; define OWNAS = 211024;
@@ -232,59 +233,88 @@ in
neighbor 2a07:cd40:1::9 as 202413; neighbor 2a07:cd40:1::9 as 202413;
} }
protocol bgp ixp4_frysix_rs1 from ixp_bgp4 { protocol bgp ixp4_frysix_rs3 from ixp_bgp4 {
description "Frys-IX route server 1 (IPv4)"; description "Frys-IX route server 3 (IPv4)";
neighbor 185.1.203.253 as 56393; neighbor 185.1.160.255 as 56393;
} }
protocol bgp ixp6_frysix_rs1 from ixp_bgp6 { protocol bgp ixp6_frysix_rs3 from ixp_bgp6 {
description "Frys-IX route server 1 (IPv6)"; description "Frys-IX route server 3 (IPv6)";
neighbor 2001:7f8:10f::dc49:253 as 56393; neighbor 2001:7f8:10f::dc49:1 as 56393;
} }
protocol bgp ixp4_frysix_rs2 from ixp_bgp4 { protocol bgp ixp4_frysix_rs4 from ixp_bgp4 {
description "Frys-IX route server 2 (IPv4)"; description "Frys-IX route server 4 (IPv4)";
neighbor 185.1.203.254 as 56393; neighbor 185.1.161.0 as 56393;
} }
protocol bgp ixp6_frysix_rs2 from ixp_bgp6 { protocol bgp ixp6_frysix_rs4 from ixp_bgp6 {
description "Frys-IX route server 2 (IPv6)"; description "Frys-IX route server 4 (IPv6)";
neighbor 2001:7f8:10f::dc49:254 as 56393; neighbor 2001:7f8:10f::dc49:2 as 56393;
} }
protocol bgp peer4_frysix_luje from peer_bgp4 { protocol bgp peer4_frysix_luje from peer_bgp4 {
description "LUJE.net (on Frys-IX, IPv4)"; description "LUJE.net (on Frys-IX, IPv4)";
neighbor 185.1.203.152 as 212855; neighbor 185.1.160.152 as 212855;
} }
protocol bgp peer6_frysix_luje from peer_bgp6 { protocol bgp peer6_frysix_luje from peer_bgp6 {
description "LUJE.net (on Frys-IX, IPv6)"; description "LUJE.net (on Frys-IX, IPv6)";
neighbor 2001:7f8:10f::3:3f95:152 as 212855; neighbor 2001:7f8:10f::3:3f95:152 as 212855;
} }
protocol bgp peer4_frysix_he from peer_bgp4 { protocol bgp peer4_frysix_he from peer_bgp4 {
description "Hurricane Electric (on Frys-IX, IPv4)"; description "Hurricane Electric (on Frys-IX, IPv4)";
neighbor 185.1.203.154 as 6939; neighbor 185.1.160.154 as 6939;
} }
protocol bgp peer4_frysix_cloudflare from peer_bgp4 {
description "Cloudflare (on Frys-IX, IPv4)"; protocol bgp peer4_frysix_cloudflare1 from peer_bgp4 {
neighbor 185.1.203.217 as 13335; description "Cloudflare 1 (on Frys-IX, IPv4)";
neighbor 185.1.160.217 as 13335;
} }
protocol bgp peer6_frysix_cloudflare from peer_bgp6 { protocol bgp peer4_frysix_cloudflare2 from peer_bgp4 {
description "Cloudflare (on Frys-IX, IPv6)"; description "Cloudflare 2 (on Frys-IX, IPv4)";
neighbor 185.1.160.109 as 13335;
}
protocol bgp peer6_frysix_cloudflare1 from peer_bgp6 {
description "Cloudflare 1 (on Frys-IX, IPv6)";
neighbor 2001:7f8:10f::3417:217 as 13335; neighbor 2001:7f8:10f::3417:217 as 13335;
} }
protocol bgp peer6_frysix_cloudflare2 from peer_bgp6 {
description "Cloudflare 2 (on Frys-IX, IPv6)";
neighbor 2001:7f8:10f::3417:109 as 13335;
}
protocol bgp peer4_frysix_jurrian from peer_bgp4 { protocol bgp peer4_frysix_jurrian from peer_bgp4 {
description "AS212635 aka jurrian (on Frys-IX, IPv4)"; description "AS212635 aka jurrian (on Frys-IX, IPv4)";
neighbor 185.1.203.134 as 212635; neighbor 185.1.160.134 as 212635;
} }
protocol bgp peer6_frysix_jurrian from peer_bgp6 { protocol bgp peer6_frysix_jurrian from peer_bgp6 {
description "AS212635 aka jurrian (on Frys-IX, IPv6)"; description "AS212635 aka jurrian (on Frys-IX, IPv6)";
neighbor 2001:7f8:10f::3:3e9b:134 as 212635; neighbor 2001:7f8:10f::3:3e9b:134 as 212635;
} }
protocol bgp peer4_nlix_meta1 from peer_bgp4 {
description "Meta 1 (on NL-ix, IPv4)";
neighbor 193.239.116.147 as 32934;
}
protocol bgp peer4_nlix_meta2 from peer_bgp4 {
description "Meta 2 (on NL-ix, IPv4)";
neighbor 193.239.116.148 as 32934;
}
protocol bgp peer6_nlix_meta1 from peer_bgp6 {
description "Meta 1 (on NL-ix, IPv6)";
neighbor 2001:7f8:13::a503:2934:1 as 32934;
}
protocol bgp peer6_nlix_meta2 from peer_bgp6 {
description "Meta 2 (on NL-ix, IPv6)";
neighbor 2001:7f8:13::a503:2934:2 as 32934;
}
protocol bgp peer4_frysix_meta1 from peer_bgp4 { protocol bgp peer4_frysix_meta1 from peer_bgp4 {
description "Meta 1 (on Frys-IX, IPv4)"; description "Meta 1 (on Frys-IX, IPv4)";
neighbor 185.1.203.225 as 32934; neighbor 185.1.160.225 as 32934;
} }
protocol bgp peer4_frysix_meta2 from peer_bgp4 { protocol bgp peer4_frysix_meta2 from peer_bgp4 {
description "Meta 2 (on Frys-IX, IPv4)"; description "Meta 2 (on Frys-IX, IPv4)";
neighbor 185.1.203.226 as 32934; neighbor 185.1.160.226 as 32934;
} }
protocol bgp peer6_frysix_meta1 from peer_bgp6 { protocol bgp peer6_frysix_meta1 from peer_bgp6 {
description "Meta 1 (on Frys-IX, IPv6)"; description "Meta 1 (on Frys-IX, IPv6)";
@@ -317,36 +347,36 @@ in
ipv6 { preference (PREFIXP-1); }; ipv6 { preference (PREFIXP-1); };
} }
protocol bgp peer4_nlix_cloudflare1 from peer_bgp4 { # protocol bgp peer4_nlix_cloudflare1 from peer_bgp4 {
description "Cloudflare NL-ix 1 (IPv4)"; # description "Cloudflare NL-ix 1 (IPv4)";
neighbor 193.239.117.14 as 13335; # neighbor 193.239.117.14 as 13335;
ipv4 { preference (PREFPEER-1); }; # ipv4 { preference (PREFPEER-1); };
} # }
protocol bgp peer4_nlix_cloudflare2 from peer_bgp4 { # protocol bgp peer4_nlix_cloudflare2 from peer_bgp4 {
description "Cloudflare NL-ix 2 (IPv4)"; # description "Cloudflare NL-ix 2 (IPv4)";
neighbor 193.239.117.114 as 13335; # neighbor 193.239.117.114 as 13335;
ipv4 { preference (PREFPEER-1); }; # ipv4 { preference (PREFPEER-1); };
} # }
protocol bgp peer4_nlix_cloudflare3 from peer_bgp4 { # protocol bgp peer4_nlix_cloudflare3 from peer_bgp4 {
description "Cloudflare NL-ix 3 (IPv4)"; # description "Cloudflare NL-ix 3 (IPv4)";
neighbor 193.239.118.138 as 13335; # neighbor 193.239.118.138 as 13335;
ipv4 { preference (PREFPEER-1); }; # ipv4 { preference (PREFPEER-1); };
} # }
protocol bgp peer6_nlix_cloudflare1 from peer_bgp6 { # protocol bgp peer6_nlix_cloudflare1 from peer_bgp6 {
description "Cloudflare NL-ix 1 (IPv6)"; # description "Cloudflare NL-ix 1 (IPv6)";
neighbor 2001:7f8:13::a501:3335:1 as 13335; # neighbor 2001:7f8:13::a501:3335:1 as 13335;
ipv6 { preference (PREFPEER-1); }; # ipv6 { preference (PREFPEER-1); };
} # }
protocol bgp peer6_nlix_cloudflare2 from peer_bgp6 { # protocol bgp peer6_nlix_cloudflare2 from peer_bgp6 {
description "Cloudflare NL-ix 2 (IPv6)"; # description "Cloudflare NL-ix 2 (IPv6)";
neighbor 2001:7f8:13::a501:3335:2 as 13335; # neighbor 2001:7f8:13::a501:3335:2 as 13335;
ipv6 { preference (PREFPEER-1); }; # ipv6 { preference (PREFPEER-1); };
} # }
protocol bgp peer6_nlix_cloudflare3 from peer_bgp6 { # protocol bgp peer6_nlix_cloudflare3 from peer_bgp6 {
description "Cloudflare NL-ix 3 (IPv6)"; # description "Cloudflare NL-ix 3 (IPv6)";
neighbor 2001:7f8:13::a501:3335:3 as 13335; # neighbor 2001:7f8:13::a501:3335:3 as 13335;
ipv6 { preference (PREFPEER-1); }; # ipv6 { preference (PREFPEER-1); };
} # }
protocol bgp peer4_nlix_jurrian from peer_bgp4 { protocol bgp peer4_nlix_jurrian from peer_bgp4 {
description "AS212635 aka jurrian (on NL-ix, IPv4)"; description "AS212635 aka jurrian (on NL-ix, IPv4)";
neighbor 193.239.117.55 as 212635; neighbor 193.239.117.55 as 212635;
+99 -16
View File
@@ -9,6 +9,7 @@ in
vpns = { vpns = {
l2 = { l2 = {
as211024 = { as211024 = {
udpEncapsulation = true;
vni = 211024; vni = 211024;
security.enable = true; security.enable = true;
peers = { peers = {
@@ -103,11 +104,9 @@ in
lvm = { lvm = {
dmeventd.enable = true; dmeventd.enable = true;
}; };
resolved = { resolved.settings.Resolve = {
llmnr = "false"; LLMNR = false;
extraConfig = '' MulticastDNS = false;
MulticastDNS=false
'';
}; };
netdata.enable = true; netdata.enable = true;
@@ -163,12 +162,48 @@ in
}; };
wireguardPeers = [ wireguardPeers = [
{ {
wireguardPeerConfig = {
PublicKey = "7N9YdQaCMWWIwAnW37vrthm9ZpbnG4Lx3gheHeRYz2E="; PublicKey = "7N9YdQaCMWWIwAnW37vrthm9ZpbnG4Lx3gheHeRYz2E=";
AllowedIPs = [ allAssignments.kelder.estuary.ipv4.address ]; AllowedIPs = [ allAssignments.kelder.estuary.ipv4.address ];
PersistentKeepalive = 25; PersistentKeepalive = 25;
}
];
}; };
} }
{
"30-hillcrest" = {
netdevConfig = {
Name = "hillcrest";
Kind = "wireguard";
};
wireguardConfig = {
PrivateKeyFile = config.age.secrets."estuary/hillcrest-wg.key".path;
ListenPort = lib.my.c.hillcrest.vpn.port;
};
wireguardPeers = [
{
PublicKey = "+67Ks+ZRk1ssNCfg5BFKmIE9NtLasAxRE6XMqufx5GY=";
AllowedIPs = [ (net.cidr.host 2 prefixes.hillcrest.v4) ];
PersistentKeepalive = 25;
}
];
};
}
{
"30-john-valorant" = {
netdevConfig = {
Name = "john-valorant";
Kind = "wireguard";
};
wireguardConfig = {
PrivateKeyFile = config.age.secrets."estuary/john-valorant-wg.key".path;
ListenPort = lib.my.c.john-valorant.vpn.port;
};
wireguardPeers = [
{
PublicKey = "xyqKF0yOAv1bObN1paL2vATFh77pdFfvN+JmuAxaTCk=";
AllowedIPs = [ (net.cidr.host 2 prefixes.john-valorant.v4) ];
PersistentKeepalive = 25;
}
]; ];
}; };
} }
@@ -219,7 +254,7 @@ in
in in
mkMerge mkMerge
[ [
(mkIXPConfig "frys-ix" "185.1.203.196/24" "2001:7f8:10f::3:3850:196/64") (mkIXPConfig "frys-ix" "185.1.160.196/23" "2001:7f8:10f::3:3850:196/64")
(mkIXPConfig "nl-ix" "193.239.116.145/22" "2001:7f8:13::a521:1024:1/64") (mkIXPConfig "nl-ix" "193.239.116.145/22" "2001:7f8:13::a521:1024:1/64")
(mkIXPConfig "fogixp" "185.1.147.159/24" "2001:7f8:ca:1::159/64") (mkIXPConfig "fogixp" "185.1.147.159/24" "2001:7f8:ca:1::159/64")
{ {
@@ -277,11 +312,10 @@ in
}; };
ipv6Prefixes = [ ipv6Prefixes = [
{ {
ipv6PrefixConfig.Prefix = prefixes.base.v6; Prefix = prefixes.base.v6;
} }
]; ];
routes = map (r: { routeConfig = r; }) (flatten routes = flatten ([
([
{ {
Destination = prefixes.vip1; Destination = prefixes.vip1;
Gateway = allAssignments.colony.routing.ipv4.address; Gateway = allAssignments.colony.routing.ipv4.address;
@@ -307,6 +341,11 @@ in
Destination = lib.my.c.tailscale.prefix.v6; Destination = lib.my.c.tailscale.prefix.v6;
Gateway = allAssignments.colony.internal.ipv6.address; Gateway = allAssignments.colony.internal.ipv6.address;
} }
{
Destination = prefixes.qclk.v4;
Gateway = allAssignments.colony.routing.ipv4.address;
}
] ++ ] ++
(map (pName: [ (map (pName: [
{ {
@@ -317,7 +356,7 @@ in
Destination = prefixes."${pName}".v6; Destination = prefixes."${pName}".v6;
Gateway = allAssignments.colony.internal.ipv6.address; Gateway = allAssignments.colony.internal.ipv6.address;
} }
]) [ "vms" "ctrs" "oci" ]))); ]) [ "vms" "ctrs" "oci" ]));
} }
]; ];
@@ -326,7 +365,7 @@ in
{ {
matchConfig.Name = "as211024"; matchConfig.Name = "as211024";
networkConfig.IPv6AcceptRA = mkForce false; networkConfig.IPv6AcceptRA = mkForce false;
routes = map (r: { routeConfig = r; }) [ routes = [
{ {
Destination = lib.my.c.home.prefixes.all.v4; Destination = lib.my.c.home.prefixes.all.v4;
Gateway = lib.my.c.home.vips.as211024.v4; Gateway = lib.my.c.home.vips.as211024.v4;
@@ -338,10 +377,28 @@ in
matchConfig.Name = "kelder"; matchConfig.Name = "kelder";
routes = [ routes = [
{ {
routeConfig = {
Destination = allAssignments.kelder.estuary.ipv4.address; Destination = allAssignments.kelder.estuary.ipv4.address;
Scope = "link"; Scope = "link";
}
];
}; };
"95-hillcrest" = {
matchConfig.Name = "hillcrest";
address = [ "${net.cidr.host 1 prefixes.hillcrest.v4}/32" ];
routes = [
{
Destination = net.cidr.host 2 prefixes.hillcrest.v4;
Scope = "link";
}
];
};
"95-john-valorant" = {
matchConfig.Name = "john-valorant";
address = [ "${net.cidr.host 1 prefixes.john-valorant.v4}/32" ];
routes = [
{
Destination = net.cidr.host 2 prefixes.john-valorant.v4;
Scope = "link";
} }
]; ];
}; };
@@ -355,6 +412,12 @@ in
"estuary/kelder-wg.key" = { "estuary/kelder-wg.key" = {
owner = "systemd-network"; owner = "systemd-network";
}; };
"estuary/hillcrest-wg.key" = {
owner = "systemd-network";
};
"estuary/john-valorant-wg.key" = {
owner = "systemd-network";
};
"l2mesh/as211024.key" = {}; "l2mesh/as211024.key" = {};
}; };
}; };
@@ -366,7 +429,13 @@ in
}; };
}; };
firewall = { firewall = {
udp.allowed = [ 5353 lib.my.c.kelder.vpn.port ]; udp.allowed = [
5353
lib.my.c.kelder.vpn.port
lib.my.c.hillcrest.vpn.port
lib.my.c.john-valorant.vpn.port
];
tcp.allowed = [ 5353 "bgp" ]; tcp.allowed = [ 5353 "bgp" ];
nat = { nat = {
enable = true; enable = true;
@@ -393,16 +462,28 @@ in
# Safe enough to allow all SSH # Safe enough to allow all SSH
tcp dport ssh accept tcp dport ssh accept
# jam-ctr forwards
ip daddr ${aa.shill.internal.ipv4.address} tcp dport 60022 accept
ip6 daddr ${aa.middleman.internal.ipv6.address} tcp dport { http, https, 8448 } accept ip6 daddr ${aa.middleman.internal.ipv6.address} tcp dport { http, https, 8448 } accept
${matchInet "tcp dport { http, https } accept" "git"} ${matchInet "tcp dport { http, https } accept" "git"}
ip6 daddr ${aa.simpcraft-oci.internal.ipv6.address} tcp dport { 25565, 25575 } accept ip6 daddr ${aa.simpcraft-oci.internal.ipv6.address} tcp dport 25565 accept
ip6 daddr ${aa.simpcraft-staging-oci.internal.ipv6.address} tcp dport 25565 accept ip6 daddr ${aa.simpcraft-staging-oci.internal.ipv6.address} tcp dport 25565 accept
ip6 daddr ${aa.kevcraft-oci.internal.ipv6.address} tcp dport 25567 accept
ip6 daddr ${aa.kinkcraft-oci.internal.ipv6.address} tcp dport 25568 accept
ip6 daddr ${aa.graeme-oci.internal.ipv6.address} tcp dport 25569 accept
ip6 daddr ${aa.gam.internal.ipv6.address} tcp dport 7777 accept
return return
} }
chain routing-udp { chain routing-udp {
ip6 daddr ${aa.valheim-oci.internal.ipv6.address} udp dport { 2456-2457 } accept ip6 daddr ${aa.valheim-oci.internal.ipv6.address} udp dport { 2456-2457 } accept
ip6 daddr ${aa.waffletail.internal.ipv6.address} udp dport 41641 accept ip6 daddr ${aa.waffletail.internal.ipv6.address} udp dport 41641 accept
ip6 daddr ${aa.simpcraft-oci.internal.ipv6.address} udp dport 25565 accept ip6 daddr ${aa.simpcraft-oci.internal.ipv6.address} udp dport 25565 accept
ip6 daddr ${aa.enshrouded-oci.internal.ipv6.address} udp dport { 15636-15637 } accept
ip6 daddr ${aa.kevcraft-oci.internal.ipv6.address} udp dport 25567 accept
ip6 daddr ${aa.kinkcraft-oci.internal.ipv6.address} udp dport 25568 accept
ip6 daddr ${aa.graeme-oci.internal.ipv6.address} udp dport 25569 accept
ip6 daddr ${aa.gam.internal.ipv6.address} udp dport 7777 accept
return return
} }
chain filter-routing { chain filter-routing {
@@ -423,7 +504,7 @@ in
iifname { wan, as211024, $ixps } oifname base jump filter-routing iifname { wan, as211024, $ixps } oifname base jump filter-routing
oifname $ixps jump ixp oifname $ixps jump ixp
iifname base oifname { base, wan, $ixps } accept iifname base oifname { base, wan, $ixps } accept
oifname { as211024, kelder } accept oifname { as211024, kelder, hillcrest, john-valorant } accept
} }
chain output { chain output {
oifname ifog ether type != vlan reject oifname ifog ether type != vlan reject
@@ -435,6 +516,8 @@ in
${matchInet "meta l4proto { udp, tcp } th dport domain redirect to :5353" "estuary"} ${matchInet "meta l4proto { udp, tcp } th dport domain redirect to :5353" "estuary"}
} }
chain postrouting { chain postrouting {
oifname hillcrest snat ip to ${net.cidr.host 1 prefixes.hillcrest.v4}
oifname john-valorant snat ip to ${net.cidr.host 1 prefixes.john-valorant.v4}
ip saddr ${prefixes.all.v4} oifname != as211024 snat to ${assignments.internal.ipv4.address} ip saddr ${prefixes.all.v4} oifname != as211024 snat to ${assignments.internal.ipv4.address}
} }
} }
+43 -17
View File
@@ -2,7 +2,7 @@
let let
inherit (builtins) attrNames; inherit (builtins) attrNames;
inherit (lib.my) net; inherit (lib.my) net;
inherit (lib.my.c.colony) prefixes; inherit (lib.my.c.colony) prefixes custRouting;
authZones = attrNames config.my.pdns.auth.bind.zones; authZones = attrNames config.my.pdns.auth.bind.zones;
in in
@@ -14,7 +14,7 @@ in
owner = "pdns"; owner = "pdns";
group = "pdns"; group = "pdns";
}; };
"estuary/pdns/recursor.conf" = { "estuary/pdns/recursor.yml" = {
owner = "pdns-recursor"; owner = "pdns-recursor";
group = "pdns-recursor"; group = "pdns-recursor";
}; };
@@ -31,7 +31,7 @@ in
pdns.recursor = { pdns.recursor = {
enable = true; enable = true;
extraSettingsFile = config.age.secrets."estuary/pdns/recursor.conf".path; extraSettingsFile = config.age.secrets."estuary/pdns/recursor.yml".path;
}; };
}; };
@@ -44,34 +44,37 @@ in
}; };
pdns-recursor = { pdns-recursor = {
dns = { settings = {
address = [ incoming = {
listen = [
"127.0.0.1" "::1" "127.0.0.1" "::1"
assignments.base.ipv4.address assignments.base.ipv6.address assignments.base.ipv4.address assignments.base.ipv6.address
]; ];
allowFrom = [ allow_from = [
"127.0.0.0/8" "::1/128" "127.0.0.0/8" "::1/128"
prefixes.all.v4 prefixes.all.v6 prefixes.all.v4 prefixes.all.v6
] ++ (with lib.my.c.tailscale.prefix; [ v4 v6 ]); ] ++ (with lib.my.c.tailscale.prefix; [ v4 v6 ]);
# DNS NOTIFY messages override TTL
allow_notify_for = authZones;
allow_notify_from = [ "127.0.0.0/8" "::1/128" ];
}; };
settings = { outgoing = {
query-local-address = [ source_address = [
assignments.internal.ipv4.address assignments.internal.ipv4.address
assignments.internal.ipv6.address assignments.internal.ipv6.address
assignments.base.ipv6.address assignments.base.ipv6.address
]; ];
forward-zones = map (z: "${z}=127.0.0.1:5353") authZones; };
# DNS NOTIFY messages override TTL recursor = {
allow-notify-for = authZones; forward_zones = map (z: {
allow-notify-from = [ "127.0.0.0/8" "::1/128" ]; zone = z;
forwarders = [ "127.0.0.1:5353" ];
}) authZones;
webserver = true; lua_dns_script = pkgs.writeText "pdns-script.lua" ''
webserver-address = "::";
webserver-allow-from = [ "127.0.0.1" "::1" ];
lua-dns-script = pkgs.writeText "pdns-script.lua" ''
function preresolve(dq) function preresolve(dq)
if dq.qname:equal("nix-cache.nul.ie") then if dq.qname:equal("nix-cache.nul.ie") then
dq:addAnswer(pdns.CNAME, "http.${config.networking.domain}.") dq:addAnswer(pdns.CNAME, "http.${config.networking.domain}.")
@@ -84,6 +87,13 @@ in
end end
''; '';
}; };
webservice = {
webserver = true;
address = "::";
allow_from = [ "127.0.0.1" "::1" ];
};
};
}; };
}; };
@@ -153,6 +163,15 @@ in
simpcraft IN AAAA ${allAssignments.simpcraft-oci.internal.ipv6.address} simpcraft IN AAAA ${allAssignments.simpcraft-oci.internal.ipv6.address}
simpcraft-staging IN A ${assignments.internal.ipv4.address} simpcraft-staging IN A ${assignments.internal.ipv4.address}
simpcraft-staging IN AAAA ${allAssignments.simpcraft-staging-oci.internal.ipv6.address} simpcraft-staging IN AAAA ${allAssignments.simpcraft-staging-oci.internal.ipv6.address}
enshrouded IN A ${assignments.internal.ipv4.address}
kevcraft IN A ${assignments.internal.ipv4.address}
kevcraft IN AAAA ${allAssignments.kevcraft-oci.internal.ipv6.address}
kinkcraft IN A ${assignments.internal.ipv4.address}
kinkcraft IN AAAA ${allAssignments.kinkcraft-oci.internal.ipv6.address}
graeme IN A ${assignments.internal.ipv4.address}
graeme IN AAAA ${allAssignments.graeme-oci.internal.ipv6.address}
terraria IN A ${assignments.internal.ipv4.address}
terraria IN AAAA ${allAssignments.gam.internal.ipv6.address}
mail-vm IN A ${net.cidr.host 0 prefixes.mail.v4} mail-vm IN A ${net.cidr.host 0 prefixes.mail.v4}
mail-vm IN AAAA ${net.cidr.host 1 prefixes.mail.v6} mail-vm IN AAAA ${net.cidr.host 1 prefixes.mail.v6}
@@ -162,6 +181,13 @@ in
andrey-cust IN A ${allAssignments.kelder.estuary.ipv4.address} andrey-cust IN A ${allAssignments.kelder.estuary.ipv4.address}
jam-cust IN A ${net.cidr.host 0 prefixes.jam.v4}
jam-fwd IN A ${allAssignments.shill.internal.ipv4.address}
jam-cust IN AAAA ${net.cidr.host 1 prefixes.jam.v6}
hillcrest-tun IN A ${net.cidr.host 2 prefixes.hillcrest.v4}
john-valorant-tun IN A ${net.cidr.host 2 prefixes.john-valorant.v4}
$TTL 3 $TTL 3
_acme-challenge IN LUA TXT @@FILE@@ _acme-challenge IN LUA TXT @@FILE@@
+3 -36
View File
@@ -4,7 +4,7 @@ let
inherit (lib) mkMerge mkDefault; inherit (lib) mkMerge mkDefault;
inherit (lib.my) net; inherit (lib.my) net;
inherit (lib.my.c) pubDomain; inherit (lib.my.c) pubDomain;
inherit (lib.my.c.colony) domain prefixes; inherit (lib.my.c.colony) domain prefixes firewallForwards;
inherit (lib.my.c.nginx) baseHttpConfig proxyHeaders; inherit (lib.my.c.nginx) baseHttpConfig proxyHeaders;
in in
{ {
@@ -95,41 +95,13 @@ in
"*.${pubDomain}" "*.${pubDomain}"
]; ];
dnsProvider = "cloudflare"; dnsProvider = "cloudflare";
credentialsFile = config.age.secrets."middleman/cloudflare-credentials.conf".path; environmentFile = config.age.secrets."middleman/cloudflare-credentials.conf".path;
}; };
}; };
}; };
services = { services = {
fstrim = lib.my.c.colony.fstrimConfig; fstrim = lib.my.c.colony.fstrimConfig;
# Hacks for Jsch (Minecraft FastBack) to work
openssh = {
hostKeys = [
{
bits = 4096;
path = "/etc/ssh/ssh_host_rsa_key";
type = "rsa";
}
{
path = "/etc/ssh/ssh_host_ed25519_key";
type = "ed25519";
}
{
type = "ecdsa-sha2-nistp256";
path = "/etc/ssh/ssh_host_ecdsa_key";
}
];
settings = {
Macs = [
"hmac-sha2-512-etm@openssh.com"
"hmac-sha2-256-etm@openssh.com"
"umac-128-etm@openssh.com"
"hmac-sha2-256"
];
};
};
netdata.enable = true; netdata.enable = true;
nginx = { nginx = {
enable = true; enable = true;
@@ -139,7 +111,6 @@ in
recommendedTlsSettings = true; recommendedTlsSettings = true;
clientMaxBodySize = "0"; clientMaxBodySize = "0";
serverTokens = true; serverTokens = true;
sslDhparam = config.age.secrets."dhparams.pem".path;
# Based on recommended*Settings, but probably better to be explicit about these # Based on recommended*Settings, but probably better to be explicit about these
appendHttpConfig = '' appendHttpConfig = ''
@@ -210,11 +181,6 @@ in
secrets = { secrets = {
key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIP+KINpHLMduBuW96JzfSRDLUzkI+XaCBghu5/wHiW5R"; key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIP+KINpHLMduBuW96JzfSRDLUzkI+XaCBghu5/wHiW5R";
files = { files = {
"dhparams.pem" = {
owner = "acme";
group = "acme";
mode = "440";
};
"middleman/cloudflare-credentials.conf" = { "middleman/cloudflare-credentials.conf" = {
owner = "acme"; owner = "acme";
group = "acme"; group = "acme";
@@ -225,6 +191,7 @@ in
firewall = { firewall = {
tcp.allowed = [ 19999 "http" "https" ]; tcp.allowed = [ 19999 "http" "https" ];
nat.forwardPorts."${allAssignments.estuary.internal.ipv4.address}" = firewallForwards allAssignments;
extraRules = '' extraRules = ''
table inet filter { table inet filter {
chain forward { chain forward {
+15 -15
View File
@@ -1,19 +1,7 @@
{ lib, pkgs, config, ... }: { lib, pkgs, config, ... }:
let let
inherit (builtins) toJSON;
inherit (lib) mkForce; inherit (lib) mkForce;
inherit (lib.my.c) pubDomain; inherit (lib.my.c) pubDomain;
cfgFile = pkgs.writeText "gitea-actions-runner.yaml" (toJSON {
container = {
network = "podman";
privileged = true;
};
cache = {
enabled = true;
dir = "/var/cache/gitea-runner";
};
});
in in
{ {
config = { config = {
@@ -30,11 +18,24 @@ in
enable = true; enable = true;
name = "main-docker"; name = "main-docker";
labels = [ labels = [
"debian-node-bullseye:docker://node:18-bullseye" "debian-node-trixie:docker://node:24-trixie"
"ubuntu-22.04:docker://git.nul.ie/dev/actions-ubuntu:22.04" "ubuntu-26.04:docker://git.nul.ie/dev/actions-ubuntu:26.04"
]; ];
url = "https://git.${pubDomain}"; url = "https://git.${pubDomain}";
tokenFile = config.age.secrets."gitea/actions-runner.env".path; tokenFile = config.age.secrets."gitea/actions-runner.env".path;
settings = {
runner = {
timeout = "8h";
};
container = {
network = "podman";
privileged = true;
};
cache = {
enabled = true;
dir = "/var/cache/gitea-runner";
};
};
}; };
}; };
}; };
@@ -61,7 +62,6 @@ in
DynamicUser = mkForce false; DynamicUser = mkForce false;
User = "gitea-runner"; User = "gitea-runner";
Group = "gitea-runner"; Group = "gitea-runner";
ExecStart = mkForce "${config.services.gitea-actions-runner.package}/bin/act_runner -c ${cfgFile} daemon";
}; };
}; };
}; };
@@ -0,0 +1,106 @@
{ lib, pkgs, assignments, ... }:
let
inherit (lib.my) net;
inherit (lib.my.c.colony) prefixes custRouting;
in
{
fileSystems = {
"/mnt/jam" = {
device = "/dev/disk/by-label/jam";
fsType = "ext4";
};
"/var/lib/machines/jam" = {
device = "/mnt/jam";
options = [ "bind" ];
fsType = "none";
};
};
systemd = {
nspawn = {
jam = {
enable = true;
execConfig = {
Boot = true;
PrivateUsers = "pick";
LinkJournal = false;
};
networkConfig = {
Private = true;
VirtualEthernet = true;
};
};
};
network.networks = {
"50-ve-jam" = {
matchConfig = {
Kind = "veth";
Name = "ve-jam";
};
address = [
custRouting.jam-ctr
prefixes.jam.v6
];
networkConfig = {
IPv6AcceptRA = false;
IPv6SendRA = true;
};
ipv6Prefixes = [
{
Prefix = prefixes.jam.v6;
}
];
routes = [
{
Destination = prefixes.jam.v4;
Scope = "link";
}
];
};
};
services = {
"systemd-nspawn@jam" = {
overrideStrategy = "asDropin";
serviceConfig = {
CPUQuota = "400%";
MemoryHigh = "infinity";
MemoryMax = "4G";
};
wantedBy = [ "machines.target" ];
};
};
};
my = {
firewall =
let
jamIP = net.cidr.host 0 prefixes.jam.v4;
in
{
nat.forwardPorts."${assignments.internal.ipv4.address}" = [
{
port = 60022;
dst = jamIP;
dstPort = "ssh";
}
];
extraRules = ''
table inet filter {
chain forward {
iifname { ve-jam } oifname vms accept
iifname vms oifname { ve-jam } accept
}
}
table inet nat {
chain postrouting {
ip saddr ${jamIP} snat to ${assignments.internal.ipv4.address}
}
}
'';
};
};
}
@@ -24,7 +24,7 @@ in
configuration = { lib, pkgs, config, assignments, allAssignments, ... }: configuration = { lib, pkgs, config, assignments, allAssignments, ... }:
let let
inherit (lib) mkMerge mkIf mkForce; inherit (lib) genAttrs mkMerge mkIf mkForce;
inherit (lib.my) networkdAssignment; inherit (lib.my) networkdAssignment;
in in
{ {
@@ -45,9 +45,22 @@ in
owner = "matrix-synapse"; owner = "matrix-synapse";
group = "matrix-synapse"; group = "matrix-synapse";
}; };
"chatterbox/syncv3.env" = { "chatterbox/doublepuppet.yaml" = {
owner = "matrix-syncv3"; owner = "matrix-synapse";
group = "matrix-syncv3"; group = "matrix-synapse";
};
"chatterbox/mautrix-whatsapp.env" = {
owner = "mautrix-whatsapp";
group = "mautrix-whatsapp";
};
"chatterbox/mautrix-messenger.env" = {
owner = "mautrix-meta-messenger";
group = "mautrix-meta";
};
"chatterbox/mautrix-instagram.env" = {
owner = "mautrix-meta-instagram";
group = "mautrix-meta";
}; };
}; };
}; };
@@ -59,29 +72,24 @@ in
users = with lib.my.c.ids; { users = with lib.my.c.ids; {
users = { users = {
matrix-syncv3 = { matrix-synapse.extraGroups = [
isSystemUser = true; "mautrix-whatsapp"
uid = uids.matrix-syncv3; ];
group = "matrix-syncv3";
};
};
groups = {
matrix-syncv3.gid = gids.matrix-syncv3;
}; };
groups = { };
}; };
systemd = { systemd = {
network.networks."80-container-host0" = networkdAssignment "host0" assignments.internal; network.networks."80-container-host0" = networkdAssignment "host0" assignments.internal;
services = { services = { } // (genAttrs [ "mautrix-whatsapp" "mautrix-meta-messenger" "mautrix-meta-instagram" ] (_: {
matrix-sliding-sync.serviceConfig = { # ffmpeg needed to convert GIFs to video
# Needs to be able to read its secrets path = with pkgs; [ ffmpeg ];
DynamicUser = mkForce false; }));
User = "matrix-syncv3";
Group = "matrix-syncv3";
};
};
}; };
# TODO/FIXME: https://github.com/NixOS/nixpkgs/issues/336052
nixpkgs.config.permittedInsecurePackages = [ "olm-3.2.16" ];
services = { services = {
netdata.enable = true; netdata.enable = true;
matrix-synapse = { matrix-synapse = {
@@ -168,18 +176,10 @@ in
app_service_config_files = [ app_service_config_files = [
"/var/lib/heisenbridge/registration.yml" "/var/lib/heisenbridge/registration.yml"
config.age.secrets."chatterbox/doublepuppet.yaml".path
]; ];
}; };
sliding-sync = {
enable = true;
createDatabase = false;
environmentFile = config.age.secrets."chatterbox/syncv3.env".path;
settings = {
SYNCV3_BINDADDR = "[::]:8009";
SYNCV3_SERVER = "http://localhost:8008";
};
};
}; };
heisenbridge = { heisenbridge = {
@@ -195,6 +195,181 @@ in
]; ];
}; };
}; };
mautrix-whatsapp = {
enable = true;
# package = pkgs.mautrix-whatsapp.overrideAttrs (o: rec {
# version = "26.05";
# tag = "v0.2605.0";
# src = pkgs.fetchFromGitHub {
# owner = "mautrix";
# repo = "whatsapp";
# inherit tag;
# hash = "sha256-WlVfGQoP9e/wl98hUJei8O2JMcOKijoEY8XuU/z69Qk=";
# };
# vendorHash = "sha256-Hi/dZHJHoTTCnxLXgbkcYzuzis4fl5kxb5wMd9fKTY8=";
# });
environmentFile = config.age.secrets."chatterbox/mautrix-whatsapp.env".path;
settings = {
database = {
type = "postgres";
uri = "$MAU_WAPP_PSQL_URI";
};
homeserver = {
address = "http://localhost:8008";
domain = "nul.ie";
};
appservice = {
id = "whatsapp2";
bot = {
username = "whatsapp2";
displayname = "WhatsApp Bridge Bot";
};
username_template = "wapp2_{{.}}";
};
bridge = {
personal_filtering_spaces = true;
command_prefix = "!wa";
permissions = {
"@dev:nul.ie" = "admin";
};
};
double_puppet = {
secrets."nul.ie" = "$MAU_WAPP_DOUBLE_PUPPET_TOKEN";
};
encryption = {
allow = true;
default = true;
require = true;
pickle_key = "maunium.net/go/mautrix-whatsapp";
};
matrix = {
delivery_receipts = true;
};
network = {
displayname_template = ''{{or .BusinessName .PushName .FullName .Phone "Unknown user"}} (WA)'';
url_previews = true;
};
};
};
# mautrix-meta.package = pkgs.mautrix-meta.overrideAttrs (o: rec {
# version = "26.05.1";
# tag = "v0.2605.1";
# src = pkgs.fetchFromGitHub {
# owner = "mautrix";
# repo = "meta";
# inherit tag;
# hash = "sha256-zpolDtwGulDTiojJPnkj9O0D5b4rgPYQX6A28rvuvM0=";
# };
# vendorHash = "sha256-+i45bXBhlXPXX24VMS9IJLLX+i4VPnqy5RAH4j88sTA=";
# });
mautrix-meta.instances = {
messenger = {
enable = true;
registerToSynapse = true;
dataDir = "mautrix-messenger";
environmentFile = config.age.secrets."chatterbox/mautrix-messenger.env".path;
settings = {
database = {
type = "postgres";
uri = "$MAU_FBM_PSQL_URI";
};
homeserver = {
address = "http://localhost:8008";
domain = "nul.ie";
};
appservice = {
id = "fbm2";
bot = {
username = "messenger2";
displayname = "Messenger Bridge Bot";
avatar = "mxc://maunium.net/ygtkteZsXnGJLJHRchUwYWak";
};
username_template = "fbm2_{{.}}";
};
network = {
mode = "messenger";
displayname_template = ''{{or .DisplayName .Username "Unknown user"}} (FBM)'';
};
bridge = {
personal_filtering_spaces = true;
# management_room_text.welcome = "Hello, I'm a Messenger bridge bot.";
command_prefix = "!fbm";
backfill = {
enabled = true;
};
permissions = {
"@dev:nul.ie" = "admin";
};
};
double_puppet = {
secrets."nul.ie" = "$MAU_FBM_DOUBLE_PUPPET_TOKEN";
};
encryption = {
allow = true;
default = true;
require = true;
};
matrix = {
delivery_receipts = true;
};
};
};
instagram = {
enable = true;
registerToSynapse = true;
dataDir = "mautrix-instagram";
environmentFile = config.age.secrets."chatterbox/mautrix-instagram.env".path;
settings = {
database = {
type = "postgres";
uri = "$MAU_IG_PSQL_URI";
};
homeserver = {
address = "http://localhost:8008";
domain = "nul.ie";
};
appservice = {
id = "instagram";
bot = {
username = "instagram";
displayname = "Instagram Bridge Bot";
avatar = "mxc://maunium.net/JxjlbZUlCPULEeHZSwleUXQv";
};
username_template = "ig_{{.}}";
};
network = {
mode = "instagram";
displayname_template = ''{{or .DisplayName .Username "Unknown user"}} (IG)'';
};
bridge = {
personal_filtering_spaces = true;
# management_room_text.welcome = "Hello, I'm an Instagram bridge bot.";
command_prefix = "!ig";
backfill = {
enabled = true;
};
permissions = {
"@dev:nul.ie" = "admin";
"@adzerq:nul.ie" = "user";
};
};
double_puppet = {
secrets."nul.ie" = "$MAU_IG_DOUBLE_PUPPET_TOKEN";
};
encryption = {
allow = true;
default = true;
require = true;
};
matrix = {
delivery_receipts = true;
};
};
};
};
}; };
} }
(mkIf config.my.build.isDevVM { (mkIf config.my.build.isDevVM {
@@ -8,5 +8,7 @@
./object.nix ./object.nix
./toot.nix ./toot.nix
./waffletail.nix ./waffletail.nix
./qclk
./gam.nix
]; ];
} }
@@ -0,0 +1,72 @@
{ lib, ... }:
let
inherit (lib.my) net;
inherit (lib.my.c) pubDomain;
inherit (lib.my.c.colony) domain prefixes;
in
{
nixos.systems.gam = { config, ... }: {
system = "x86_64-linux";
nixpkgs = "mine";
rendered = config.configuration.config.my.asContainer;
assignments = {
internal = {
name = "gam-ctr";
inherit domain;
ipv4.address = net.cidr.host 11 prefixes.ctrs.v4;
ipv6 = {
iid = "::11";
address = net.cidr.host 11 prefixes.ctrs.v6;
};
};
};
configuration = { lib, pkgs, config, assignments, allAssignments, ... }:
let
inherit (lib) mkMerge mkIf mkForce;
inherit (lib.my) networkdAssignment;
in
{
config = mkMerge [
{
my = {
deploy.enable = false;
server.enable = true;
secrets = {
key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAvDlH3nT1kve741gBluYmn5KQs8yz7FAEt8qLt+f0K6";
files = {
"gam/terraria.conf" = {
owner = "terraria";
group = "terraria";
};
};
};
};
systemd = {
network.networks."80-container-host0" = networkdAssignment "host0" assignments.internal;
};
services = {
terraria = {
enable = true;
noUPnP = true;
messageOfTheDay = "sup gamers";
autoCreatedWorldSize = "large";
worldPath = "/var/lib/terraria/NotWorld.wld";
configFile = config.age.secrets."gam/terraria.conf".path;
openFirewall = true;
};
};
}
(mkIf config.my.build.isDevVM {
virtualisation = {
forwardPorts = [ ];
};
})
];
};
};
}
@@ -1,6 +1,8 @@
{ lib, ... }: { lib, ... }:
let let
inherit (lib) concatStringsSep;
inherit (lib.my) net; inherit (lib.my) net;
inherit (lib.my.c) pubDomain;
inherit (lib.my.c.colony) domain prefixes; inherit (lib.my.c.colony) domain prefixes;
in in
{ {
@@ -21,7 +23,7 @@ in
}; };
}; };
configuration = { lib, pkgs, config, ... }: configuration = { lib, pkgs, config, allAssignments, ... }:
let let
inherit (lib) mkForce; inherit (lib) mkForce;
in in
@@ -35,6 +37,19 @@ in
secrets = { secrets = {
key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPUv1ntVrZv5ripsKpcOAnyDQX2PHjowzyhqWK10Ml53"; key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPUv1ntVrZv5ripsKpcOAnyDQX2PHjowzyhqWK10Ml53";
files = {
"jackflix/photoprism-pass.txt" = {};
"jackflix/copyparty-pass.txt" = {
owner = "copyparty";
group = "copyparty";
};
};
};
firewall = {
tcp.allowed = [
3923
];
}; };
}; };
@@ -45,15 +60,26 @@ in
transmission.extraGroups = [ "media" ]; transmission.extraGroups = [ "media" ];
radarr.extraGroups = [ "media" ]; radarr.extraGroups = [ "media" ];
sonarr.extraGroups = [ "media" ]; sonarr.extraGroups = [ "media" ];
jellyseerr = { seerr = {
isSystemUser = true; isSystemUser = true;
uid = uids.jellyseerr; uid = uids.jellyseerr;
group = "jellyseerr"; group = "seerr";
};
photoprism = {
isSystemUser = true;
uid = uids.photoprism;
group = "photoprism";
};
copyparty = {
uid = uids.copyparty;
extraGroups = [ "media" ];
}; };
}; };
groups = { groups = {
media.gid = 2000; media.gid = 2000;
jellyseerr.gid = gids.jellyseerr; seerr.gid = gids.jellyseerr;
photoprism.gid = gids.photoprism;
copyparty.gid = gids.copyparty;
}; };
}; };
@@ -62,13 +88,15 @@ in
jackett.bindsTo = [ "systemd-networkd-wait-online@vpn.service" ]; jackett.bindsTo = [ "systemd-networkd-wait-online@vpn.service" ];
transmission.bindsTo = [ "systemd-networkd-wait-online@vpn.service" ]; transmission.bindsTo = [ "systemd-networkd-wait-online@vpn.service" ];
radarr.serviceConfig.UMask = "0002"; radarr.serviceConfig.UMask = mkForce "0002";
sonarr.serviceConfig.UMask = "0002"; radarr.path = with pkgs; [ ffmpeg ];
jellyseerr.serviceConfig = { sonarr.serviceConfig.UMask = mkForce "0002";
sonarr.path = with pkgs; [ ffmpeg ];
seerr.serviceConfig = {
# Needs to be able to read its secrets # Needs to be able to read its secrets
DynamicUser = mkForce false; DynamicUser = mkForce false;
User = "jellyseerr"; User = "seerr";
Group = "jellyseerr"; Group = "seerr";
}; };
# https://github.com/NixOS/nixpkgs/issues/258793#issuecomment-1748168206 # https://github.com/NixOS/nixpkgs/issues/258793#issuecomment-1748168206
@@ -76,6 +104,10 @@ in
RootDirectoryStartOnly = lib.mkForce false; RootDirectoryStartOnly = lib.mkForce false;
RootDirectory = lib.mkForce ""; RootDirectory = lib.mkForce "";
}; };
photoprism.serviceConfig = {
# Needs to be able to access its data
DynamicUser = mkForce false;
};
}; };
}; };
@@ -84,6 +116,7 @@ in
transmission = { transmission = {
enable = true; enable = true;
package = pkgs.transmission_4;
downloadDirPermissions = null; downloadDirPermissions = null;
performanceNetParameters = true; performanceNetParameters = true;
settings = { settings = {
@@ -108,15 +141,78 @@ in
}; };
}; };
flaresolverr.enable = true;
jackett.enable = true; jackett.enable = true;
radarr.enable = true; radarr.enable = true;
sonarr.enable = true; sonarr.enable = true;
jellyseerr = { seerr = {
enable = true; enable = true;
openFirewall = true; openFirewall = true;
}; };
jellyfin.enable = true; jellyfin.enable = true;
photoprism = {
enable = true;
address = "[::]";
port = 2342;
originalsPath = "/mnt/media/photoprism/originals";
importPath = "/mnt/media/photoprism/import";
passwordFile = config.age.secrets."jackflix/photoprism-pass.txt".path;
settings = {
PHOTOPRISM_AUTH_MODE = "password";
PHOTOPRISM_ADMIN_USER = "dev";
PHOTOPRISM_APP_NAME = "/dev/player0 Photos";
PHOTOPRISM_SITE_URL = "https://photos.${pubDomain}/";
PHOTOPRISM_SITE_TITLE = "/dev/player0 Photos";
PHOTOPRISM_TRUSTED_PROXY = concatStringsSep "," (with prefixes.ctrs; [ v4 v6 ]);
PHOTOPRISM_DATABASE_DRIVER = "sqlite";
};
};
copyparty = {
enable = true;
package = pkgs.copyparty.override {
withMagic = true;
};
settings = {
name = "dev-stuff";
no-reload = true;
j = 8; # cores
http-only = true;
xff-src =
with allAssignments.middleman.internal;
[ "${ipv4.address}/32" prefixes.ctrs.v6 ];
rproxy = 1; # get if from x-forwarded-for
magic = true; # enable checking file magic on upload
hist = "/var/cache/copyparty";
shr = "/share"; # enable share creation
ed = true; # enable dotfiles
chmod-f = "664";
chmod-d = "775";
e2dsa = true; # file indexing
e2t = true; # metadata indexing
og-ua = "(Discord|Twitter|Slack)bot"; # embeds
theme = 6;
};
accounts.dev.passwordFile = config.age.secrets."jackflix/copyparty-pass.txt".path;
volumes = {
"/" = {
path = "/mnt/media/public";
access = {
A = "dev";
"r." = "*";
};
flags = {
shr_who = "no"; # no reason to have shares here
};
};
"/priv" = {
path = "/mnt/media/stuff";
access.A = "dev"; # dev has admin access
};
};
};
}; };
}; };
}; };
@@ -12,7 +12,7 @@ let
}; };
# Forwarded in AirVPN config # Forwarded in AirVPN config
transmissionPeerPort = 47016; transmissionPeerPort = 28457;
in in
{ {
config = mkMerge [ config = mkMerge [
@@ -37,7 +37,7 @@ in
tcp dport ${toString transmissionPeerPort} accept tcp dport ${toString transmissionPeerPort} accept
iifname vpn return iifname vpn return
tcp dport { 19999, 9091, 9117, 7878, 8989, 8096 } accept tcp dport { 19999, 9091, 9117, 7878, 8989, 8096, 2342 } accept
return return
} }
chain input { chain input {
@@ -71,14 +71,12 @@ in
RouteTable = routeTable; RouteTable = routeTable;
}; };
wireguardPeers = [ wireguardPeers = [
{
# AirVPN NL # AirVPN NL
wireguardPeerConfig = { {
Endpoint = "2a00:1678:1337:2329:e5f:35d4:4404:ef9f:1637"; Endpoint = "2a00:1678:1337:2329:e5f:35d4:4404:ef9f:1637";
PublicKey = "PyLCXAQT8KkM4T+dUsOQfn+Ub3pGxfGlxkIApuig+hk="; PublicKey = "PyLCXAQT8KkM4T+dUsOQfn+Ub3pGxfGlxkIApuig+hk=";
PresharedKeyFile = config.age.secrets."${pskFile}".path; PresharedKeyFile = config.age.secrets."${pskFile}".path;
AllowedIPs = [ "0.0.0.0/0" "::/0" ]; AllowedIPs = [ "0.0.0.0/0" "::/0" ];
};
} }
]; ];
}; };
@@ -94,7 +92,7 @@ in
matchConfig.Name = "vpn"; matchConfig.Name = "vpn";
address = [ "10.182.97.37/32" "fd7d:76ee:e68f:a993:735d:ef5e:6907:b122/128" ]; address = [ "10.182.97.37/32" "fd7d:76ee:e68f:a993:735d:ef5e:6907:b122/128" ];
dns = [ "10.128.0.1" "fd7d:76ee:e68f:a993::1" ]; dns = [ "10.128.0.1" "fd7d:76ee:e68f:a993::1" ];
routingPolicyRules = map (r: { routingPolicyRuleConfig = r; }) [ routingPolicyRules = [
{ {
Family = "both"; Family = "both";
SuppressPrefixLength = 0; SuppressPrefixLength = 0;
@@ -40,11 +40,6 @@ in
secrets = { secrets = {
key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAQM9U1e/XcUCyMJITrpAHjAGahpqkZCmtX6pJkYzuks"; key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAQM9U1e/XcUCyMJITrpAHjAGahpqkZCmtX6pJkYzuks";
files = { files = {
"dhparams.pem" = {
owner = "acme";
group = "acme";
mode = "440";
};
"pdns-file-records.key" = { "pdns-file-records.key" = {
owner = "acme"; owner = "acme";
group = "acme"; group = "acme";
@@ -72,6 +67,13 @@ in
firewall = { firewall = {
tcp.allowed = [ "http" "https" 8448 ]; tcp.allowed = [ "http" "https" 8448 ];
extraRules = ''
table inet nat {
chain postrouting {
oifname host0 snat ip6 to ${assignments.internal.ipv6.address}
}
}
'';
}; };
nginx-sso = { nginx-sso = {
@@ -169,7 +171,7 @@ in
"*.${config.networking.domain}" "*.${config.networking.domain}"
]; ];
dnsProvider = "exec"; dnsProvider = "exec";
credentialsFile = environmentFile =
let let
script = pkgs.writeShellScript "lego-update-int.sh" '' script = pkgs.writeShellScript "lego-update-int.sh" ''
case "$1" in case "$1" in
@@ -200,7 +202,7 @@ in
"*.s3.${pubDomain}" "*.s3.${pubDomain}"
]; ];
dnsProvider = "cloudflare"; dnsProvider = "cloudflare";
credentialsFile = config.age.secrets."middleman/cloudflare-credentials.conf".path; environmentFile = config.age.secrets."middleman/cloudflare-credentials.conf".path;
postRun = postRun =
let let
sshKey = config.age.secrets."middleman/mailcow-ssh.key".path; sshKey = config.age.secrets."middleman/mailcow-ssh.key".path;
@@ -239,6 +241,9 @@ in
]; ];
recommendedTlsSettings = true; recommendedTlsSettings = true;
recommendedBrotliSettings = true;
# Uh so nginx is hanging with zstd enabled... maybe let's not for now
# recommendedZstdSettings = true;
clientMaxBodySize = "0"; clientMaxBodySize = "0";
serverTokens = true; serverTokens = true;
resolver = { resolver = {
@@ -246,8 +251,10 @@ in
valid = "5s"; valid = "5s";
}; };
proxyResolveWhileRunning = true; proxyResolveWhileRunning = true;
sslDhparam = config.age.secrets."dhparams.pem".path;
appendConfig = ''
worker_processes auto;
'';
# Based on recommended*Settings, but probably better to be explicit about these # Based on recommended*Settings, but probably better to be explicit about these
appendHttpConfig = '' appendHttpConfig = ''
${baseHttpConfig} ${baseHttpConfig}
@@ -2,7 +2,7 @@
let let
inherit (builtins) mapAttrs toJSON; inherit (builtins) mapAttrs toJSON;
inherit (lib) mkMerge mkDefault genAttrs flatten concatStringsSep; inherit (lib) mkMerge mkDefault genAttrs flatten concatStringsSep;
inherit (lib.my.c) pubDomain; inherit (lib.my.c) pubDomain home;
inherit (lib.my.c.nginx) proxyHeaders; inherit (lib.my.c.nginx) proxyHeaders;
inherit (config.networking) domain; inherit (config.networking) domain;
@@ -35,7 +35,6 @@ let
# For clients # For clients
(mkWellKnown "matrix/client" (toJSON { (mkWellKnown "matrix/client" (toJSON {
"m.homeserver".base_url = "https://matrix.nul.ie"; "m.homeserver".base_url = "https://matrix.nul.ie";
"org.matrix.msc3575.proxy".url = "https://matrix-syncv3.nul.ie";
})) }))
]; ];
}; };
@@ -50,6 +49,7 @@ let
"/.well-known/webfinger".return = "301 https://toot.nul.ie$request_uri"; "/.well-known/webfinger".return = "301 https://toot.nul.ie$request_uri";
"/.well-known/nodeinfo".return = "301 https://toot.nul.ie$request_uri"; "/.well-known/nodeinfo".return = "301 https://toot.nul.ie$request_uri";
"/.well-known/host-meta".return = "301 https://toot.nul.ie$request_uri"; "/.well-known/host-meta".return = "301 https://toot.nul.ie$request_uri";
"/.well-known/atproto-did".return = "301 https://pds.nul.ie$request_uri";
}; };
in in
{ {
@@ -80,6 +80,10 @@ in
sha256 = "018wh6ps19n7323fi44njzj9yd4wqslc90dykbwfyscv7bgxhlar"; sha256 = "018wh6ps19n7323fi44njzj9yd4wqslc90dykbwfyscv7bgxhlar";
}; };
} }
{
name = "ssh.pub";
path = lib.my.c.sshKeyFiles.me;
}
]; ];
} }
wellKnown wellKnown
@@ -145,7 +149,7 @@ in
"pass.${pubDomain}" = "pass.${pubDomain}" =
let let
upstream = "http://vaultwarden-ctr.${domain}"; upstream = "http://vaultwarden-ctr.${domain}:8080";
in in
{ {
locations = { locations = {
@@ -182,10 +186,6 @@ in
]; ];
useACMEHost = pubDomain; useACMEHost = pubDomain;
}; };
"matrix-syncv3.${pubDomain}" = {
locations."/".proxyPass = "http://chatterbox-ctr.${domain}:8009";
useACMEHost = pubDomain;
};
"element.${pubDomain}" = "element.${pubDomain}" =
let let
@@ -206,7 +206,8 @@ in
# Currently it seems like single quotes aren't escaped like they should be... # Currently it seems like single quotes aren't escaped like they should be...
conf = { conf = {
brand = "/dev/player0 Matrix"; brand = "/dev/player0 Matrix";
showLabsSettings = true; show_labs_settings = true;
default_country_code = "IE";
disable_guests = true; disable_guests = true;
default_server_config = { default_server_config = {
"m.homeserver" = { "m.homeserver" = {
@@ -214,9 +215,8 @@ in
server_name = "nul.ie"; server_name = "nul.ie";
}; };
}; };
roomDirectory.servers = [ room_directory.servers = [
"nul.ie" "nul.ie"
"netsoc.ie"
"matrix.org" "matrix.org"
]; ];
}; };
@@ -327,6 +327,15 @@ in
useACMEHost = pubDomain; useACMEHost = pubDomain;
}; };
"pds.nul.ie" = {
locations."/" = {
proxyPass = "http://toot-ctr.${domain}:3000";
proxyWebsockets = true;
extraConfig = proxyHeaders;
};
useACMEHost = pubDomain;
};
"share.${pubDomain}" = { "share.${pubDomain}" = {
locations."/" = { locations."/" = {
proxyPass = "http://object-ctr.${domain}:9090"; proxyPass = "http://object-ctr.${domain}:9090";
@@ -338,16 +347,13 @@ in
"stuff.${pubDomain}" = { "stuff.${pubDomain}" = {
locations."/" = { locations."/" = {
basicAuthFile = config.age.secrets."middleman/htpasswd".path; proxyPass = "http://jackflix-ctr.${domain}:3923";
root = "/mnt/media/stuff";
extraConfig = ''
fancyindex on;
fancyindex_show_dotfiles on;
'';
}; };
useACMEHost = pubDomain; useACMEHost = pubDomain;
}; };
"public.${pubDomain}" = { "public.${pubDomain}" = {
onlySSL = false;
addSSL = true;
serverAliases = [ "p.${pubDomain}" ]; serverAliases = [ "p.${pubDomain}" ];
locations."/" = { locations."/" = {
root = "/mnt/media/public"; root = "/mnt/media/public";
@@ -364,7 +370,12 @@ in
useACMEHost = pubDomain; useACMEHost = pubDomain;
}; };
"mc-rail.${pubDomain}" = { "mc-rail.${pubDomain}" = {
locations."/".proxyPass = "http://simpcraft-staging-oci.${domain}:3876"; locations."/".proxyPass = "http://simpcraft-oci.${domain}:3876";
useACMEHost = pubDomain;
};
"mc-map-kink.${pubDomain}" = {
locations."/".proxyPass = "http://kinkcraft-oci.${domain}:8100";
useACMEHost = pubDomain; useACMEHost = pubDomain;
}; };
@@ -384,6 +395,56 @@ in
}; };
useACMEHost = pubDomain; useACMEHost = pubDomain;
}; };
"pb.${pubDomain}" = {
locations."/".proxyPass = "http://object-ctr.${domain}:8088";
useACMEHost = pubDomain;
};
"photos.${pubDomain}" = {
locations."/" = {
proxyPass = "http://jackflix-ctr.${domain}:2342";
proxyWebsockets = true;
extraConfig = proxyHeaders;
};
useACMEHost = pubDomain;
};
"pront.${pubDomain}" = mkMerge [
{
locations."/" = mkMerge [
{
proxyPass = "http://stream-hi.${home.domain}:5000";
proxyWebsockets = true;
extraConfig = proxyHeaders;
}
(ssoLoc "generic")
];
locations."~* ^/webcam/(.*)" = mkMerge [
{
proxyPass = "http://stream-hi.${home.domain}:5050/$1$is_args$args";
extraConfig = proxyHeaders;
}
(ssoLoc "generic")
];
useACMEHost = pubDomain;
}
(ssoServer "generic")
];
"hass.${pubDomain}" = {
locations."/" = {
proxyPass = "http://hass-ctr.${home.domain}:8123";
proxyWebsockets = true;
extraConfig = proxyHeaders;
};
useACMEHost = pubDomain;
};
"hass-john.${pubDomain}" = {
locations."/" = {
proxyPass = "http://john-valorant-tun.${domain}:8123";
proxyWebsockets = true;
extraConfig = proxyHeaders;
};
useACMEHost = pubDomain;
};
}; };
minio = minio =
@@ -395,10 +456,13 @@ in
ignore_invalid_headers off; ignore_invalid_headers off;
''; '';
nixCacheableRegex = ''^\/(\S+\.narinfo|nar\/\S+\.nar\.\S+)$''; nixCacheableRegex = ''^\/(\S+\.narinfo|nar\/\S+\.nar.*|serve\/.+)$'';
nixCacheHeaders = '' nixCacheHeaders = ''
add_header Cache-Control $nix_cache_control; add_header Cache-Control $nix_cache_control;
add_header Expires $nix_expires; add_header Expires $nix_expires;
brotli on;
brotli_types application/x-nix-archive;
''; '';
in in
{ {
@@ -440,9 +504,11 @@ in
"nix-cache.${pubDomain}" = { "nix-cache.${pubDomain}" = {
locations = { locations = {
"/".proxyPass = "http://${host}:8069"; "/" = {
proxyPass = "http://${host}:5000";
};
"~ ${nixCacheableRegex}" = { "~ ${nixCacheableRegex}" = {
proxyPass = "http://${host}:8069"; proxyPass = "http://${host}:5000";
extraConfig = nixCacheHeaders; extraConfig = nixCacheHeaders;
}; };
}; };
@@ -31,6 +31,14 @@ in
{ {
config = mkMerge [ config = mkMerge [
{ {
fileSystems = {
"/var/lib/harmonia" = {
device = "/mnt/nix-cache";
options = [ "bind" ];
fsType = "none";
};
};
my = { my = {
deploy.enable = false; deploy.enable = false;
server.enable = true; server.enable = true;
@@ -48,7 +56,9 @@ in
group = config.my.user.config.group; group = config.my.user.config.group;
}; };
"object/atticd.env" = {}; "object/atticd.env" = {};
"nix-cache.key" = {};
"object/hedgedoc.env" = {}; "object/hedgedoc.env" = {};
"object/wastebin.env" = {};
}; };
}; };
@@ -57,7 +67,9 @@ in
9000 9001 9000 9001
config.services.sharry.config.bind.port config.services.sharry.config.bind.port
8069 8069
5000
config.services.hedgedoc.settings.port config.services.hedgedoc.settings.port
8088
]; ];
}; };
@@ -66,14 +78,31 @@ in
}; };
}; };
users = with lib.my.c.ids; let inherit (config.services.atticd) user group; in { users = with lib.my.c.ids; mkMerge [
(let inherit (config.services.atticd) user group; in {
users."${user}" = { users."${user}" = {
isSystemUser = true; isSystemUser = true;
uid = uids.atticd; uid = uids.atticd;
group = group; group = group;
}; };
groups."${user}".gid = gids.atticd; groups."${user}".gid = gids.atticd;
})
{
users = {
harmonia = {
isSystemUser = true;
group = "harmonia";
shell = pkgs.bashInteractive;
openssh.authorizedKeys.keyFiles = [
lib.my.c.sshKeyFiles.harmonia
];
}; };
};
groups = {
harmonia = { };
};
}
];
systemd = { systemd = {
network.networks."80-container-host0" = networkdAssignment "host0" assignments.internal; network.networks."80-container-host0" = networkdAssignment "host0" assignments.internal;
@@ -91,7 +120,9 @@ in
MINIO_BROWSER_REDIRECT_URL = "https://minio.nul.ie"; MINIO_BROWSER_REDIRECT_URL = "https://minio.nul.ie";
}; };
}; };
sharry = awaitPostgres; sharry = awaitPostgres;
atticd = mkMerge [ atticd = mkMerge [
awaitPostgres awaitPostgres
{ {
@@ -102,6 +133,26 @@ in
}; };
} }
]; ];
harmonia-dev = {
# environment.RUST_LOG = mkForce "trace";
# serviceConfig = {
# StateDirectory = "harmonia";
# DynamicUser = mkForce false;
# };
};
harmonia-daemon = {
# environment.RUST_LOG = mkForce "trace";
preStart = ''
${config.nix.package}/bin/nix store info --store /var/lib/harmonia
'';
serviceConfig = {
User = "harmonia";
Group = "harmonia";
StateDirectory = "harmonia";
DynamicUser = mkForce false;
};
};
}; };
}; };
@@ -111,6 +162,9 @@ in
]; ];
}; };
# TODO/FIXME: this is bad...
nixpkgs.config.permittedInsecurePackages = [ "minio-2025-10-15T17-29-55Z" ];
services = { services = {
minio = { minio = {
enable = true; enable = true;
@@ -181,8 +235,8 @@ in
}; };
atticd = { atticd = {
enable = true; enable = false;
credentialsFile = config.age.secrets."object/atticd.env".path; environmentFile = config.age.secrets."object/atticd.env".path;
settings = { settings = {
listen = "[::]:8069"; listen = "[::]:8069";
allowed-hosts = [ "nix-cache.${pubDomain}" ]; allowed-hosts = [ "nix-cache.${pubDomain}" ];
@@ -201,6 +255,23 @@ in
}; };
}; };
harmonia-dev = {
daemon = {
enable = true;
storeDir = "/nix/store";
dbPath = "/var/lib/harmonia/nix/var/nix/db/db.sqlite";
};
cache = {
enable = true;
signKeyPaths = [ config.age.secrets."nix-cache.key".path ];
settings = {
priority = 30;
virtual_nix_store = "/nix/store";
real_nix_store = "/var/lib/harmonia/nix/store";
};
};
};
hedgedoc = { hedgedoc = {
enable = true; enable = true;
environmentFile = config.age.secrets."object/hedgedoc.env".path; environmentFile = config.age.secrets."object/hedgedoc.env".path;
@@ -220,6 +291,15 @@ in
allowEmailRegister = false; allowEmailRegister = false;
}; };
}; };
wastebin = {
enable = true;
settings = {
WASTEBIN_MAX_BODY_SIZE = 67108864; # 16 MiB
WASTEBIN_PASSWORD_SALT = "TeGhaemeer0Siez3";
};
secretFile = config.age.secrets."object/wastebin.env".path;
};
}; };
} }
(mkIf config.my.build.isDevVM { (mkIf config.my.build.isDevVM {
@@ -0,0 +1,115 @@
{ lib, ... }:
let
inherit (lib.my) net;
inherit (lib.my.c.colony) domain prefixes qclk;
in
{
nixos.systems.qclk = { config, ... }: {
system = "x86_64-linux";
nixpkgs = "mine";
rendered = config.configuration.config.my.asContainer;
assignments = {
internal = {
name = "qclk-ctr";
inherit domain;
ipv4.address = net.cidr.host 10 prefixes.ctrs.v4;
ipv6 = {
iid = "::a";
address = net.cidr.host 10 prefixes.ctrs.v6;
};
};
qclk = {
ipv4 = {
address = net.cidr.host 1 prefixes.qclk.v4;
gateway = null;
};
};
};
configuration = { lib, pkgs, config, assignments, ... }:
let
inherit (lib) concatStringsSep mkMerge mkIf mkForce;
inherit (lib.my) networkdAssignment;
apiPort = 8080;
instances = [
{
host = 2;
wgKey = "D7z1FhcdxpnrGCE0wBW5PZb5BKuhCu6tcZ/5ZaYxdwQ=";
}
];
ipFor = i: net.cidr.host i.host prefixes.qclk.v4;
in
{
config = {
environment = {
systemPackages = with pkgs; [
wireguard-tools
];
};
my = {
deploy.enable = false;
server.enable = true;
secrets = {
key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIC1kcfvahYmSk8IJKaUIcGkhxf/8Yse2XnU7Qqgcglyq";
files = {
"qclk/wg.key" = {
group = "systemd-network";
mode = "440";
};
};
};
firewall = {
udp.allowed = [ qclk.wgPort ];
extraRules = ''
table inet filter {
chain input {
iifname management tcp dport ${toString apiPort} accept
}
chain forward {
iifname host0 oifname management ip saddr { ${concatStringsSep ", " lib.my.c.as211024.trusted.v4} } accept
}
}
table inet nat {
chain postrouting {
iifname host0 oifname management snat ip to ${assignments.qclk.ipv4.address}
}
}
'';
};
};
systemd = {
network = {
netdevs."30-management" = {
netdevConfig = {
Name = "management";
Kind = "wireguard";
};
wireguardConfig = {
PrivateKeyFile = config.age.secrets."qclk/wg.key".path;
ListenPort = qclk.wgPort;
};
wireguardPeers = map (i: {
PublicKey = i.wgKey;
AllowedIPs = [ (ipFor i) ];
}) instances;
};
networks = {
"30-container-host0" = networkdAssignment "host0" assignments.internal;
"30-management" = networkdAssignment "management" assignments.qclk;
};
};
};
services = { };
};
};
};
}
@@ -26,6 +26,8 @@ in
let let
inherit (lib) mkMerge mkIf genAttrs; inherit (lib) mkMerge mkIf genAttrs;
inherit (lib.my) networkdAssignment systemdAwaitPostgres; inherit (lib.my) networkdAssignment systemdAwaitPostgres;
pdsPort = 3000;
in in
{ {
config = mkMerge [ config = mkMerge [
@@ -36,7 +38,7 @@ in
secrets = { secrets = {
key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILSslLkDe54AKYzxdtKD70zcU72W0EpYsfbdJ6UFq0QK"; key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILSslLkDe54AKYzxdtKD70zcU72W0EpYsfbdJ6UFq0QK";
files = genAttrs files = (genAttrs
(map (f: "toot/${f}") [ (map (f: "toot/${f}") [
"postgres-password.txt" "postgres-password.txt"
"secret-key.txt" "secret-key.txt"
@@ -48,7 +50,12 @@ in
(_: with config.services.mastodon; { (_: with config.services.mastodon; {
owner = user; owner = user;
inherit group; inherit group;
}); })) // {
"toot/pds.env" = {
owner = "pds";
group = "pds";
};
};
}; };
firewall = { firewall = {
@@ -56,6 +63,7 @@ in
19999 19999
"http" "http"
pdsPort
]; ];
}; };
}; };
@@ -79,7 +87,7 @@ in
netdata.enable = true; netdata.enable = true;
mastodon = mkMerge [ mastodon = mkMerge [
rec { rec {
enable = true; enable = false;
localDomain = extraConfig.WEB_DOMAIN; # for nginx config localDomain = extraConfig.WEB_DOMAIN; # for nginx config
extraConfig = { extraConfig = {
LOCAL_DOMAIN = "nul.ie"; LOCAL_DOMAIN = "nul.ie";
@@ -87,7 +95,9 @@ in
}; };
secretKeyBaseFile = config.age.secrets."toot/secret-key.txt".path; secretKeyBaseFile = config.age.secrets."toot/secret-key.txt".path;
otpSecretFile = config.age.secrets."toot/otp-secret.txt".path; # TODO: This was removed at some point.
# If we want to bring Mastodon back, this will probably need to be addressd.
# otpSecretFile = config.age.secrets."toot/otp-secret.txt".path;
vapidPrivateKeyFile = config.age.secrets."toot/vapid-key.txt".path; vapidPrivateKeyFile = config.age.secrets."toot/vapid-key.txt".path;
vapidPublicKeyFile = toString (pkgs.writeText vapidPublicKeyFile = toString (pkgs.writeText
"vapid-pubkey.txt" "vapid-pubkey.txt"
@@ -155,6 +165,32 @@ in
}; };
}; };
}; };
bluesky-pds = {
enable = true;
environmentFiles = [ config.age.secrets."toot/pds.env".path ];
settings = {
PDS_HOSTNAME = "pds.nul.ie";
PDS_PORT = pdsPort;
PDS_BLOBSTORE_DISK_LOCATION = null;
PDS_BLOBSTORE_S3_BUCKET = "pds";
PDS_BLOBSTORE_S3_ENDPOINT = "https://s3.nul.ie/";
PDS_BLOBSTORE_S3_REGION = "eu-central-1";
PDS_BLOBSTORE_S3_ACCESS_KEY_ID = "pds";
PDS_BLOB_UPLOAD_LIMIT = "52428800";
PDS_EMAIL_FROM_ADDRESS = "pds@nul.ie";
PDS_DID_PLC_URL = "https://plc.directory";
PDS_INVITE_REQUIRED = "true";
PDS_BSKY_APP_VIEW_URL = "https://api.bsky.app";
PDS_BSKY_APP_VIEW_DID = "did:web:api.bsky.app";
PDS_REPORT_SERVICE_URL = "https://mod.bsky.app";
PDS_REPORT_SERVICE_DID = "did:plc:ar7c4by46qjdydhdevvrndac";
PDS_CRAWLERS = "https://bsky.network";
};
};
}; };
} }
(mkIf config.my.build.isDevVM { (mkIf config.my.build.isDevVM {
@@ -83,7 +83,7 @@ in
DOMAIN = "https://pass.${lib.my.c.pubDomain}"; DOMAIN = "https://pass.${lib.my.c.pubDomain}";
ROCKET_ADDRESS = "::"; ROCKET_ADDRESS = "::";
ROCKET_PORT = 80; ROCKET_PORT = 8080;
SMTP_HOST = "mail.nul.ie"; SMTP_HOST = "mail.nul.ie";
SMTP_FROM = "pass@nul.ie"; SMTP_FROM = "pass@nul.ie";
@@ -99,6 +99,8 @@ in
}; };
borgbackup.jobs.vaultwarden = { borgbackup.jobs.vaultwarden = {
readWritePaths = [ "/var/lib/borgbackup" "/var/cache/borgbackup" ];
paths = [ vwData ]; paths = [ vwData ];
repo = "zh2855@zh2855.rsync.net:borg/vaultwarden2"; repo = "zh2855@zh2855.rsync.net:borg/vaultwarden2";
doInit = true; doInit = true;
@@ -86,7 +86,7 @@ in
interfaceName = "tailscale0"; interfaceName = "tailscale0";
extraUpFlags = [ extraUpFlags = [
"--operator=${config.my.user.config.name}" "--operator=${config.my.user.config.name}"
"--login-server=https://ts.nul.ie" "--login-server=https://hs.nul.ie"
"--netfilter-mode=off" "--netfilter-mode=off"
"--advertise-exit-node" "--advertise-exit-node"
"--advertise-routes=${advRoutes}" "--advertise-routes=${advRoutes}"
+17 -6
View File
@@ -49,7 +49,11 @@ in
inherit (lib.my) networkdAssignment; inherit (lib.my) networkdAssignment;
in in
{ {
imports = [ "${modulesPath}/profiles/qemu-guest.nix" ]; imports = [
"${modulesPath}/profiles/qemu-guest.nix"
./containers-ext.nix
];
config = mkMerge [ config = mkMerge [
{ {
@@ -90,8 +94,8 @@ in
device = "/dev/disk/by-label/minio"; device = "/dev/disk/by-label/minio";
fsType = "xfs"; fsType = "xfs";
}; };
"/mnt/atticd" = { "/mnt/nix-cache" = {
device = "/dev/disk/by-label/atticd"; device = "/dev/disk/by-label/nix-cache";
fsType = "ext4"; fsType = "ext4";
}; };
}; };
@@ -136,10 +140,10 @@ in
}; };
ipv6Prefixes = [ ipv6Prefixes = [
{ {
ipv6PrefixConfig.Prefix = prefixes.ctrs.v6; Prefix = prefixes.ctrs.v6;
} }
]; ];
routes = map (r: { routeConfig = r; }) [ routes = [
{ {
Destination = lib.my.c.tailscale.prefix.v4; Destination = lib.my.c.tailscale.prefix.v4;
Gateway = allAssignments.waffletail.internal.ipv4.address; Gateway = allAssignments.waffletail.internal.ipv4.address;
@@ -148,6 +152,11 @@ in
Destination = lib.my.c.tailscale.prefix.v6; Destination = lib.my.c.tailscale.prefix.v6;
Gateway = allAssignments.waffletail.internal.ipv6.address; Gateway = allAssignments.waffletail.internal.ipv6.address;
} }
{
Destination = prefixes.qclk.v4;
Gateway = allAssignments.qclk.internal.ipv4.address;
}
]; ];
} }
]; ];
@@ -202,11 +211,13 @@ in
object = { object = {
bindMounts = { bindMounts = {
"/mnt/minio".readOnly = false; "/mnt/minio".readOnly = false;
"/mnt/atticd".readOnly = false; "/mnt/nix-cache".readOnly = false;
}; };
}; };
toot = {}; toot = {};
waffletail = {}; waffletail = {};
qclk = {};
gam = {};
}; };
in in
mkMerge [ mkMerge [
+17 -27
View File
@@ -52,6 +52,10 @@ in
valheim-oci = 2; valheim-oci = 2;
simpcraft-oci = 3; simpcraft-oci = 3;
simpcraft-staging-oci = 4; simpcraft-staging-oci = 4;
enshrouded-oci = 5;
kevcraft-oci = 6;
kinkcraft-oci = 7;
graeme-oci = 8;
}; };
configuration = { lib, pkgs, modulesPath, config, assignments, allAssignments, ... }: configuration = { lib, pkgs, modulesPath, config, assignments, allAssignments, ... }:
@@ -66,6 +70,7 @@ in
./valheim.nix ./valheim.nix
./minecraft ./minecraft
# ./enshrouded.nix
]; ];
config = mkMerge [ config = mkMerge [
@@ -108,46 +113,31 @@ in
oci-containers = { oci-containers = {
backend = "podman"; backend = "podman";
}; };
# NixOS has switched to using netavark, which is native to podman. It's currently missing an option to containers.containersConf.settings.network = {
# disable iptables rules generation, which is very annoying. network_backend = "netavark";
containers.containersConf.settings.network.network_backend = mkForce "cni"; firewall_driver = mkForce "none";
};
}; };
environment = { environment = {
etc = { etc = {
"cni/net.d/90-colony.conflist".text = toJSON { "containers/networks/colony.json".text = toJSON {
cniVersion = "0.4.0";
name = "colony"; name = "colony";
plugins = [ id = "0000000000000000000000000000000000000000000000000000000000000001";
{ driver = "bridge";
type = "bridge"; network_interface = "oci";
bridge = "oci"; ipv6_enabled = true;
isGateway = true; internal = false;
ipMasq = false; dns_enabled = false;
hairpinMode = true; subnets = [
ipam = {
type = "host-local";
routes = [
{ dst = "0.0.0.0/0"; }
{ dst = "::/0"; }
];
ranges = [
[
{ {
subnet = prefixes.oci.v4; subnet = prefixes.oci.v4;
gateway = net.cidr.host 1 prefixes.oci.v4; gateway = net.cidr.host 1 prefixes.oci.v4;
} }
]
[
{ {
subnet = prefixes.oci.v6; subnet = prefixes.oci.v6;
gateway = net.cidr.host 1 prefixes.oci.v6; gateway = net.cidr.host 1 prefixes.oci.v6;
} }
]
];
};
capabilities.ips = true;
}
]; ];
}; };
}; };
@@ -0,0 +1,35 @@
{ lib, config, allAssignments, ... }:
let
inherit (lib) concatStringsSep;
inherit (lib.my) dockerNetAssignment;
in
{
config = {
virtualisation.oci-containers.containers = {
enshrouded = {
image = "sknnr/enshrouded-dedicated-server@sha256:f163e8ba9caa2115d8a0a7b16c3696968242fb6fba82706d9a77a882df083497";
environment = {
SERVER_NAME = "UWUshrouded";
# SERVER_IP = "::"; # no IPv6?? :(
TZ = "Europe/Dublin";
};
environmentFiles = [ config.age.secrets."whale2/enshrouded.env".path ];
volumes = [
"enshrouded:/home/steam/enshrouded/savegame"
];
extraOptions = [
''--network=colony:${dockerNetAssignment allAssignments "enshrouded-oci"}''
];
};
};
my = {
secrets.files = {
"whale2/enshrouded.env" = {};
};
};
};
}
@@ -5,12 +5,13 @@ let
# devplayer0 # devplayer0
op = "6d7d971b-ce10-435b-85c5-c99c0d8d288c"; op = "6d7d971b-ce10-435b-85c5-c99c0d8d288c";
kev = "703b378a-09f9-4c1d-9876-1c9305728c49";
whitelist = concatStringsSep "," [ whitelist = concatStringsSep "," [
op op
"dcd2ecb9-2b5e-49cb-9d4f-f5a76162df56" # Elderlypug "dcd2ecb9-2b5e-49cb-9d4f-f5a76162df56" # Elderlypug
"fcb26db2-c3ce-41aa-b588-efec79d37a8a" # Jesthral_ "fcb26db2-c3ce-41aa-b588-efec79d37a8a" # Jesthral_
"1d366062-12c0-4e29-aba7-6ab5d8c6bb05" # shr3kas0ras "1d366062-12c0-4e29-aba7-6ab5d8c6bb05" # shr3kas0ras
"703b378a-09f9-4c1d-9876-1c9305728c49" # OROURKEIRE kev
"f105bbe6-eda6-4a13-a8cf-894e77cab77b" # Adzerq "f105bbe6-eda6-4a13-a8cf-894e77cab77b" # Adzerq
"1fc94979-41fb-497a-81e9-34ae24ca537a" # johnnyscrims "1fc94979-41fb-497a-81e9-34ae24ca537a" # johnnyscrims
"d53c91df-b6e6-4463-b106-e8427d7a8d01" # BossLonus "d53c91df-b6e6-4463-b106-e8427d7a8d01" # BossLonus
@@ -25,22 +26,20 @@ let
email = "simpcraft@nul.ie" email = "simpcraft@nul.ie"
name = "Simpcraft bot" name = "Simpcraft bot"
''; '';
knownHosts = pkgs.writeText "known_hosts" ''
git.nul.ie ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBD023ECzYmLeXIpcGVaciPjq6UN/Sjmsys5HP/Nei5GkrUZqPa3OJ2uSXKLUSKGYdeNhxaFTPJe8Yx3TsZxMme8=
'';
}; };
in in
{ {
config = { config = {
virtualisation.oci-containers.containers = { virtualisation.oci-containers.containers = {
simpcraft = { simpcraft = {
image = "ghcr.io/itzg/minecraft-server:2023.12.2-java17-alpine"; image = "git.nul.ie/dev/craftblock:2024.1.0-java17-alpine";
environment = { environment = {
TYPE = "MODRINTH"; TYPE = "MODRINTH";
EULA = "true"; EULA = "true";
ENABLE_QUERY = "true"; ENABLE_QUERY = "true";
ENABLE_RCON = "true";
MOTD = "§4§k----- §9S§ai§bm§cp§dc§er§fa§6f§5t §4§k-----"; MOTD = "§4§k----- §9S§ai§bm§cp§dc§er§fa§6f§5t §4§k-----";
ICON = "/ext/icon.png"; ICON = "/ext/icon.png";
@@ -52,15 +51,17 @@ in
SPAWN_PROTECTION = "0"; SPAWN_PROTECTION = "0";
VIEW_DISTANCE = "20"; VIEW_DISTANCE = "20";
MAX_MEMORY = "6G"; MAX_MEMORY = "8G";
MODRINTH_MODPACK = "https://cdn.modrinth.com/data/CIYf3Hk8/versions/cdj2bSKg/Simpcraft-0.1.2.mrpack"; MODRINTH_MODPACK = "https://cdn.modrinth.com/data/CIYf3Hk8/versions/NGutsQSd/Simpcraft-0.2.1.mrpack";
TZ = "Europe/Dublin"; TZ = "Europe/Dublin";
}; };
environmentFiles = [ config.age.secrets."whale2/simpcraft.env".path ];
volumes = [ volumes = [
"minecraft_data:/data" "minecraft_data:/data"
"${./icon.png}:/ext/icon.png:ro" "${./icon.png}:/ext/icon.png:ro"
"${fastback.gitConfig}:/data/.config/git/config:ro"
]; ];
extraOptions = [ extraOptions = [
@@ -68,16 +69,96 @@ in
]; ];
}; };
simpcraft-staging = { # simpcraft-staging = {
image = "git.nul.ie/dev/craftblock:2024.1.0-java17-alpine"; # image = "git.nul.ie/dev/craftblock:2024.1.0-java17-alpine";
# environment = {
# TYPE = "MODRINTH";
# EULA = "true";
# ENABLE_QUERY = "true";
# ENABLE_RCON = "true";
# MOTD = "§4§k----- §9S§ai§bm§cp§dc§er§fa§6f§5t [staging] §4§k-----";
# ICON = "/ext/icon.png";
# EXISTING_WHITELIST_FILE = "SYNCHRONIZE";
# WHITELIST = whitelist;
# EXISTING_OPS_FILE = "SYNCHRONIZE";
# OPS = op;
# DIFFICULTY = "normal";
# SPAWN_PROTECTION = "0";
# VIEW_DISTANCE = "20";
# MAX_MEMORY = "4G";
# MODRINTH_MODPACK = "https://cdn.modrinth.com/data/CIYf3Hk8/versions/Ym3sIi6H/Simpcraft-0.2.0.mrpack";
# TZ = "Europe/Dublin";
# };
# environmentFiles = [ config.age.secrets."whale2/simpcraft.env".path ];
# volumes = [
# "minecraft_staging_data:/data"
# "${./icon.png}:/ext/icon.png:ro"
# ];
# extraOptions = [
# ''--network=colony:${dockerNetAssignment allAssignments "simpcraft-staging-oci"}''
# ];
# };
kevcraft = {
# 2025.2.1-java21-alpine
image = "itzg/minecraft-server@sha256:57e319c15e9fee63f61029a65a33acc3de85118b21a2b4bb29f351cf4a915027";
environment = { environment = {
TYPE = "MODRINTH"; TYPE = "VANILLA";
VERSION = "1.20.1";
SERVER_PORT = "25567";
QUERY_PORT = "25567";
EULA = "true"; EULA = "true";
ENABLE_QUERY = "true"; ENABLE_QUERY = "true";
ENABLE_RCON = "true"; ENABLE_RCON = "true";
MOTD = "§4§k----- §9S§ai§bm§cp§dc§er§fa§6f§5t [staging] §4§k-----"; MOTD = "§4§k----- §9K§ae§bv§cc§dr§ea§ff§6t §4§k-----";
ICON = "/ext/icon.png";
EXISTING_WHITELIST_FILE = "SYNCHRONIZE";
WHITELIST = whitelist;
EXISTING_OPS_FILE = "SYNCHRONIZE";
OPS = concatStringsSep "," [ op kev ];
DIFFICULTY = "normal";
SPAWN_PROTECTION = "0";
# VIEW_DISTANCE = "20";
MAX_MEMORY = "4G";
TZ = "Europe/Dublin";
};
environmentFiles = [ config.age.secrets."whale2/simpcraft.env".path ];
volumes = [
"kevcraft_data:/data"
"${./kev.png}:/ext/icon.png:ro"
];
extraOptions = [
''--network=colony:${dockerNetAssignment allAssignments "kevcraft-oci"}''
];
};
kinkcraft = {
# 2025.5.1-java21-alpine
image = "itzg/minecraft-server@sha256:de26c7128e3935f3be48fd30283f0b5a6da1b3d9f1a10c9f92502ee1ba072f7b";
environment = {
TYPE = "MODRINTH";
SERVER_PORT = "25568";
QUERY_PORT = "25568";
EULA = "true";
ENABLE_QUERY = "true";
ENABLE_RCON = "true";
MOTD = "§4§k----- §9K§ai§bn§ck§dc§er§fa§6f§5t §4§k-----";
ICON = "/ext/icon.png"; ICON = "/ext/icon.png";
EXISTING_WHITELIST_FILE = "SYNCHRONIZE"; EXISTING_WHITELIST_FILE = "SYNCHRONIZE";
@@ -88,33 +169,102 @@ in
SPAWN_PROTECTION = "0"; SPAWN_PROTECTION = "0";
VIEW_DISTANCE = "20"; VIEW_DISTANCE = "20";
MAX_MEMORY = "4G"; MAX_MEMORY = "6G";
MODRINTH_MODPACK = "https://cdn.modrinth.com/data/CIYf3Hk8/versions/Ym3sIi6H/Simpcraft-0.2.0.mrpack"; MODRINTH_MODPACK = "https://cdn.modrinth.com/data/CIYf3Hk8/versions/NGutsQSd/Simpcraft-0.2.1.mrpack";
TZ = "Europe/Dublin"; TZ = "Europe/Dublin";
}; };
environmentFiles = [ config.age.secrets."whale2/simpcraft.env".path ]; environmentFiles = [ config.age.secrets."whale2/simpcraft.env".path ];
volumes = [ volumes = [
"minecraft_staging_data:/data" "kinkcraft_data:/data"
"${./icon.png}:/ext/icon.png:ro" "${./icon.png}:/ext/icon.png:ro"
"${fastback.gitConfig}:/data/.config/git/config:ro"
"${fastback.knownHosts}:/data/.ssh/known_hosts:ro"
"${config.age.secrets."whale2/simpcraft-git.key".path}:/data/.ssh/id_rsa"
]; ];
extraOptions = [ extraOptions = [
''--network=colony:${dockerNetAssignment allAssignments "simpcraft-staging-oci"}'' ''--network=colony:${dockerNetAssignment allAssignments "kinkcraft-oci"}''
]; ];
}; };
graeme = {
# 2026.2.1-java21-alpine
image = "itzg/minecraft-server@sha256:82adaddfe0156f07c34228f1c1065cdbd298abc174de0a9961abb068b11beebb";
environment = {
TYPE = "VANILLA";
SERVER_PORT = "25569";
QUERY_PORT = "25569";
EULA = "true";
ENABLE_QUERY = "true";
ENABLE_RCON = "false";
MOTD = "§4§k----- §9G§ar§ba§ce§dm§ee §4§k-----";
ICON = "/ext/icon.png";
EXISTING_WHITELIST_FILE = "SYNCHRONIZE";
WHITELIST = concatStringsSep "," [
op
"fffa146c-0bc8-421c-9e3a-3635c0aca2ea" # Scarlehh
"1ea05f48-76cc-4034-bcd3-2fa1fc5a7375" # Dario
"4bf837b1-01db-4491-a0e0-700d98542833" # JoeSpencer
"d07a9554-1b05-4b0b-b558-27e4a86e1f53" # AmyClover
];
EXISTING_OPS_FILE = "SYNCHRONIZE";
OPS = op;
DIFFICULTY = "hard";
SPAWN_PROTECTION = "0";
VIEW_DISTANCE = "20";
MAX_MEMORY = "4G";
TZ = "Europe/Dublin";
};
volumes = [
"graeme_data:/data"
"${./graeme.png}:/ext/icon.png:ro"
];
extraOptions = [
''--network=colony:${dockerNetAssignment allAssignments "graeme-oci"}''
];
};
};
services = {
borgbackup.jobs.simpcraft =
let
rconCommand = cmd: ''${pkgs.mcrcon}/bin/mcrcon -H simpcraft-oci -p "$RCON_PASSWORD" "${cmd}"'';
in
{
paths = [ "/var/lib/containers/storage/volumes/minecraft_data/_data/world" ];
repo = "/var/lib/containers/backup/simpcraft";
doInit = true;
encryption.mode = "none";
compression = "zstd,10";
# every ~15 minutes offset from 5 minute intervals (Minecraft seems to save at precise times?)
startAt = "*:03,17,33,47";
prune.keep = {
within = "12H";
hourly = 48;
};
readWritePaths = [ "/var/lib/borgbackup" "/var/cache/borgbackup" ];
# Avoid Minecraft poking the files while we back up
preHook = rconCommand "save-off";
postHook = rconCommand "save-on";
};
};
systemd = {
services = {
borgbackup-job-simpcraft.serviceConfig.EnvironmentFile = [ config.age.secrets."whale2/simpcraft.env".path ];
};
}; };
my = { my = {
secrets.files = { secrets.files = {
"whale2/simpcraft.env" = {}; "whale2/simpcraft.env" = {};
"whale2/simpcraft-git.key" = {
owner = "1000";
};
}; };
}; };
}; };
Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

@@ -1,7 +1,8 @@
{ lib, ... }: { lib, ... }:
let let
inherit (lib.my) net; inherit (lib.my) net;
inherit (lib.my.c.home) domain vlans prefixes; inherit (lib.my.c) networkd;
inherit (lib.my.c.home) domain vlans prefixes vips roceBootModules;
in in
{ {
nixos.systems.castle = { nixos.systems.castle = {
@@ -15,7 +16,7 @@ in
ipv4 = { ipv4 = {
address = net.cidr.host 40 prefixes.hi.v4; address = net.cidr.host 40 prefixes.hi.v4;
mask = 22; mask = 22;
gateway = null; gateway = vips.hi.v4;
}; };
ipv6 = { ipv6 = {
iid = "::3:1"; iid = "::3:1";
@@ -35,7 +36,7 @@ in
cpu = { cpu = {
amd.updateMicrocode = true; amd.updateMicrocode = true;
}; };
opengl.extraPackages = with pkgs; [ graphics.extraPackages = with pkgs; [
intel-media-driver intel-media-driver
]; ];
bluetooth.enable = true; bluetooth.enable = true;
@@ -47,7 +48,7 @@ in
timeout = 10; timeout = 10;
}; };
kernelPackages = lib.my.c.kernel.latest pkgs; kernelPackages = lib.my.c.kernel.latest pkgs;
kernelModules = [ "kvm-amd" ]; kernelModules = [ "kvm-amd" "dm-snapshot" ];
kernelParams = [ "amd_iommu=on" "amd_pstate=passive" ]; kernelParams = [ "amd_iommu=on" "amd_pstate=passive" ];
kernelPatches = [ kernelPatches = [
# { # {
@@ -57,27 +58,40 @@ in
# } # }
]; ];
initrd = { initrd = {
availableKernelModules = [ "thunderbolt" "xhci_pci" "nvme" "ahci" "usbhid" "usb_storage" "sd_mod" ]; availableKernelModules = [
"thunderbolt" "xhci_pci" "nvme" "ahci" "usbhid" "usb_storage" "sd_mod"
"8021q"
] ++ roceBootModules;
systemd.network = {
netdevs = mkVLAN "lan-hi" vlans.hi;
networks = {
"10-et100g" = {
matchConfig.Name = "et100g";
vlan = [ "lan-hi" ];
linkConfig.RequiredForOnline = "no";
networkConfig = networkd.noL3;
}; };
"20-lan-hi" = networkdAssignment "lan-hi" assignments.hi;
};
};
};
binfmt.emulatedSystems = [ "aarch64-linux" "armv7l-linux" ];
}; };
fileSystems = { fileSystems = {
"/boot" = {
device = "/dev/disk/by-partuuid/8ce4248a-3ee4-f44f-801f-064a628b4d6e";
fsType = "vfat";
};
"/nix" = { "/nix" = {
device = "/dev/disk/by-partuuid/2da23a1d-2daf-d943-b91e-fc175f3dad07"; device = "/dev/nvmeof/nix";
fsType = "ext4"; fsType = "ext4";
}; };
"/persist" = { "/persist" = {
device = "/dev/disk/by-partuuid/f4c80d4f-a022-e941-b5d1-fe2e65e444b9"; device = "/dev/nvmeof/persist";
fsType = "ext4"; fsType = "ext4";
neededForBoot = true; neededForBoot = true;
}; };
"/home" = { "/home" = {
device = "/dev/disk/by-partuuid/992a93cf-6c9c-324b-b0ce-f8eb2d1ce10d"; device = "/dev/nvmeof/home";
fsType = "ext4"; fsType = "ext4";
}; };
}; };
@@ -95,26 +109,29 @@ in
}; };
fstrim.enable = true; fstrim.enable = true;
resolved = { resolved.settings.Resolve.LLMNR = mkForce true;
enable = true;
extraConfig = mkForce "";
dnssec = "false";
};
pipewire.extraConfig.pipewire = {
"10-buffer"."context.properties" = {
"default.clock.quantum" = 128;
"default.clock.max-quantum" = 128;
};
};
blueman.enable = true; blueman.enable = true;
avahi.enable = true;
}; };
programs = { programs = {
virt-manager.enable = true; virt-manager.enable = true;
wireshark = { wireshark = {
enable = true; enable = true;
package = pkgs.wireshark-qt; package = pkgs.wireshark;
}; };
}; };
virtualisation.libvirtd.enable = true; virtualisation.libvirtd.enable = true;
networking = { networking = {
domain = "h.${lib.my.c.pubDomain}"; inherit domain;
firewall.enable = false; firewall.enable = false;
}; };
@@ -130,25 +147,22 @@ in
mstflint mstflint
qperf qperf
ethtool ethtool
android-tools
]; ];
environment.etc = {
"pipewire/pipewire.conf.d/sample-size.conf".text = ''
context.properties = {
default.clock.quantum = 128
default.clock.max-quantum = 128
}
'';
};
nix = { nix = {
gc.automatic = false; gc.automatic = false;
settings = {
experimental-features = [ "recursive-nix" ];
system-features = [ "nixos-test" "benchmark" "big-parallel" "kvm" "recursive-nix" ];
};
}; };
systemd = { systemd = {
network = { network = {
wait-online.enable = false;
netdevs = mkMerge [ netdevs = mkMerge [
(mkVLAN "lan-hi" vlans.hi) (mkVLAN "lan-hi" vlans.hi)
(mkVLAN "lan-lo" vlans.lo)
]; ];
links = { links = {
"10-et2.5g" = { "10-et2.5g" = {
@@ -163,30 +177,37 @@ in
matchConfig.PermanentMACAddress = "24:8a:07:a8:fe:3a"; matchConfig.PermanentMACAddress = "24:8a:07:a8:fe:3a";
linkConfig = { linkConfig = {
Name = "et100g"; Name = "et100g";
MTUBytes = "9000"; MTUBytes = toString lib.my.c.home.hiMTU;
}; };
}; };
}; };
networks = { networks = {
"50-lan" = { "30-et100g" = {
matchConfig.Name = "et2.5g";
DHCP = "no";
address = [ "10.16.7.1/16" ];
};
"50-et100g" = {
matchConfig.Name = "et100g"; matchConfig.Name = "et100g";
vlan = [ "lan-hi" ]; vlan = [ "lan-hi" "lan-lo" ];
networkConfig.IPv6AcceptRA = false; networkConfig.IPv6AcceptRA = false;
}; };
"60-lan-hi" = mkMerge [ "40-lan-hi" = mkMerge [
(networkdAssignment "lan-hi" assignments.hi) (networkdAssignment "lan-hi" assignments.hi)
{ # So we don't drop the IP we use to connect to NVMe-oF!
DHCP = "yes"; { networkConfig.KeepConfiguration = "static"; }
matchConfig.Name = "lan-hi";
linkConfig.MTUBytes = "9000";
}
]; ];
"45-lan-lo" = {
matchConfig.Name = "lan-lo";
networkConfig = {
DHCP = "ipv4";
IPv6AcceptRA = true;
UseDomains = false;
};
dhcpV4Config = {
UseDNS = false;
UseGateway = false;
};
ipv6AcceptRAConfig = {
UseDNS = false;
UseGateway = false;
};
};
}; };
}; };
}; };
@@ -205,15 +226,19 @@ in
packages = with pkgs; [ packages = with pkgs; [
jacktrip jacktrip
qpwgraph qpwgraph
# TODO: seems to be borked (infinite recursion???) boardie
# (writeShellScriptBin "boardie" ''
# exec pw-jack ${boardie}/bin/boardie "$@"
# '')
]; ];
}; };
programs = {
claude-code = {
enable = true;
};
};
services = { services = {
blueman-applet.enable = true; blueman-applet.enable = true;
easyeffects.enable = true;
}; };
wayland.windowManager.sway = { wayland.windowManager.sway = {
@@ -222,6 +247,7 @@ in
HDMI-A-1 = { HDMI-A-1 = {
transform = "270"; transform = "270";
position = "0 0"; position = "0 0";
bg = "${./his-team-player.jpg} fill";
}; };
DP-1 = { DP-1 = {
mode = "2560x1440@170Hz"; mode = "2560x1440@170Hz";
@@ -243,11 +269,19 @@ in
}; };
#deploy.generate.system.mode = "boot"; #deploy.generate.system.mode = "boot";
deploy.node.hostname = "castle.box.${config.networking.domain}";
secrets = { secrets = {
key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMlVuTzKObeaUuPocCF41IO/8X+443lzUJLuCIclt2vr"; key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMlVuTzKObeaUuPocCF41IO/8X+443lzUJLuCIclt2vr";
}; };
nvme.uuid = "2230b066-a674-4f45-a1dc-f7727b3a9e7b"; netboot.client = {
enable = true;
};
nvme = {
uuid = "2230b066-a674-4f45-a1dc-f7727b3a9e7b";
boot = {
nqn = "nqn.2016-06.io.spdk:castle";
address = "192.168.68.80";
};
};
firewall = { firewall = {
enable = false; enable = false;
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 MiB

+8 -1
View File
@@ -94,7 +94,7 @@ in
extraOptions = [ "-A /var/log/smartd/" "--interval=600" ]; extraOptions = [ "-A /var/log/smartd/" "--interval=600" ];
}; };
udev.extraRules = '' udev.extraRules = ''
ACTION=="add", SUBSYSTEM=="net", ENV{ID_NET_DRIVER}=="mlx5_core", ENV{ID_PATH}=="pci-0000:44:00.0", ATTR{device/sriov_numvfs}="3" ACTION=="add", SUBSYSTEM=="net", ENV{ID_NET_DRIVER}=="mlx5_core", ENV{ID_PATH}=="pci-0000:44:00.0", ATTR{device/sriov_numvfs}="4"
''; '';
}; };
@@ -188,6 +188,13 @@ in
VLANId=${toString vlans.hi} VLANId=${toString vlans.hi}
LinkState=yes LinkState=yes
MACAddress=52:54:00:ac:15:a9 MACAddress=52:54:00:ac:15:a9
# sfh bridge
[SR-IOV]
VirtualFunction=3
VLANId=${toString vlans.hi}
LinkState=yes
MACAddress=52:54:00:90:34:95
''; '';
}; };
"60-lan-hi" = networkdAssignment "lan-hi" assignments.hi; "60-lan-hi" = networkdAssignment "lan-hi" assignments.hi;
@@ -66,6 +66,7 @@ in
]; ];
services = { services = {
fstrim.enable = true;
netdata.enable = true; netdata.enable = true;
}; };
@@ -129,6 +129,12 @@ in
hostnqn = hostnqn =
"nqn.2014-08.org.nvmexpress:uuid:2230b066-a674-4f45-a1dc-f7727b3a9e7b"; "nqn.2014-08.org.nvmexpress:uuid:2230b066-a674-4f45-a1dc-f7727b3a9e7b";
serial = "SPDK00000000000002"; serial = "SPDK00000000000002";
}) ++ (nvmfBdev {
bdev = "NVMeRaidp3";
nqn = "nqn.2016-06.io.spdk:sfh";
hostnqn =
"nqn.2014-08.org.nvmexpress:uuid:85d7df36-0de0-431b-b06e-51f7c0a455b4";
serial = "SPDK00000000000003";
}); });
}; };
}; };
+18 -6
View File
@@ -2,6 +2,7 @@
imports = [ imports = [
./cellar ./cellar
./river.nix ./river.nix
./sfh
]; ];
nixos.systems.palace.configuration = { lib, pkgs, config, systems, allAssignments, ... }: nixos.systems.palace.configuration = { lib, pkgs, config, systems, allAssignments, ... }:
@@ -57,11 +58,11 @@
systemd.services = systemd.services =
let let
awaitCellar = { awaitVM = system: {
after = [ "vm@cellar.service" ]; after = [ "vm@${system}.service" ];
bindsTo = [ "vm@cellar.service" ]; bindsTo = [ "vm@${system}.service" ];
preStart = '' preStart = ''
until ${pkgs.netcat}/bin/nc -w1 -z ${allAssignments.cellar.hi.ipv4.address} 22; do until ${pkgs.netcat}/bin/nc -w1 -z ${allAssignments.${system}.hi.ipv4.address} 22; do
sleep 1 sleep 1
done done
''; '';
@@ -81,13 +82,13 @@
vtapUnit = "sys-subsystem-net-devices-vm\\x2det1g0.device"; vtapUnit = "sys-subsystem-net-devices-vm\\x2det1g0.device";
in in
mkMerge [ mkMerge [
awaitCellar (awaitVM "cellar")
{ {
requires = [ vtapUnit ]; requires = [ vtapUnit ];
after = [ vtapUnit ]; after = [ vtapUnit ];
} }
]; ];
"vm@sfh" = awaitCellar; "vm@sfh" = (awaitVM "river");
}; };
my = { my = {
@@ -182,8 +183,19 @@
index = 0; index = 0;
hostBDF = "44:00.3"; hostBDF = "44:00.3";
}; };
et100g0vf3 = {
index = 1;
hostBDF = "44:00.4";
}; };
}; };
qemuFlags = [
"device qemu-xhci,id=xhci"
# Front-right port?
"device usb-host,hostbus=1,hostport=4"
# Front-left port
"device usb-host,hostbus=1,hostport=3"
];
};
}; };
}; };
}; };
+25 -33
View File
@@ -10,18 +10,7 @@
let let
inherit (lib.my) networkdAssignment mkVLAN; inherit (lib.my) networkdAssignment mkVLAN;
inherit (lib.my.c) networkd; inherit (lib.my.c) networkd;
inherit (lib.my.c.home) vlans; inherit (lib.my.c.home) vlans domain prefixes roceBootModules;
lanLink = {
matchConfig = {
Driver = "mlx5_core";
PermanentMACAddress = "52:54:00:8a:8a:f2";
};
linkConfig = {
Name = "lan";
MTUBytes = toString lib.my.c.home.hiMTU;
};
};
in in
{ {
imports = [ imports = [
@@ -30,29 +19,17 @@
config = { config = {
boot = { boot = {
kernelModules = [ "kvm-intel" ]; kernelModules = [ "kvm-amd" ];
kernelParams = [ "console=ttyS0,115200n8" ]; kernelParams = [ "console=ttyS0,115200n8" ];
initrd = { initrd = {
availableKernelModules = [ availableKernelModules = [
"virtio_pci" "ahci" "sr_mod" "virtio_blk" "virtio_pci" "ahci" "sr_mod" "virtio_blk"
"ib_core" "ib_uverbs" "mlx5_core" "mlx5_ib" "8021q" "8021q"
"rdma_cm" "iw_cm" "ib_cm" "nvme_core" "nvme_rdma" ] ++ roceBootModules;
]; kernelModules = [ "dm-snapshot" ];
kernelModules = [ "dm-snapshot" "nvme-fabrics" ];
systemd = { systemd = {
extraBin = with pkgs; {
dmesg = "${util-linux}/bin/dmesg";
ip = "${iproute2}/bin/ip";
};
extraConfig = ''
DefaultTimeoutStartSec=50
DefaultDeviceTimeoutSec=50
'';
network = { network = {
enable = true; # Don't need to put the link config here, they're copied from main config
wait-online.enable = true;
links."10-lan" = lanLink;
netdevs = mkVLAN "lan-hi" vlans.hi; netdevs = mkVLAN "lan-hi" vlans.hi;
networks = { networks = {
"20-lan" = { "20-lan" = {
@@ -70,9 +47,6 @@
hardware = { hardware = {
enableRedistributableFirmware = true; enableRedistributableFirmware = true;
cpu = {
intel.updateMicrocode = true;
};
}; };
fileSystems = { fileSystems = {
@@ -96,6 +70,7 @@
boot.thin.enable = true; boot.thin.enable = true;
dmeventd.enable = true; dmeventd.enable = true;
}; };
fstrim.enable = true;
}; };
systemd.network = { systemd.network = {
@@ -114,7 +89,16 @@
}; };
}; };
"10-lan" = lanLink; "10-lan" = {
matchConfig = {
Driver = "mlx5_core";
PermanentMACAddress = "52:54:00:8a:8a:f2";
};
linkConfig = {
Name = "lan";
MTUBytes = toString lib.my.c.home.hiMTU;
};
};
}; };
# So we don't drop the IP we use to connect to NVMe-oF! # So we don't drop the IP we use to connect to NVMe-oF!
@@ -134,6 +118,14 @@
}; };
}; };
netboot.server = {
enable = true;
ip = assignments.lo.ipv4.address;
host = "boot.${domain}";
allowedPrefixes = with prefixes; [ hi.v4 hi.v6 lo.v4 lo.v6 ];
instances = [ "sfh" "castle" ];
};
deploy.node.hostname = "192.168.68.1"; deploy.node.hostname = "192.168.68.1";
}; };
}; };
@@ -0,0 +1,6 @@
{
imports = [
# ./unifi.nix
./hass.nix
];
}
@@ -0,0 +1,263 @@
{ lib, ... }:
let
inherit (lib.my) net;
inherit (lib.my.c) pubDomain;
inherit (lib.my.c.home) domain prefixes vips hiMTU;
in
{
nixos.systems.hass = { config, ... }: {
system = "x86_64-linux";
nixpkgs = "mine";
rendered = config.configuration.config.my.asContainer;
assignments = {
hi = {
name = "hass-ctr";
altNames = [ "frigate" ];
inherit domain;
mtu = hiMTU;
ipv4 = {
address = net.cidr.host 103 prefixes.hi.v4;
mask = 22;
gateway = vips.hi.v4;
};
ipv6 = {
iid = "::5:3";
address = net.cidr.host (65536*5+3) prefixes.hi.v6;
};
};
lo = {
name = "hass-ctr-lo";
inherit domain;
mtu = 1500;
ipv4 = {
address = net.cidr.host 103 prefixes.lo.v4;
mask = 21;
gateway = null;
};
ipv6 = {
iid = "::5:3";
address = net.cidr.host (65536*5+3) prefixes.lo.v6;
};
};
};
configuration = { lib, config, pkgs, assignments, allAssignments, ... }:
let
inherit (lib) mkMerge mkIf mkForce;
inherit (lib.my) networkdAssignment;
hassCli = pkgs.writeShellScriptBin "hass-cli" ''
export HASS_SERVER="http://localhost:${toString config.services.home-assistant.config.http.server_port}"
export HASS_TOKEN="$(< ${config.age.secrets."hass/cli-token.txt".path})"
exec ${pkgs.home-assistant-cli}/bin/hass-cli "$@"
'';
in
{
config = {
my = {
deploy.enable = false;
server.enable = true;
secrets = {
key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGpYX2WbYwUqHp8bFFf0eHFrqrR8xp8IheguA054F8V4";
files = {
"hass/cli-token.txt" = {
owner = config.my.user.config.name;
};
};
};
firewall = {
tcp.allowed = [ "http" 1883 ];
};
};
environment = {
systemPackages = with pkgs; [
usbutils
hassCli
];
};
systemd = {
network.networks = {
"80-container-host0" = networkdAssignment "host0" assignments.hi;
"80-container-lan-lo" = networkdAssignment "lan-lo" assignments.lo;
};
};
services = {
mosquitto = {
enable = true;
listeners = [
{
omitPasswordAuth = true;
settings = {
allow_anonymous = true;
};
}
];
};
go2rtc = {
enable = true;
settings = {
streams = {
reolink_living_room = [
# "http://reolink-living-room.${domain}/flv?port=1935&app=bcs&stream=channel0_main.bcs&user=admin#video=copy#audio=copy#audio=opus"
"rtsp://admin:@reolink-living-room:554/h264Preview_01_main"
];
webcam_office = [
"ffmpeg:device?video=/dev/video0&video_size=1024x576#video=h264"
];
};
};
};
frigate = {
enable = true;
hostname = "frigate.${domain}";
settings = {
mqtt = {
enabled = true;
host = "localhost";
topic_prefix = "frigate";
};
cameras = {
reolink_living_room = {
ffmpeg.inputs = [
{
path = "rtsp://127.0.0.1:8554/reolink_living_room";
input_args = "preset-rtsp-restream";
roles = [ "record" "detect" ];
}
];
detect = {
enabled = false;
};
record = {
enabled = true;
retain.days = 1;
};
};
webcam_office = {
ffmpeg.inputs = [
{
path = "rtsp://127.0.0.1:8554/webcam_office";
input_args = "preset-rtsp-restream";
roles = [ "record" "detect" ];
}
];
detect.enabled = false;
record = {
enabled = true;
retain.days = 1;
};
};
};
};
};
home-assistant =
let
cfg = config.services.home-assistant;
pyirishrail = ps: ps.buildPythonPackage rec {
pname = "pyirishrail";
version = "0.0.2";
src = pkgs.fetchFromGitHub {
owner = "ttroy50";
repo = "pyirishrail";
tag = version;
hash = "sha256-NgARqhcXP0lgGpgBRiNtQaSn9JcRNtCcZPljcL7t3Xc=";
};
dependencies = with ps; [
requests
];
pyproject = true;
build-system = [ ps.setuptools ];
};
in
{
enable = true;
extraComponents = [
"default_config"
"esphome"
"google_translate"
"met"
"zha"
"denonavr"
"webostv"
"androidtv_remote"
"heos"
"mqtt"
"wled"
];
extraPackages = python3Packages: with python3Packages; [
zlib-ng
isal
gtts
(pyirishrail python3Packages)
];
customComponents = with pkgs.home-assistant-custom-components; [
alarmo
frigate
west_wood_club
];
configWritable = false;
openFirewall = true;
config = {
default_config = {};
homeassistant = {
name = "Home";
unit_system = "metric";
currency = "EUR";
country = "IE";
time_zone = "Europe/Dublin";
external_url = "https://hass.${pubDomain}";
internal_url = "http://hass-ctr.${domain}:${toString cfg.config.http.server_port}";
};
http = {
use_x_forwarded_for = true;
trusted_proxies = with allAssignments.middleman.internal; [
ipv4.address
ipv6.address
];
ip_ban_enabled = false;
};
automation = "!include automations.yaml";
script = "!include scripts.yaml";
scene = "!include scenes.yaml";
sensor = [
{
platform = "irish_rail_transport";
name = "To Work from Home";
station = "Glenageary";
stops_at = "Dublin Connolly";
direction = "Northbound";
}
{
platform = "irish_rail_transport";
name = "To Home from Work";
station = "Dublin Connolly";
stops_at = "Glenageary";
direction = "Southbound";
}
];
};
};
};
};
};
};
}
@@ -0,0 +1,65 @@
{ lib, ... }:
let
inherit (lib.my) net;
inherit (lib.my.c.home) domain prefixes vips hiMTU;
in
{
nixos.systems.unifi = { config, ... }: {
system = "x86_64-linux";
nixpkgs = "mine";
rendered = config.configuration.config.my.asContainer;
assignments = {
hi = {
name = "unifi-ctr";
inherit domain;
mtu = hiMTU;
ipv4 = {
address = net.cidr.host 100 prefixes.hi.v4;
mask = 22;
gateway = vips.hi.v4;
};
ipv6 = {
iid = "::5:1";
address = net.cidr.host (65536*5+1) prefixes.hi.v6;
};
};
};
configuration = { lib, config, pkgs, assignments, ... }:
let
inherit (lib) mkMerge mkIf mkForce;
inherit (lib.my) networkdAssignment;
in
{
config = {
my = {
deploy.enable = false;
server.enable = true;
secrets = {
key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKdgcziQki/RH7E+NH2bYnzSVKaJ27905Yo5TcOjSh/U";
files = { };
};
firewall = {
tcp.allowed = [ 8443 ];
};
};
systemd = {
network.networks."80-container-host0" = networkdAssignment "host0" assignments.hi;
};
services = {
unifi = {
enable = true;
openFirewall = true;
unifiPackage = pkgs.unifi;
mongodbPackage = pkgs.mongodb-7_0;
};
};
};
};
};
}
+200
View File
@@ -0,0 +1,200 @@
{ lib, ... }:
let
inherit (lib.my) net;
inherit (lib.my.c.home) domain prefixes vips hiMTU roceBootModules;
in
{
imports = [ ./containers ];
config.nixos.systems.sfh = {
system = "x86_64-linux";
nixpkgs = "mine";
home-manager = "mine";
assignments = {
hi = {
inherit domain;
mtu = hiMTU;
ipv4 = {
address = net.cidr.host 81 prefixes.hi.v4;
mask = 22;
gateway = vips.hi.v4;
};
ipv6 = {
iid = "::4:2";
address = net.cidr.host (65536*4+2) prefixes.hi.v6;
};
};
};
configuration = { lib, modulesPath, pkgs, config, assignments, allAssignments, ... }:
let
inherit (lib) mapAttrs mkMerge mkForce;
inherit (lib.my) networkdAssignment;
inherit (lib.my.c) networkd;
inherit (lib.my.c.home) domain;
in
{
imports = [
"${modulesPath}/profiles/qemu-guest.nix"
];
config = {
boot = {
kernelModules = [ "kvm-amd" ];
kernelParams = [ "console=ttyS0,115200n8" ];
initrd = {
availableKernelModules = [
"virtio_pci" "ahci" "sr_mod" "virtio_blk"
] ++ roceBootModules;
kernelModules = [ "dm-snapshot" ];
systemd = {
network = {
networks = {
"20-lan-hi" = networkdAssignment "lan-hi" assignments.hi;
};
};
};
};
};
hardware = {
enableRedistributableFirmware = true;
};
fileSystems = {
"/nix" = {
device = "/dev/main/nix";
fsType = "ext4";
};
"/persist" = {
device = "/dev/main/persist";
fsType = "ext4";
neededForBoot = true;
};
};
networking = { inherit domain; };
services = {
lvm = {
boot.thin.enable = true;
dmeventd.enable = true;
};
};
environment = {
systemPackages = with pkgs; [
usbutils
];
};
systemd.network = {
links = {
"10-lan-hi" = {
matchConfig = {
Driver = "mlx5_core";
PermanentMACAddress = "52:54:00:ac:15:a9";
};
linkConfig = {
Name = "lan-hi";
MTUBytes = toString lib.my.c.home.hiMTU;
};
};
"10-lan-hi-ctrs" = {
matchConfig = {
Driver = "mlx5_core";
PermanentMACAddress = "52:54:00:90:34:95";
};
linkConfig = {
Name = "lan-hi-ctrs";
MTUBytes = toString lib.my.c.home.hiMTU;
};
};
"10-lan-lo-ctrs" = {
matchConfig = {
Driver = "virtio_net";
PermanentMACAddress = "52:54:00:a5:7e:93";
};
linkConfig.Name = "lan-lo-ctrs";
};
};
networks = {
"30-lan-hi" = mkMerge [
(networkdAssignment "lan-hi" assignments.hi)
# So we don't drop the IP we use to connect to NVMe-oF!
{ networkConfig.KeepConfiguration = "static"; }
];
"30-lan-hi-ctrs" = {
matchConfig.Name = "lan-hi-ctrs";
linkConfig.RequiredForOnline = "no";
networkConfig = networkd.noL3;
};
"30-lan-lo-ctrs" = {
matchConfig.Name = "lan-lo-ctrs";
linkConfig.RequiredForOnline = "no";
networkConfig = networkd.noL3;
};
};
};
systemd.nspawn = {
hass = {
networkConfig = {
MACVLAN = mkForce "lan-hi-ctrs:host0 lan-lo-ctrs:lan-lo";
};
};
};
systemd.services = {
"systemd-nspawn@hass".serviceConfig.DeviceAllow = [
"char-ttyUSB rw"
"char-video4linux rw"
];
};
my = {
secrets = {
key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAAaav5Se1E/AbqEXmADryVszYfNDscyP6jrWioN57R7";
};
server.enable = true;
netboot.client = {
enable = true;
};
nvme = {
uuid = "85d7df36-0de0-431b-b06e-51f7c0a455b4";
boot = {
nqn = "nqn.2016-06.io.spdk:sfh";
address = "192.168.68.80";
};
};
containers.instances =
let
instances = {
# unifi = {};
hass = {
bindMounts = {
"/dev/bus/usb/001/002".readOnly = false;
"/dev/video0".readOnly = false;
"/dev/serial/by-id/usb-Nabu_Casa_Home_Assistant_Connect_ZBT-1_ce549704fe38ef11a2c2e5d154516304-if00-port0" = {
readOnly = false;
mountPoint = "/dev/ttyUSB0";
};
};
};
};
in
mkMerge [
instances
(mapAttrs (n: i: {
networking.macVLAN = "lan-hi-ctrs";
}) instances)
];
};
};
};
};
}
+38 -16
View File
@@ -121,11 +121,9 @@ in
}; };
services = { services = {
resolved = { resolved.settings.Resolve = {
llmnr = "false"; LLMNR = false;
extraConfig = '' MulticastDNS = false;
MulticastDNS=false
'';
}; };
iperf3 = { iperf3 = {
@@ -141,26 +139,40 @@ in
onState = [ "configured" ]; onState = [ "configured" ];
script = '' script = ''
#!${pkgs.runtimeShell} #!${pkgs.runtimeShell}
if [ $IFACE = "wan-ifb" ]; then if [ "$IFACE" = "wan-ifb" ]; then
${pkgs.iproute2}/bin/tc filter add dev wan parent ffff: matchall action mirred egress redirect dev $IFACE ${pkgs.iproute2}/bin/tc filter add dev wan parent ffff: matchall action mirred egress redirect dev "$IFACE"
fi fi
''; '';
}; };
}; };
}; };
nginx.enable = true;
}; };
networking.domain = "h.${pubDomain}"; networking = { inherit domain; };
systemd.services = { systemd.services =
ipsec =
let let
waitOnline = "systemd-networkd-wait-online@wan.service"; waitOnline = "systemd-networkd-wait-online@wan.service";
in in
{ {
ipsec = {
after = [ waitOnline ]; after = [ waitOnline ];
requires = [ waitOnline ]; requires = [ waitOnline ];
}; };
ipv6-clear-default-route = {
description = "Clear IPv6 RA default route";
after = [ waitOnline ];
requires = [ waitOnline ];
script = ''
# Seems like we can sometimes pick up a default route somehow...
${pkgs.iproute2}/bin/ip -6 route del default via fe80::1 || true
'';
serviceConfig.Type = "oneshot";
wantedBy = [ "multi-user.target" ];
};
}; };
systemd.network = { systemd.network = {
@@ -213,8 +225,8 @@ in
networkConfig = networkd.noL3; networkConfig = networkd.noL3;
extraConfig = '' extraConfig = ''
[CAKE] [CAKE]
Bandwidth=235M Bandwidth=490M
RTTSec=10ms RTTSec=50ms
PriorityQueueingPreset=besteffort PriorityQueueingPreset=besteffort
# DOCSIS preset # DOCSIS preset
OverheadBytes=18 OverheadBytes=18
@@ -237,8 +249,8 @@ in
extraConfig = '' extraConfig = ''
[CAKE] [CAKE]
Parent=root Parent=root
Bandwidth=24M Bandwidth=48M
RTTSec=1ms RTTSec=50ms
''; '';
} }
]; ];
@@ -262,7 +274,7 @@ in
{ {
matchConfig.Name = "as211024"; matchConfig.Name = "as211024";
networkConfig.IPv6AcceptRA = mkForce false; networkConfig.IPv6AcceptRA = mkForce false;
routes = map (r: { routeConfig = r; }) [ routes = [
{ {
Destination = lib.my.c.colony.prefixes.all.v4; Destination = lib.my.c.colony.prefixes.all.v4;
Gateway = allAssignments.estuary.as211024.ipv4.address; Gateway = allAssignments.estuary.as211024.ipv4.address;
@@ -287,7 +299,7 @@ in
{ {
"60-lan-hi" = { "60-lan-hi" = {
routes = map (r: { routeConfig = r; }) [ routes = [
{ {
Destination = elemAt routersPubV4 otherIndex; Destination = elemAt routersPubV4 otherIndex;
Gateway = net.cidr.host (otherIndex + 1) prefixes.hi.v4; Gateway = net.cidr.host (otherIndex + 1) prefixes.hi.v4;
@@ -358,6 +370,16 @@ in
return return
} }
chain forward-early {
type filter hook forward priority -1; policy accept;
# MSS clamping to workaround IPv6 PMTUD being broken...
tcp flags syn tcp option maxseg size set rt mtu counter
# More Disney+ discrimination...
# TODO: This prefix could change (random AWS block)
ip6 daddr 2600:9000:2245::/48 drop
}
chain forward { chain forward {
${lib.my.c.as211024.nftTrust} ${lib.my.c.as211024.nftTrust}
iifname lan-untrusted jump filter-untrusted iifname lan-untrusted jump filter-untrusted
@@ -0,0 +1,74 @@
# Blocklist for LG WebOS Services (US)
ad.lgappstv.com
ibis.lgappstv.com
info.lgsmartad.com
lgtvsdp.com
ngfts.lge.com
rdx2.lgtvsdp.com
smartshare.lgtvsdp.com
lgappstv.com
us.ad.lgsmartad.com
us.ibs.lgappstv.com
us.info.lgsmartad.com
us.lgtvsdp.com
# Community Contributions
lgad.cjpowercast.com
edgesuite.net
yumenetworks.com
smartclip.net
smartclip.com
# Non-US Entries
rdx2.lgtvsdp.com
info.lgsmartad.com
ibs.lgappstv.com
lgtvsdp.com
lgappstv.com
smartshare.lgtvsdp.com
# Full Block for Europe and Other Regions
de.ad.lgsmartad.com
de.emp.lgsmartplatform.com
de.ibs.lgappstv.com
de.info.lgsmartad.com
de.lgeapi.com
de.lgtvsdp.com
de.rdx2.lgtvsdp.com
eu.ad.lgsmartad.com
eu.ibs.lgappstv.com
eu.info.lgsmartad.com
app-lgwebos.pluto.tv
it.lgtvsdp.com
it.lgeapi.com
it.emp.lgsmartplatform.com
# LG ThinQ Services
eic.common.lgthinq.com
eic.iotservice.lgthinq.com
eic.service.lgthinq.com
eic.ngfts.lge.com
eic.svc-lgthinq-com.aws-thinq-prd.net
eic.cdpsvc.lgtvcommon.com
eic.cdpbeacon.lgtvcommon.com
eic.cdplauncher.lgtvcommon.com
eic.homeprv.lgtvcommon.com
eic.lgtviot.com
eic.nudge.lgtvcommon.com
eic.rdl.lgtvcommon.com
eic.recommend.lgtvcommon.com
eic.service.lgtvcommon.com
gb-lgeapi-com.esi-prd.net
gb.lgeapi.com
lgtvonline.lge.com
lg-channelplus-de-beacons.xumo.com
lg-channelplus-de-mds.xumo.com
lg-channelplus-eu-beacons.xumo.com
lg-channelplus-eu-mds.xumo.com
kr-op-v2.lgthinqhome.com
ngfts.lge.com
noti.lgthinq.com
objectcontent.lgthinq.com
# Update Server Block
#snu.lge.com
+66 -21
View File
@@ -19,7 +19,7 @@ in
owner = "pdns"; owner = "pdns";
group = "pdns"; group = "pdns";
}; };
"home/pdns/recursor.conf" = { "home/pdns/recursor.yml" = {
owner = "pdns-recursor"; owner = "pdns-recursor";
group = "pdns-recursor"; group = "pdns-recursor";
}; };
@@ -28,39 +28,78 @@ in
pdns.recursor = { pdns.recursor = {
enable = true; enable = true;
extraSettingsFile = config.age.secrets."home/pdns/recursor.conf".path; extraSettingsFile = config.age.secrets."home/pdns/recursor.yml".path;
}; };
}; };
services = { services = {
pdns-recursor = { pdns-recursor = {
dns = { settings = {
address = [ incoming = {
listen = [
"127.0.0.1" "::1" "127.0.0.1" "::1"
assignments.hi.ipv4.address assignments.hi.ipv6.address assignments.hi.ipv4.address assignments.hi.ipv6.address
assignments.lo.ipv4.address assignments.lo.ipv6.address assignments.lo.ipv4.address assignments.lo.ipv6.address
]; ];
allowFrom = [ allow_from = [
"127.0.0.0/8" "::1/128" "127.0.0.0/8" "::1/128"
prefixes.hi.v4 prefixes.hi.v6 prefixes.hi.v4 prefixes.hi.v6
prefixes.lo.v4 prefixes.lo.v6 prefixes.lo.v4 prefixes.lo.v6
] ++ (with lib.my.c.tailscale.prefix; [ v4 v6 ]); ] ++ (with lib.my.c.tailscale.prefix; [ v4 v6 ]);
};
settings = {
query-local-address = [
"0.0.0.0"
"::"
];
forward-zones = map (z: "${z}=127.0.0.1:5353") authZones;
# DNS NOTIFY messages override TTL # DNS NOTIFY messages override TTL
allow-notify-for = authZones; allow_notify_for = authZones;
allow-notify-from = [ "127.0.0.0/8" "::1/128" ]; allow_notify_from = [ "127.0.0.0/8" "::1/128" ];
};
outgoing = {
source_address = [ "0.0.0.0" "::" ];
};
recursor = {
forward_zones = map (z: {
zone = z;
forwarders = [ "127.0.0.1:5353" ];
}) authZones;
lua_dns_script = pkgs.writeText "pdns-script.lua" ''
blocklist = newDS()
function preresolve(dq)
local name = dq.qname:toString()
-- Disney+ doesn't like our IP space...
if dq.qtype == pdns.AAAA and (string.find(name, "disneyplus") or string.find(name, "disney-plus") or string.find(name , "disney.api")) then
dq.rcode = 0
return true
end
if blocklist:check(dq.qname) then
if dq.qtype == pdns.A then
dq:addAnswer(dq.qtype, "127.0.0.1")
elseif dq.qtype == pdns.AAAA then
dq:addAnswer(dq.qtype, "::1")
end
return true
end
return false
end
for line in io.lines("${./dns-blocklist.txt}") do
entry = line:gsub("%s+", "")
if entry ~= "" and string.sub(entry, 1, 1) ~= "#" then
blocklist:add(entry)
end
end
'';
};
webservice = {
webserver = true; webserver = true;
webserver-address = "::"; address = "::";
webserver-allow-from = [ "127.0.0.1" "::1" ]; allow_from = [ "127.0.0.1" "::1" ];
};
}; };
}; };
}; };
@@ -157,8 +196,9 @@ in
hostname = "${otherName}.${config.networking.domain}"; hostname = "${otherName}.${config.networking.domain}";
server = net.cidr.host (otherIndex + 1) prefixes.hi.v4; server = net.cidr.host (otherIndex + 1) prefixes.hi.v4;
}} }}
${elemAt routers 0} IN AAAA ${net.cidr.host 1 prefixes.hi.v6} ${elemAt routers 0} IN AAAA ${allAssignments."${elemAt routers 0}".as211024.ipv6.address}
${elemAt routers 1} IN AAAA ${net.cidr.host 2 prefixes.hi.v6} ${elemAt routers 1} IN AAAA ${allAssignments."${elemAt routers 1}".as211024.ipv6.address}
boot IN CNAME river-hi.${config.networking.domain}.
@ IN NS ns1 @ IN NS ns1
@ IN NS ns2 @ IN NS ns2
@@ -182,14 +222,19 @@ in
dave-lo IN A ${net.cidr.host 11 prefixes.lo.v4} dave-lo IN A ${net.cidr.host 11 prefixes.lo.v4}
dave-lo IN AAAA ${net.cidr.host (65536+2) prefixes.lo.v6} dave-lo IN AAAA ${net.cidr.host (65536+2) prefixes.lo.v6}
;ap0 IN A ${net.cidr.host 12 prefixes.hi.v4} shytzel IN A ${net.cidr.host 12 prefixes.core.v4}
;ap0 IN AAAA ${net.cidr.host (65536+3) prefixes.hi.v6}
wave IN A ${net.cidr.host 12 prefixes.hi.v4}
; wave IN AAAA ${net.cidr.host (65536+3) prefixes.hi.v6}
vibe IN A ${net.cidr.host 13 prefixes.hi.v4} vibe IN A ${net.cidr.host 13 prefixes.hi.v4}
vibe IN AAAA ${net.cidr.host (65536+4) prefixes.hi.v6} vibe IN AAAA ${net.cidr.host (65536+4) prefixes.hi.v6}
ups IN A ${net.cidr.host 20 prefixes.lo.v4} ups IN A ${net.cidr.host 20 prefixes.lo.v4}
palace-kvm IN A ${net.cidr.host 21 prefixes.lo.v4} palace-kvm IN A ${net.cidr.host 21 prefixes.lo.v4}
reolink-living-room IN A ${net.cidr.host 45 prefixes.lo.v4}
nixlight IN A ${net.cidr.host 46 prefixes.lo.v4}
${lib.my.dns.fwdRecords { ${lib.my.dns.fwdRecords {
inherit allAssignments names; inherit allAssignments names;
domain = config.networking.domain; domain = config.networking.domain;
+12 -7
View File
@@ -2,7 +2,7 @@
import argparse import argparse
import subprocess import subprocess
import CloudFlare import cloudflare
def main(): def main():
parser = argparse.ArgumentParser(description='Cloudflare DNS update script') parser = argparse.ArgumentParser(description='Cloudflare DNS update script')
@@ -19,17 +19,22 @@ def main():
if args.api_token_file: if args.api_token_file:
with open(args.api_token_file) as f: with open(args.api_token_file) as f:
cf_token = f.readline().strip() cf_token = f.readline().strip()
cf = cloudflare.Cloudflare(api_token=cf_token)
cf = CloudFlare.CloudFlare(token=cf_token) zones = list(cf.zones.list(name=args.zone))
zones = cf.zones.get(params={'name': args.zone})
assert zones, f'Zone {args.zone} not found' assert zones, f'Zone {args.zone} not found'
records = cf.zones.dns_records.get(zones[0]['id'], params={'name': args.record}) assert len(zones) == 1, f'More than one zone found for {args.zone}'
zone = zones[0]
records = list(cf.dns.records.list(zone_id=zone.id, name=args.record, type='A'))
assert records, f'Record {args.record} not found in zone {args.zone}' assert records, f'Record {args.record} not found in zone {args.zone}'
assert len(records) == 1, f'More than one record found for {args.record}'
record = records[0]
print(f'Updating {args.record} -> {address}') print(f'Updating {args.record} -> {address}')
cf.zones.dns_records.patch( cf.dns.records.edit(
zones[0]['id'], records[0]['id'], zone_id=zone.id, dns_record_id=record.id, name=args.record, ttl=60,
data={'type': 'A', 'name': args.record, 'content': address}) type='A', content=address)
if __name__ == '__main__': if __name__ == '__main__':
main() main()
+68 -3
View File
@@ -1,7 +1,7 @@
index: { lib, pkgs, assignments, ... }: index: { lib, pkgs, config, assignments, allAssignments, ... }:
let let
inherit (lib) mkForce; inherit (lib) mkForce;
inherit (lib.my) net; inherit (lib.my) net netbootKeaClientClasses;
inherit (lib.my.c.home) domain prefixes vips hiMTU; inherit (lib.my.c.home) domain prefixes vips hiMTU;
dns-servers = [ dns-servers = [
@@ -26,7 +26,11 @@ in
}; };
systemd.services = { systemd.services = {
kea-dhcp4-server.serviceConfig.DynamicUser = mkForce false; kea-dhcp4-server.serviceConfig = {
# Sometimes interfaces might not be ready in time and Kea doesn't like that
Restart = "on-failure";
DynamicUser = mkForce false;
};
kea-dhcp-ddns-server.serviceConfig.DynamicUser = mkForce false; kea-dhcp-ddns-server.serviceConfig.DynamicUser = mkForce false;
}; };
@@ -59,6 +63,14 @@ in
always-send = true; always-send = true;
} }
]; ];
client-classes = netbootKeaClientClasses {
tftpIP = allAssignments.river.lo.ipv4.address;
hostname = "boot.${domain}";
systems = {
sfh = "52:54:00:a5:7e:93";
castle = "c8:7f:54:6e:17:0f";
};
};
subnet4 = [ subnet4 = [
{ {
id = 1; id = 1;
@@ -120,6 +132,59 @@ in
hw-address = "24:8a:07:a8:fe:3a"; hw-address = "24:8a:07:a8:fe:3a";
ip-address = net.cidr.host 40 prefixes.lo.v4; ip-address = net.cidr.host 40 prefixes.lo.v4;
} }
{
# avr
hw-address = "8c:a9:6f:30:03:6b";
ip-address = net.cidr.host 41 prefixes.lo.v4;
}
{
# tv
hw-address = "00:a1:59:b8:4d:86";
ip-address = net.cidr.host 42 prefixes.lo.v4;
}
{
# android tv
hw-address = "b8:7b:d4:95:c6:74";
ip-address = net.cidr.host 43 prefixes.lo.v4;
}
{
# hass-panel
hw-address = "80:30:49:cd:d7:51";
ip-address = net.cidr.host 44 prefixes.lo.v4;
}
{
# reolink-living-room
hw-address = "ec:71:db:30:69:a4";
ip-address = net.cidr.host 45 prefixes.lo.v4;
}
{
# nixlight
hw-address = "00:4b:12:3b:d3:14";
ip-address = net.cidr.host 46 prefixes.lo.v4;
}
];
}
{
id = 3;
subnet = prefixes.untrusted.v4;
interface = "lan-untrusted";
option-data = [
{
name = "routers";
data = vips.untrusted.v4;
}
{
name = "domain-name-servers";
data = "1.1.1.1, 1.0.0.1";
}
];
pools = [
{
pool = if index == 0
then "192.168.80.10 - 192.168.80.127"
else "192.168.80.128 - 192.168.80.250";
}
]; ];
} }
]; ];
+48 -17
View File
@@ -1,52 +1,83 @@
index: { lib, pkgs, config, ... }: index: { lib, pkgs, config, ... }:
let let
inherit (builtins) attrNames concatMap; inherit (builtins) attrNames concatMap length;
inherit (lib) optional; inherit (lib) optional concatMapStringsSep;
inherit (lib.my) net; inherit (lib.my) net;
inherit (lib.my.c.home) prefixes vips; inherit (lib.my.c.home) prefixes vips;
pingScriptFor = name: ips:
let
script' = pkgs.writeShellScript
"keepalived-ping-${name}"
(concatMapStringsSep " || " (ip: "${pkgs.iputils}/bin/ping -qnc 1 -W 1 ${ip}") ips);
in
{
script = toString script';
interval = 1;
timeout = (length ips) + 1;
rise = 3;
fall = 3;
};
vlanIface = vlan: if vlan == "as211024" then vlan else "lan-${vlan}"; vlanIface = vlan: if vlan == "as211024" then vlan else "lan-${vlan}";
vrrpIPs = family: concatMap (vlan: [ vrrpIPs = family: concatMap (vlan: [
{ {
addr = "${vips.${vlan}.${family}}/${toString (net.cidr.length prefixes.${vlan}.${family})}"; addr = "${vips.${vlan}.${family}}/${toString (net.cidr.length prefixes.${vlan}.${family})}";
dev = vlanIface vlan; dev = vlanIface vlan;
} }
] ++ (optional (family == "v6") { ]) (attrNames vips);
addr = "fe80::1/64";
dev = vlanIface vlan;
})) (attrNames vips);
mkVRRP = family: routerId: { mkVRRP = family: routerId: {
state = if index == 0 then "MASTER" else "BACKUP"; state = if index == 0 then "MASTER" else "BACKUP";
interface = "lan-core"; interface = "lan-core";
priority = 255 - index; priority = 255 - index;
virtualRouterId = routerId; virtualRouterId = routerId;
virtualIps = vrrpIPs family; virtualIps = vrrpIPs family;
extraConfig = '' trackScripts = [ "${family}Alive" ];
notify_master "${config.systemd.package}/bin/systemctl start radvd.service"
notify_backup "${config.systemd.package}/bin/systemctl stop radvd.service"
'';
}; };
in in
{ {
users = with lib.my.c.ids; {
users.keepalived_script = {
uid = uids.keepalived_script;
isSystemUser = true;
group = "keepalived_script";
};
groups.keepalived_script.gid = gids.keepalived_script;
};
services = { services = {
keepalived = { keepalived = {
enable = true; enable = true;
enableScriptSecurity = true;
extraGlobalDefs = '' extraGlobalDefs = ''
vrrp_version 3 vrrp_version 3
nftables keepalived nftables keepalived
''; '';
vrrpScripts = {
v4Alive = pingScriptFor "v4" [ "1.1.1.1" "8.8.8.8" "216.218.236.2" ];
v6Alive = pingScriptFor "v6" [ "2606:4700:4700::1111" "2001:4860:4860::8888" "2600::" ];
};
vrrpInstances = { vrrpInstances = {
v4 = mkVRRP "v4" 51; v4 = mkVRRP "v4" 51;
v6 = mkVRRP "v6" 52; v6 = (mkVRRP "v6" 52) // {
};
extraConfig = '' extraConfig = ''
vrrp_sync_group main { virtual_ipaddress_excluded {
group { ${concatMapStringsSep "\n" (vlan: "fe80::1/64 dev ${vlanIface vlan}") (attrNames vips)}
v4
v6
}
} }
notify_master "${config.systemd.package}/bin/systemctl start radvd.service" root
notify_backup "${config.systemd.package}/bin/systemctl stop radvd.service" root
''; '';
}; };
}; };
# Actually disable this for now, don't want to fault IPv4 just because IPv6 is broken...
# extraConfig = ''
# vrrp_sync_group main {
# group {
# v4
# v6
# }
# }
# '';
};
};
} }
+7 -4
View File
@@ -3,11 +3,14 @@ let
# TODO: Move into nixpkgs # TODO: Move into nixpkgs
mstpd = pkgs.mstpd.overrideAttrs { mstpd = pkgs.mstpd.overrideAttrs {
patches = [ ./mstpd.patch ]; patches = [ ./mstpd.patch ];
# Delete postInstall since it nukes the bridge-stp script we need
postInstall = "";
}; };
in in
{ {
environment = { environment = {
systemPackages = [ systemPackages = [
# For kernel to call bridge-stp (see ./pkgs/os-specific/linux/kernel/bridge-stp-helper.patch)
mstpd mstpd
]; ];
etc = { etc = {
@@ -24,8 +27,8 @@ in
onState = [ "routable" ]; onState = [ "routable" ];
script = '' script = ''
#!${pkgs.runtimeShell} #!${pkgs.runtimeShell}
if [ $IFACE = "lan" ]; then if [ "$IFACE" = "lan" ]; then
${mstpd}/sbin/mstpctl setforcevers $IFACE rstp ${mstpd}/sbin/mstpctl setforcevers "$IFACE" rstp
fi fi
''; '';
}; };
@@ -39,8 +42,8 @@ in
before = [ "network-pre.target" ]; before = [ "network-pre.target" ];
serviceConfig = { serviceConfig = {
Type = "forking"; Type = "forking";
ExecStart = "${mstpd}/sbin/bridge-stp restart"; ExecStart = "${mstpd}/bin/bridge-stp restart";
ExecReload = "${mstpd}/sbin/bridge-stp restart_config"; ExecReload = "${mstpd}/bin/bridge-stp restart_config";
PIDFile = "/run/mstpd.pid"; PIDFile = "/run/mstpd.pid";
Restart = "always"; Restart = "always";
PrivateTmp = true; PrivateTmp = true;
+33 -1
View File
@@ -43,6 +43,38 @@
}; };
}; };
services = {
mjpg-streamer = {
enable = false;
inputPlugin = "input_uvc.so";
outputPlugin = "output_http.so -w @www@ -n -p 5050";
};
octoprint = {
enable = false;
host = "::";
extraConfig = {
plugins = {
classicwebcam = {
snapshot = "/webcam/?action=snapshot";
stream = "/webcam/?action=stream";
streamRatio = "4:3";
};
};
serial = {
port = "/dev/ttyACM0";
baudrate = 115200;
};
temperature.profiles = [
{
bed = 60;
extruder = 215;
name = "PLA";
}
];
};
};
};
systemd.network = { systemd.network = {
netdevs = { netdevs = {
"25-lan" = { "25-lan" = {
@@ -123,7 +155,7 @@
key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPYTB4zeAqotrEJ8M+AiGm/s9PFsWlAodz3hYSROGuDb"; key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPYTB4zeAqotrEJ8M+AiGm/s9PFsWlAodz3hYSROGuDb";
}; };
server.enable = true; server.enable = true;
deploy.node.hostname = "192.168.68.2"; # deploy.node.hostname = "192.168.68.2";
}; };
}; };
}; };
@@ -26,10 +26,10 @@ in
config = { config = {
# Hardware acceleration for Jellyfin # Hardware acceleration for Jellyfin
hardware.opengl = { hardware.graphics = {
enable = true; enable = true;
extraPackages = with pkgs; [ extraPackages = with pkgs; [
vaapiIntel intel-vaapi-driver
intel-ocl intel-ocl
]; ];
}; };
@@ -65,16 +65,31 @@ in
systemd = { systemd = {
services = { services = {
jackett.bindsTo = [ "systemd-networkd-wait-online@vpn.service" ]; jackett.bindsTo = [ "systemd-networkd-wait-online@vpn.service" ];
transmission.bindsTo = [ "systemd-networkd-wait-online@vpn.service" ];
radarr.serviceConfig.UMask = "0002"; transmission.bindsTo = [ "systemd-networkd-wait-online@vpn.service" ];
sonarr.serviceConfig.UMask = "0002"; # https://github.com/NixOS/nixpkgs/issues/258793#issuecomment-1748168206
transmission.serviceConfig = {
RootDirectoryStartOnly = lib.mkForce false;
RootDirectory = lib.mkForce "";
};
radarr.serviceConfig.UMask = lib.mkForce "0002";
sonarr.serviceConfig.UMask = lib.mkForce "0002";
}; };
}; };
nixpkgs.config.permittedInsecurePackages = [
# FIXME: This is needed for Sonarr
"aspnetcore-runtime-wrapped-6.0.36"
"aspnetcore-runtime-6.0.36"
"dotnet-sdk-wrapped-6.0.428"
"dotnet-sdk-6.0.428"
];
services = { services = {
transmission = { transmission = {
enable = true; enable = true;
package = pkgs.transmission_4;
downloadDirPermissions = null; downloadDirPermissions = null;
performanceNetParameters = true; performanceNetParameters = true;
settings = { settings = {
@@ -2,6 +2,7 @@
let let
inherit (lib) mkMerge mkIf; inherit (lib) mkMerge mkIf;
inherit (lib.my) networkdAssignment; inherit (lib.my) networkdAssignment;
inherit (lib.my.c.kelder) ipv4MTU;
wg = { wg = {
keyFile = "kelder/acquisition/airvpn-privkey"; keyFile = "kelder/acquisition/airvpn-privkey";
@@ -72,14 +73,12 @@ in
RouteTable = routeTable; RouteTable = routeTable;
}; };
wireguardPeers = [ wireguardPeers = [
{
# AirVPN IE # AirVPN IE
wireguardPeerConfig = { {
Endpoint = "146.70.94.2:1637"; Endpoint = "146.70.94.2:1637";
PublicKey = "PyLCXAQT8KkM4T+dUsOQfn+Ub3pGxfGlxkIApuig+hk="; PublicKey = "PyLCXAQT8KkM4T+dUsOQfn+Ub3pGxfGlxkIApuig+hk=";
PresharedKeyFile = config.age.secrets."${pskFile}".path; PresharedKeyFile = config.age.secrets."${pskFile}".path;
AllowedIPs = [ "0.0.0.0/0" "::/0" ]; AllowedIPs = [ "0.0.0.0/0" "::/0" ];
};
} }
]; ];
}; };
@@ -89,13 +88,14 @@ in
(networkdAssignment "host0" assignments.internal) (networkdAssignment "host0" assignments.internal)
{ {
networkConfig.DNSDefaultRoute = false; networkConfig.DNSDefaultRoute = false;
linkConfig.MTUBytes = toString ipv4MTU;
} }
]; ];
"90-vpn" = with wg; { "90-vpn" = with wg; {
matchConfig.Name = "vpn"; matchConfig.Name = "vpn";
address = [ "10.161.170.28/32" "fd7d:76ee:e68f:a993:b12d:6d15:c80a:9516/128" ]; address = [ "10.161.170.28/32" "fd7d:76ee:e68f:a993:b12d:6d15:c80a:9516/128" ];
dns = [ "10.128.0.1" "fd7d:76ee:e68f:a993::1" ]; dns = [ "10.128.0.1" "fd7d:76ee:e68f:a993::1" ];
routingPolicyRules = map (r: { routingPolicyRuleConfig = r; }) [ routingPolicyRules = [
{ {
Family = "both"; Family = "both";
SuppressPrefixLength = 0; SuppressPrefixLength = 0;
@@ -60,7 +60,7 @@ in
"*.${domain}" "*.${domain}"
]; ];
dnsProvider = "cloudflare"; dnsProvider = "cloudflare";
credentialsFile = config.age.secrets."kelder/cloudflare-credentials.conf".path; environmentFile = config.age.secrets."kelder/cloudflare-credentials.conf".path;
}; };
}; };
}; };
@@ -88,21 +88,23 @@ in
}; };
services = { services = {
resolved.extraConfig = mkForce ""; resolved.settings.Resolve = mkForce { };
nextcloud = { nextcloud = {
enable = true; enable = true;
package = pkgs.nextcloud28; # TODO: Might need to do some bullshit to go from Nextcloud 28 (?) to 32
package = pkgs.nextcloud32;
datadir = "/mnt/storage/nextcloud"; datadir = "/mnt/storage/nextcloud";
hostName = "cloud.${domain}"; hostName = "cloud.${domain}";
https = true; https = true;
config = { config = {
extraTrustedDomains = [ "cloud-local.${domain}" ];
adminpassFile = config.age.secrets."kelder/nextcloud-root.txt".path; adminpassFile = config.age.secrets."kelder/nextcloud-root.txt".path;
defaultPhoneRegion = "IE"; dbtype = "sqlite";
}; };
extraOptions = { settings = {
updatechecker = false; updatechecker = false;
trusted_domains = [ "cloud-local.${domain}" ];
default_phone_region = "IE";
}; };
}; };
}; };
+6 -11
View File
@@ -13,11 +13,6 @@ in
owner = "nginx"; owner = "nginx";
group = "nginx"; group = "nginx";
}; };
"dhparams.pem" = {
owner = "acme";
group = "acme";
mode = "440";
};
}; };
firewall = { firewall = {
@@ -35,7 +30,6 @@ in
recommendedTlsSettings = true; recommendedTlsSettings = true;
clientMaxBodySize = "0"; clientMaxBodySize = "0";
serverTokens = true; serverTokens = true;
sslDhparam = config.age.secrets."dhparams.pem".path;
# Based on recommended*Settings, but probably better to be explicit about these # Based on recommended*Settings, but probably better to be explicit about these
appendHttpConfig = '' appendHttpConfig = ''
@@ -84,6 +78,7 @@ in
c c
]; ];
acquisition = "http://${allAssignments.kelder-acquisition.internal.ipv4.address}"; acquisition = "http://${allAssignments.kelder-acquisition.internal.ipv4.address}";
# This is kinda borked because Virgin Media filters DNS responses with local IPs...
localRedirect = to: '' localRedirect = to: ''
rewrite_by_lua_block { rewrite_by_lua_block {
if ngx.var.remote_addr == pub_ip then if ngx.var.remote_addr == pub_ip then
@@ -103,7 +98,7 @@ in
"monitor.${domain}" = withAuth { "monitor.${domain}" = withAuth {
serverAliases = [ "monitor-local.${domain}" ]; serverAliases = [ "monitor-local.${domain}" ];
extraConfig = localRedirect "monitor-local.${domain}"; # extraConfig = localRedirect "monitor-local.${domain}";
locations = { locations = {
"/" = { "/" = {
proxyPass = "http://${allAssignments.kelder.ctrs.ipv4.address}:19999"; proxyPass = "http://${allAssignments.kelder.ctrs.ipv4.address}:19999";
@@ -136,17 +131,17 @@ in
}; };
"torrents.${domain}" = withAuth { "torrents.${domain}" = withAuth {
serverAliases = [ "torrents-local.${domain}" ]; serverAliases = [ "torrents-local.${domain}" ];
extraConfig = localRedirect "torrents-local.${domain}"; # extraConfig = localRedirect "torrents-local.${domain}";
locations."/".proxyPass = "${acquisition}:9091"; locations."/".proxyPass = "${acquisition}:9091";
}; };
"jackett.${domain}" = withAuth { "jackett.${domain}" = withAuth {
serverAliases = [ "jackett-local.${domain}" ]; serverAliases = [ "jackett-local.${domain}" ];
extraConfig = localRedirect "jackett-local.${domain}"; # extraConfig = localRedirect "jackett-local.${domain}";
locations."/".proxyPass = "${acquisition}:9117"; locations."/".proxyPass = "${acquisition}:9117";
}; };
"radarr.${domain}" = withAuth { "radarr.${domain}" = withAuth {
serverAliases = [ "radarr-local.${domain}" ]; serverAliases = [ "radarr-local.${domain}" ];
extraConfig = localRedirect "radarr-local.${domain}"; # extraConfig = localRedirect "radarr-local.${domain}";
locations."/" = { locations."/" = {
proxyPass = "${acquisition}:7878"; proxyPass = "${acquisition}:7878";
proxyWebsockets = true; proxyWebsockets = true;
@@ -155,7 +150,7 @@ in
}; };
"sonarr.${domain}" = withAuth { "sonarr.${domain}" = withAuth {
serverAliases = [ "sonarr-local.${domain}" ]; serverAliases = [ "sonarr-local.${domain}" ];
extraConfig = localRedirect "sonarr-local.${domain}"; # extraConfig = localRedirect "sonarr-local.${domain}";
locations."/" = { locations."/" = {
proxyPass = "${acquisition}:8989"; proxyPass = "${acquisition}:8989";
proxyWebsockets = true; proxyWebsockets = true;
+8 -8
View File
@@ -1,7 +1,7 @@
{ lib, ... }: { lib, ... }:
let let
inherit (lib.my) net; inherit (lib.my) net;
inherit (lib.my.c.kelder) domain prefixes; inherit (lib.my.c.kelder) domain prefixes ipv4MTU;
in in
{ {
imports = [ ./containers ]; imports = [ ./containers ];
@@ -121,8 +121,7 @@ in
samba = { samba = {
enable = true; enable = true;
enableNmbd = true; settings = {
shares = {
storage = { storage = {
path = "/mnt/storage"; path = "/mnt/storage";
browseable = "yes"; browseable = "yes";
@@ -131,6 +130,8 @@ in
"directory mask" = "0775"; "directory mask" = "0775";
}; };
}; };
nmbd.enable = true;
}; };
samba-wsdd.enable = true; samba-wsdd.enable = true;
@@ -180,12 +181,10 @@ in
}; };
wireguardPeers = [ wireguardPeers = [
{ {
wireguardPeerConfig = {
PublicKey = "bP1XUNxp9i8NLOXhgPaIaRzRwi5APbam44/xjvYcyjU="; PublicKey = "bP1XUNxp9i8NLOXhgPaIaRzRwi5APbam44/xjvYcyjU=";
Endpoint = "estuary-vm.${lib.my.c.colony.domain}:${toString lib.my.c.kelder.vpn.port}"; Endpoint = "${allAssignments.estuary.internal.ipv4.address}:${toString lib.my.c.kelder.vpn.port}";
AllowedIPs = [ "0.0.0.0/0" ]; AllowedIPs = [ "0.0.0.0/0" ];
PersistentKeepalive = 25; PersistentKeepalive = 25;
};
} }
]; ];
}; };
@@ -200,6 +199,7 @@ in
"50-lan" = { "50-lan" = {
matchConfig.Name = "et1g0"; matchConfig.Name = "et1g0";
DHCP = "yes"; DHCP = "yes";
linkConfig.MTUBytes = toString ipv4MTU;
}; };
"80-ctrs" = mkMerge [ "80-ctrs" = mkMerge [
(networkdAssignment "ctrs" assignments.ctrs) (networkdAssignment "ctrs" assignments.ctrs)
@@ -212,7 +212,7 @@ in
address = with assignments.estuary; [ address = with assignments.estuary; [
(with ipv4; "${address}/${toString mask}") (with ipv4; "${address}/${toString mask}")
]; ];
routingPolicyRules = map (r: { routingPolicyRuleConfig = r; }) [ routingPolicyRules = [
{ {
Family = "both"; Family = "both";
SuppressPrefixLength = 0; SuppressPrefixLength = 0;
@@ -272,7 +272,7 @@ in
config.name = "kontent"; config.name = "kontent";
}; };
#deploy.node.hostname = "10.16.9.21"; # deploy.node.hostname = "192.168.0.69";
secrets = { secrets = {
key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOFvUdJshXkqmchEgkZDn5rgtZ1NO9vbd6Px+S6YioWi"; key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOFvUdJshXkqmchEgkZDn5rgtZ1NO9vbd6Px+S6YioWi";
files = { files = {
+6 -13
View File
@@ -14,7 +14,7 @@
cpu = { cpu = {
intel.updateMicrocode = true; intel.updateMicrocode = true;
}; };
opengl.extraPackages = with pkgs; [ graphics.extraPackages = with pkgs; [
intel-media-driver intel-media-driver
]; ];
bluetooth.enable = true; bluetooth.enable = true;
@@ -99,12 +99,6 @@
}; };
}; };
resolved = {
enable = true;
extraConfig = mkForce "";
dnssec = "false";
};
fprintd.enable = true; fprintd.enable = true;
blueman.enable = true; blueman.enable = true;
@@ -118,7 +112,7 @@
steam.enable = true; steam.enable = true;
wireshark = { wireshark = {
enable = true; enable = true;
package = pkgs.wireshark-qt; package = pkgs.wireshark;
}; };
}; };
@@ -129,10 +123,9 @@
wifi = { wifi = {
backend = "wpa_supplicant"; backend = "wpa_supplicant";
}; };
extraConfig = '' settings = {
[main] main.no-auto-default = "*";
no-auto-default=* };
'';
}; };
}; };
@@ -178,7 +171,7 @@
programs = { programs = {
fish = { fish = {
shellAbbrs = { shellAbbrs = {
tsup = "doas tailscale up --login-server=https://ts.nul.ie --accept-routes"; tsup = "doas tailscale up --login-server=https://hs.nul.ie --accept-routes";
}; };
}; };
}; };
+3 -2
View File
@@ -23,7 +23,7 @@ let
pkgs = pkgs'.${config'.nixpkgs}.${config'.system}; pkgs = pkgs'.${config'.nixpkgs}.${config'.system};
allPkgs = mapAttrs (_: p: p.${config'.system}) pkgs'; allPkgs = mapAttrs (_: p: p.${config'.system}) pkgs';
modules' = [ hmFlakes.${config'.home-manager}.nixosModule ] ++ (attrValues cfg.modules); modules' = [ hmFlakes.${config'.home-manager}.nixosModules.default ] ++ (attrValues cfg.modules);
in in
# Import eval-config ourselves since the flake now force-sets lib # Import eval-config ourselves since the flake now force-sets lib
import "${pkgsFlake}/nixos/lib/eval-config.nix" { import "${pkgsFlake}/nixos/lib/eval-config.nix" {
@@ -35,7 +35,7 @@ let
system = null; system = null;
# Put the inputs in specialArgs to avoid infinite recursion when modules try to do imports # Put the inputs in specialArgs to avoid infinite recursion when modules try to do imports
specialArgs = { inherit inputs pkgsFlakes pkgsFlake allAssignments; inherit (cfg) systems; }; specialArgs = { inherit self inputs pkgsFlakes pkgsFlake allAssignments; inherit (cfg) systems; };
# `baseModules` informs the manual which modules to document # `baseModules` informs the manual which modules to document
baseModules = baseModules =
@@ -135,6 +135,7 @@ let
ipv6 = mkBoolOpt' false "Whether this mesh's underlay operates over IPv6."; ipv6 = mkBoolOpt' false "Whether this mesh's underlay operates over IPv6.";
baseMTU = mkOpt' ints.unsigned 1500 "Base MTU to calculate VXLAN MTU with."; baseMTU = mkOpt' ints.unsigned 1500 "Base MTU to calculate VXLAN MTU with.";
l3Overhead = mkOpt' ints.unsigned 40 "Overhead of L3 header (to calculate MTU)."; l3Overhead = mkOpt' ints.unsigned 40 "Overhead of L3 header (to calculate MTU).";
udpEncapsulation = mkBoolOpt' false "Whether to encapsulate ESP frames in UDP.";
firewall = mkBoolOpt' true "Whether to generate firewall rules."; firewall = mkBoolOpt' true "Whether to generate firewall rules.";
vni = mkOpt' ints.unsigned 1 "VXLAN VNI."; vni = mkOpt' ints.unsigned 1 "VXLAN VNI.";
peers = mkOpt' (attrsOf (submodule l2PeerOpts)) { } "Peers."; peers = mkOpt' (attrsOf (submodule l2PeerOpts)) { } "Peers.";
+11 -5
View File
@@ -1,7 +1,7 @@
{ {
nixos.systems.installer = { config, ... }: { nixos.systems.installer = { config, ... }: {
system = "x86_64-linux"; system = "x86_64-linux";
nixpkgs = "unstable"; nixpkgs = "mine";
docCustom = false; docCustom = false;
rendered = config.configuration.config.my.asISO; rendered = config.configuration.config.my.asISO;
@@ -31,8 +31,10 @@
server.enable = true; server.enable = true;
}; };
image = {
baseName = "jackos-installer";
};
isoImage = { isoImage = {
isoBaseName = "jackos-installer";
volumeID = "jackos-${config.system.nixos.release}-${pkgs.stdenv.hostPlatform.uname.processor}"; volumeID = "jackos-${config.system.nixos.release}-${pkgs.stdenv.hostPlatform.uname.processor}";
edition = "devplayer0"; edition = "devplayer0";
appendToMenuLabel = " /dev/player0 Installer"; appendToMenuLabel = " /dev/player0 Installer";
@@ -52,6 +54,8 @@
home.shellAliases = { home.shellAliases = {
show-hw-config = "nixos-generate-config --show-hardware-config --root $INSTALL_ROOT"; show-hw-config = "nixos-generate-config --show-hardware-config --root $INSTALL_ROOT";
}; };
my.gui.enable = false;
}; };
services = { services = {
@@ -59,8 +63,8 @@
}; };
networking = { networking = {
# Will be set dynamically # Will be set dynamically, but need something to satisfy `/etc/os-release` stuff
hostName = ""; hostName = "installer";
useNetworkd = false; useNetworkd = false;
}; };
@@ -80,6 +84,8 @@
${pkgs.gawk}/bin/awk '{ print $1 }')" ${pkgs.gawk}/bin/awk '{ print $1 }')"
''; '';
boot.supportedFilesystems.nfs = true;
environment.systemPackages = with pkgs; [ environment.systemPackages = with pkgs; [
dhcpcd dhcpcd
lm_sensors lm_sensors
@@ -93,7 +99,7 @@
# Enable wpa_supplicant, but don't start it by default. # Enable wpa_supplicant, but don't start it by default.
networking.wireless.enable = mkDefault true; networking.wireless.enable = mkDefault true;
networking.wireless.userControlled.enable = true; networking.wireless.userControlled = true;
systemd.services.wpa_supplicant.wantedBy = mkForce []; systemd.services.wpa_supplicant.wantedBy = mkForce [];
# Tell the Nix evaluator to garbage collect more aggressively. # Tell the Nix evaluator to garbage collect more aggressively.
+2 -1
View File
@@ -14,11 +14,12 @@
network = ./network.nix; network = ./network.nix;
pdns = ./pdns.nix; pdns = ./pdns.nix;
nginx-sso = ./nginx-sso.nix; nginx-sso = ./nginx-sso.nix;
gui = ./gui.nix; gui = ./gui;
l2mesh = ./l2mesh.nix; l2mesh = ./l2mesh.nix;
borgthin = ./borgthin.nix; borgthin = ./borgthin.nix;
nvme = ./nvme; nvme = ./nvme;
spdk = ./spdk.nix; spdk = ./spdk.nix;
librespeed = ./librespeed; librespeed = ./librespeed;
netboot = ./netboot;
}; };
} }
+4 -2
View File
@@ -1,4 +1,4 @@
{ lib, pkgs, config, ... }: { inputs, lib, pkgs, config, ... }:
let let
inherit (builtins) substring match; inherit (builtins) substring match;
inherit (lib) inherit (lib)
@@ -127,7 +127,9 @@ in
enable = mkBoolOpt' false "Whether to enable borgthin jobs"; enable = mkBoolOpt' false "Whether to enable borgthin jobs";
lvmPackage = mkOpt' package pkgs.lvm2 "Packge containing LVM tools"; lvmPackage = mkOpt' package pkgs.lvm2 "Packge containing LVM tools";
thinToolsPackage = mkOpt' package pkgs.thin-provisioning-tools "Package containing thin-provisioning-tools"; thinToolsPackage = mkOpt' package pkgs.thin-provisioning-tools "Package containing thin-provisioning-tools";
package = mkOpt' package pkgs.borgthin "borgthin package"; # Really we should use the version from the overlay, but the package is quite far behind...
# Not bothering to update until Borg 2.0 releases
package = mkOpt' package inputs.borgthin.packages.${config.nixpkgs.system}.borgthin "borgthin package";
jobs = mkOpt' (attrsOf jobType) { } "borgthin jobs"; jobs = mkOpt' (attrsOf jobType) { } "borgthin jobs";
}; };
+142 -10
View File
@@ -1,6 +1,6 @@
{ lib, pkgs, extendModules, modulesPath, options, config, ... }: { lib, pkgs, extendModules, modulesPath, options, config, ... }:
let let
inherit (lib) recursiveUpdate mkOption mkDefault mkIf mkMerge flatten optional; inherit (lib) recursiveUpdate mkOption mkDefault mkIf mkMerge mkForce flatten optional;
inherit (lib.my) mkBoolOpt' dummyOption; inherit (lib.my) mkBoolOpt' dummyOption;
cfg = config.my.build; cfg = config.my.build;
@@ -43,15 +43,145 @@ let
modules = flatten [ modules = flatten [
"${modulesPath}/installer/netboot/netboot.nix" "${modulesPath}/installer/netboot/netboot.nix"
allHardware allHardware
];
};
asNetboot = extendModules {
modules = flatten [
allHardware
({ pkgs, config, ... }: { ({ pkgs, config, ... }: {
system.build.netbootArchive = pkgs.runCommand "netboot-${config.system.name}-archive.tar" { } '' boot = {
${pkgs.gnutar}/bin/tar -rvC "${config.system.build.kernel}" \ loader.grub.enable = false;
-f "$out" "${config.system.boot.loader.kernelFile}" initrd = {
${pkgs.gnutar}/bin/tar -rvC "${config.system.build.netbootRamdisk}" \ kernelModules = [ "nbd" ];
-f "$out" initrd availableKernelModules = [ "igb" "igc" ];
${pkgs.gnutar}/bin/tar -rvC "${config.system.build.netbootIpxeScript}" \
-f "$out" netboot.ipxe systemd = {
storePaths = with pkgs; [
gnused
nbd
netcat
];
extraBin = with pkgs; {
dmesg = "${util-linux}/bin/dmesg";
ip = "${iproute2}/bin/ip";
nbd-client = "${nbd}/bin/nbd-client";
};
extraConfig = ''
DefaultTimeoutStartSec=20
DefaultDeviceTimeoutSec=20
''; '';
network = {
enable = true;
wait-online.enable = true;
networks."10-netboot" = {
matchConfig.Name = "et-boot";
DHCP = "yes";
};
};
services = {
nbd = {
description = "NBD Root FS";
script = ''
get_cmdline() {
${pkgs.gnused}/bin/sed -rn "s/^.*$1=(\\S+).*\$/\\1/p" < /proc/cmdline
}
s="$(get_cmdline nbd_server)"
until ${pkgs.netcat}/bin/nc -zv "$s" 22; do
sleep 0.1
done
exec ${pkgs.nbd}/bin/nbd-client -systemd-mark -N "$(get_cmdline nbd_export)" "$s" /dev/nbd0
'';
unitConfig = {
IgnoreOnIsolate = "yes";
DefaultDependencies = "no";
};
serviceConfig = {
Type = "forking";
Restart = "on-failure";
RestartSec = 10;
};
wantedBy = [ "initrd-root-device.target" ];
};
};
};
};
postBootCommands = ''
# After booting, register the contents of the Nix store
# in the Nix database in the COW root.
${config.nix.package}/bin/nix-store --load-db < /nix-path-registration
# nixos-rebuild also requires a "system" profile and an
# /etc/NIXOS tag.
touch /etc/NIXOS
${config.nix.package.out}/bin/nix-env -p /nix/var/nix/profiles/system --set /run/current-system
'';
};
programs.nbd.enable = true;
fileSystems = {
"/" = {
fsType = "ext4";
device = "/dev/nbd0";
noCheck = true;
autoResize = true;
};
};
networking.useNetworkd = mkForce true;
systemd = {
network.networks."10-boot" = {
matchConfig.Name = "et-boot";
DHCP = "yes";
networkConfig.KeepConfiguration = "yes";
};
};
system.build = {
rootImage = pkgs.callPackage "${modulesPath}/../lib/make-ext4-fs.nix" {
storePaths = [ config.system.build.toplevel ];
volumeLabel = "netboot-root";
};
netbootScript = pkgs.writeText "boot.ipxe" ''
#!ipxe
kernel ${pkgs.stdenv.hostPlatform.linux-kernel.target} init=${config.system.build.toplevel}/init initrd=initrd ifname=et-boot:''${mac} nbd_server=''${next-server} ${toString config.boot.kernelParams} ''${cmdline}
initrd initrd
boot
'';
netbootTree = pkgs.linkFarm "netboot-${config.system.name}" [
{
name = config.system.boot.loader.kernelFile;
path = "${config.system.build.kernel}/${config.system.boot.loader.kernelFile}";
}
{
name = "initrd";
path = "${config.system.build.initialRamdisk}/initrd";
}
{
name = "rootfs.ext4";
path = config.system.build.rootImage;
}
{
name = "boot.ipxe";
path = config.system.build.netbootScript;
}
];
netbootArchive = pkgs.runCommand "netboot-${config.system.name}.tar.zst" { } ''
export PATH=${pkgs.zstd}/bin:$PATH
${pkgs.gnutar}/bin/tar --dereference --zstd -cvC ${config.system.build.netbootTree} -f "$out" .
'';
};
}) })
]; ];
}; };
@@ -77,6 +207,7 @@ in
asISO = mkAsOpt asISO "a bootable .iso image"; asISO = mkAsOpt asISO "a bootable .iso image";
asContainer = mkAsOpt asContainer "a container"; asContainer = mkAsOpt asContainer "a container";
asKexecTree = mkAsOpt asKexecTree "a kexec-able kernel and initrd"; asKexecTree = mkAsOpt asKexecTree "a kexec-able kernel and initrd";
asNetboot = mkAsOpt asNetboot "a netboot-able kernel initrd, and iPXE script";
buildAs = options.system.build; buildAs = options.system.build;
}; };
@@ -90,8 +221,8 @@ in
memorySize = dummyOption; memorySize = dummyOption;
qemu.options = dummyOption; qemu.options = dummyOption;
}; };
image.baseName = dummyOption;
isoImage = { isoImage = {
isoBaseName = dummyOption;
volumeID = dummyOption; volumeID = dummyOption;
edition = dummyOption; edition = dummyOption;
appendToMenuLabel = dummyOption; appendToMenuLabel = dummyOption;
@@ -110,7 +241,8 @@ in
iso = config.my.asISO.config.system.build.isoImage; iso = config.my.asISO.config.system.build.isoImage;
container = config.my.asContainer.config.system.build.toplevel; container = config.my.asContainer.config.system.build.toplevel;
kexecTree = config.my.asKexecTree.config.system.build.kexecTree; kexecTree = config.my.asKexecTree.config.system.build.kexecTree;
netbootArchive = config.my.asKexecTree.config.system.build.netbootArchive; netbootTree = config.my.asNetboot.config.system.build.netbootTree;
netbootArchive = config.my.asNetboot.config.system.build.netbootArchive;
}; };
}; };
}; };
+59 -19
View File
@@ -1,4 +1,4 @@
{ lib, pkgs, pkgs', inputs, config, ... }: { lib, pkgsFlake, pkgs, pkgs', self, inputs, config, ... }:
let let
inherit (lib) mkIf mkDefault mkMerge; inherit (lib) mkIf mkDefault mkMerge;
inherit (lib.my) mkDefault'; inherit (lib.my) mkDefault';
@@ -9,10 +9,11 @@ in
}; };
imports = [ imports = [
inputs.impermanence.nixosModule inputs.impermanence.nixosModules.default
inputs.ragenix.nixosModules.age inputs.ragenix.nixosModules.age
inputs.sharry.nixosModules.default inputs.sharry.nixosModules.default
inputs.attic.nixosModules.atticd inputs.copyparty.nixosModules.default
inputs.harmonia.nixosModules.harmonia
]; ];
config = mkMerge [ config = mkMerge [
@@ -37,10 +38,20 @@ in
enable = mkDefault true; enable = mkDefault true;
wheelNeedsPassword = mkDefault false; wheelNeedsPassword = mkDefault false;
}; };
# TODO: Add this to fix login
# pam = {
# services = {
# kmscon.rules. = mkIf config.services.kmscon.config.libseat {
# };
# };
# };
}; };
nix = { nix = {
package = pkgs'.mine.nix; package = pkgs'.mine.nix;
channel.enable = false;
settings = with lib.my.c.nix; { settings = with lib.my.c.nix; {
trusted-users = [ "@wheel" ]; trusted-users = [ "@wheel" ];
experimental-features = [ "nix-command" "flakes" "ca-derivations" ]; experimental-features = [ "nix-command" "flakes" "ca-derivations" ];
@@ -53,7 +64,7 @@ in
pkgs = { pkgs = {
to = { to = {
type = "path"; type = "path";
path = "${pkgs.path}"; path = "${pkgsFlake}";
}; };
exact = true; exact = true;
}; };
@@ -65,10 +76,13 @@ in
}; };
nixpkgs = { nixpkgs = {
overlays = [ overlays = [
inputs.deploy-rs.overlay inputs.deploy-rs.overlays.default
inputs.sharry.overlays.default inputs.sharry.overlays.default
inputs.borgthin.overlays.default # TODO: Re-enable when borgthin is updated
# inputs.borgthin.overlays.default
inputs.boardie.overlays.default inputs.boardie.overlays.default
inputs.copyparty.overlays.default
inputs.hass-west-wood.overlays.default
]; ];
config = { config = {
allowUnfree = true; allowUnfree = true;
@@ -127,11 +141,15 @@ in
}; };
}; };
environment.etc = {
"nixos/flake.nix".source = "/run/nixfiles/flake.nix";
};
environment.systemPackages = with pkgs; mkMerge [ environment.systemPackages = with pkgs; mkMerge [
[ [
bash-completion bash-completion
git git
unzip unzip
tcpdump
] ]
(mkIf config.services.netdata.enable [ netdata ]) (mkIf config.services.netdata.enable [ netdata ])
]; ];
@@ -142,20 +160,21 @@ in
fish.enable = mkDefault true; fish.enable = mkDefault true;
# TODO: This is expecting to look up the channel for the database... # TODO: This is expecting to look up the channel for the database...
command-not-found.enable = mkDefault false; command-not-found.enable = mkDefault false;
vim.defaultEditor = true; vim = {
enable = true;
defaultEditor = true;
};
}; };
services = { services = {
kmscon = { # TODO: Remove if-else when 26.11 releases
# As it turns out, kmscon hasn't been updated in years and has some bugs... kmscon = if (config.system.nixos.release == "26.06:u-26.11") then {
enable = mkDefault false; enable = mkDefault false;
hwRender = mkDefault true; config = {
extraOptions = "--verbose"; hwaccel = config.hardware.graphics.enable;
extraConfig = font-name = "SauceCodePro Nerd Font Mono";
''
font-name=SauceCodePro Nerd Font Mono
'';
}; };
} else { };
getty.greetingLine = mkDefault' ''<<< Welcome to ${config.system.nixos.distroName} ${config.system.nixos.label} (\m) - \l >>>''; getty.greetingLine = mkDefault' ''<<< Welcome to ${config.system.nixos.distroName} ${config.system.nixos.label} (\m) - \l >>>'';
openssh = { openssh = {
@@ -209,14 +228,35 @@ in
# python.d plugin script does #!/usr/bin/env bash # python.d plugin script does #!/usr/bin/env bash
path = with pkgs; [ bash ]; path = with pkgs; [ bash ];
}; };
nixfiles-mutable = {
description = "Mutable nixfiles";
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
};
path = with pkgs; [ util-linux ];
script = ''
nixfilesDir="${self}"
mkdir -p /run/nixfiles{,/.rw,/.work}
mount -t overlay overlay -o lowerdir="$nixfilesDir",upperdir=/run/nixfiles/.rw,workdir=/run/nixfiles/.work /run/nixfiles
chmod -R u+w /run/nixfiles
'';
preStop = ''
umount /run/nixfiles
rm -rf /run/nixfiles
'';
wantedBy = [ "multi-user.target" ];
};
}; };
}; };
} }
(mkIf config.services.kmscon.enable { (mkIf config.services.kmscon.enable {
fonts.fonts = with pkgs; [ fonts.packages = with pkgs; [
(nerdfonts.override { nerd-fonts.sauce-code-pro
fonts = [ "SourceCodePro" ];
})
]; ];
}) })
]; ];
+15 -15
View File
@@ -1,6 +1,6 @@
{ lib, pkgs, options, config, systems, ... }: { lib, pkgs, options, config, systems, ... }:
let let
inherit (builtins) attrNames attrValues all hashString toJSON; inherit (builtins) attrNames attrValues all hashString toJSON any;
inherit (lib) inherit (lib)
groupBy' mapAttrsToList optionalString optional concatMapStringsSep filterAttrs mkOption mkDefault mkIf mkMerge; groupBy' mapAttrsToList optionalString optional concatMapStringsSep filterAttrs mkOption mkDefault mkIf mkMerge;
inherit (lib.my) mkOpt' mkBoolOpt'; inherit (lib.my) mkOpt' mkBoolOpt';
@@ -15,6 +15,7 @@ let
passAsFile = [ "code" ]; passAsFile = [ "code" ];
code = '' code = ''
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include <signal.h> #include <signal.h>
#include <unistd.h> #include <unistd.h>
#include <systemd/sd-daemon.h> #include <systemd/sd-daemon.h>
@@ -98,6 +99,7 @@ let
}; };
networking = { networking = {
bridge = mkOpt' (nullOr str) null "Network bridge to connect to."; bridge = mkOpt' (nullOr str) null "Network bridge to connect to.";
macVLAN = mkOpt' (nullOr str) null "Network interface to make MACVLAN interface from.";
}; };
}; };
}; };
@@ -115,26 +117,19 @@ in
assertion = config.systemd.network.enable; assertion = config.systemd.network.enable;
message = "Containers currently require systemd-networkd!"; message = "Containers currently require systemd-networkd!";
} }
{
assertion = all (i: i.networking.bridge == null || i.networking.macVLAN == null) (attrValues cfg.instances);
message = "Only bridge OR MACVLAN can be set";
}
]; ];
# TODO: Better security # TODO: Better security
my.firewall.trustedInterfaces = my.firewall.trustedInterfaces =
mapAttrsToList mapAttrsToList
(n: _: "ve-${n}") (n: _: "ve-${n}")
(filterAttrs (_: c: c.networking.bridge == null) cfg.instances); (filterAttrs (_: c: c.networking.bridge == null && c.networking.macVLAN == null) cfg.instances);
systemd = mkMerge ([ systemd = mkMerge (mapAttrsToList (n: c: {
{
# By symlinking to the original systemd-nspawn@.service for every instance we force the unit generator to
# create overrides instead of replacing the unit entirely
packages = [
(pkgs.linkFarm "systemd-nspawn-containers" (map (n: {
name = "etc/systemd/system/systemd-nspawn@${n}.service";
path = "${pkgs.systemd}/example/systemd/system/systemd-nspawn@.service";
}) (attrNames cfg.instances)))
];
}
] ++ (mapAttrsToList (n: c: {
nspawn."${n}" = { nspawn."${n}" = {
execConfig = { execConfig = {
Boot = true; Boot = true;
@@ -165,6 +160,8 @@ in
}; };
networkConfig = if (c.networking.bridge != null) then { networkConfig = if (c.networking.bridge != null) then {
Bridge = c.networking.bridge; Bridge = c.networking.bridge;
} else if (c.networking.macVLAN != null) then {
MACVLAN = "${c.networking.macVLAN}:host0";
} else { } else {
VirtualEthernet = true; VirtualEthernet = true;
}; };
@@ -182,6 +179,9 @@ in
c.containerSystem; c.containerSystem;
in in
{ {
# To prevent creating a whole new unit file
overrideStrategy = "asDropin";
environment = { environment = {
# systemd.nspawn units can't set the root directory directly, but /run/machines/${n} is one of the search paths # systemd.nspawn units can't set the root directory directly, but /run/machines/${n} is one of the search paths
root = "/run/machines/${n}"; root = "/run/machines/${n}";
@@ -247,7 +247,7 @@ in
Bridge = c.networking.bridge; Bridge = c.networking.bridge;
}; };
}; };
}) cfg.instances)); }) cfg.instances);
}) })
# Inside container # Inside container
+2 -1
View File
@@ -45,8 +45,9 @@ let
journalctl -o cat --no-pager -n 0 -f -u "$unit" & journalctl -o cat --no-pager -n 0 -f -u "$unit" &
jPid=$! jPid=$!
# shellcheck disable=SC2329
cleanup() { cleanup() {
# shellcheck disable=SC2317
kill "$jPid" kill "$jPid"
} }
trap cleanup EXIT trap cleanup EXIT
File diff suppressed because it is too large Load Diff
@@ -1,9 +1,15 @@
{ lib, pkgs, config, ... }: { lib, pkgs, config, ... }:
let let
inherit (lib) optional mkIf mkDefault mkMerge; inherit (lib) optional mkIf mkDefault mkMerge mkOverride;
inherit (lib.my) mkBoolOpt'; inherit (lib.my) mkBoolOpt';
cfg = config.my.gui; cfg = config.my.gui;
androidUdevRules = pkgs.runCommand "udev-rules-android" {
rulesFile = ./android-udev.rules;
} ''
install -D "$rulesFile" "$out"/lib/udev/rules.d/51-android.rules
'';
in in
{ {
options.my.gui = with lib.types; { options.my.gui = with lib.types; {
@@ -12,7 +18,7 @@ in
config = mkIf cfg.enable { config = mkIf cfg.enable {
hardware = { hardware = {
opengl.enable = mkDefault true; graphics.enable = mkDefault true;
}; };
systemd = { systemd = {
@@ -23,15 +29,32 @@ in
security = { security = {
polkit.enable = true; polkit.enable = true;
pam.services.swaylock = {}; pam.services.swaylock-plugin = {};
};
users = {
groups = {
adbusers.gid = lib.my.c.ids.gids.adbusers;
};
}; };
environment.systemPackages = with pkgs; [ environment.systemPackages = with pkgs; [
# for pw-jack # for pw-jack
pipewire.jack pipewire.jack
swaylock swaylock-plugin
]; ];
services = { services = {
resolved = {
settings.Resolve = {
FallbackDNS = mkOverride 99 (
"1.1.1.1#cloudflare-dns.com 8.8.8.8#dns.google " +
"1.0.0.1#cloudflare-dns.com 8.8.4.4#dns.google " +
"2606:4700:4700::1111#cloudflare-dns.com 2001:4860:4860::8888#dns.google " +
"2606:4700:4700::1001#cloudflare-dns.com 2001:4860:4860::8844#dns.google" );
LLMNR = "resolve";
};
};
pipewire = { pipewire = {
enable = true; enable = true;
alsa.enable = true; alsa.enable = true;
@@ -44,13 +67,21 @@ in
gnome = { gnome = {
gnome-keyring.enable = true; gnome-keyring.enable = true;
}; };
udisks2.enable = true;
udev = { udev = {
packages = [
androidUdevRules
];
extraRules = '' extraRules = ''
# Nvidia # Nvidia
SUBSYSTEM=="usb", ATTR{idVendor}=="0955", MODE="0664", GROUP="wheel" SUBSYSTEM=="usb", ATTR{idVendor}=="0955", MODE="0664", GROUP="wheel"
# Nintendo # Nintendo
SUBSYSTEM=="usb", ATTR{idVendor}=="057e", MODE="0664", GROUP="wheel" SUBSYSTEM=="usb", ATTR{idVendor}=="057e", MODE="0664", GROUP="wheel"
# FT
SUBSYSTEM=="usb", ATTR{idVendor}=="0403", MODE="0664", GROUP="wheel"
# /dev/player0
SUBSYSTEM=="usb", ATTR{idVendor}=="6969", MODE="0664", GROUP="wheel"
''; '';
}; };
}; };
@@ -63,7 +94,7 @@ in
gyre-fonts # TrueType substitutes for standard PostScript fonts gyre-fonts # TrueType substitutes for standard PostScript fonts
liberation_ttf liberation_ttf
unifont unifont
noto-fonts-emoji noto-fonts-color-emoji
]; ];
xdg = { xdg = {
@@ -84,5 +115,13 @@ in
]; ];
}; };
}; };
my = {
user = {
config = {
extraGroups = [ "adbusers" ];
};
};
};
}; };
} }
+7 -4
View File
@@ -36,18 +36,16 @@ let
espOverhead = espOverhead =
if (!mesh.security.enable) then 0 if (!mesh.security.enable) then 0
else else
# SPI + seq + IV + pad / header + ICV # UDP encap + SPI + seq + IV + pad / header + ICV
4 + 4 + (if mesh.security.encrypt then 8 else 0) + 2 + 16; (if mesh.udpEncapsulation then 8 else 0) + 4 + 4 + (if mesh.security.encrypt then 8 else 0) + 2 + 16;
# UDP + VXLAN + Ethernet + L3 (IPv4/IPv6) # UDP + VXLAN + Ethernet + L3 (IPv4/IPv6)
overhead = espOverhead + 8 + 8 + 14 + mesh.l3Overhead; overhead = espOverhead + 8 + 8 + 14 + mesh.l3Overhead;
in in
toString (mesh.baseMTU - overhead); toString (mesh.baseMTU - overhead);
bridgeFDBs = mapAttrsToList (n: peer: { bridgeFDBs = mapAttrsToList (n: peer: {
bridgeFDBConfig = {
MACAddress = "00:00:00:00:00:00"; MACAddress = "00:00:00:00:00:00";
Destination = peer.addr; Destination = peer.addr;
};
}) otherPeers; }) otherPeers;
}; };
}; };
@@ -62,8 +60,12 @@ let
chain l2mesh-${name} { chain l2mesh-${name} {
${optionalString mesh.security.enable '' ${optionalString mesh.security.enable ''
udp dport isakmp accept udp dport isakmp accept
${if mesh.udpEncapsulation then ''
udp dport ipsec-nat-t accept
'' else ''
meta l4proto esp accept meta l4proto esp accept
''} ''}
''}
${optionalString (!mesh.security.enable) (vxlanAllow mesh.vni)} ${optionalString (!mesh.security.enable) (vxlanAllow mesh.vni)}
return return
} }
@@ -94,6 +96,7 @@ let
esp=${if mesh.security.encrypt then "aes_gcm256" else "null-sha256"} esp=${if mesh.security.encrypt then "aes_gcm256" else "null-sha256"}
ikev2=yes ikev2=yes
modecfgpull=no modecfgpull=no
encapsulation=${if mesh.udpEncapsulation then "yes" else "no"}
''; '';
}) })
otherPeers); otherPeers);
+225
View File
@@ -0,0 +1,225 @@
{ lib, pkgs, config, ... }:
let
inherit (lib) mkMerge mkIf mkForce genAttrs concatMapStringsSep;
inherit (lib.my) mkOpt' mkBoolOpt';
cfg = config.my.netboot;
tftpRoot = pkgs.linkFarm "tftp-root" [
{
name = "ipxe-x86_64.efi";
path = "${pkgs.ipxe}/ipxe.efi";
}
];
menuFile = pkgs.runCommand "menu.ipxe" {
bootHost = cfg.server.host;
} ''
substituteAll ${./menu.ipxe} "$out"
'';
bootBuilder = pkgs.replaceVarsWith {
src = ./netboot-loader-builder.py;
isExecutable = true;
replacements = {
inherit (pkgs) python3;
bootspecTools = pkgs.bootspec;
nix = config.nix.package.out;
inherit (config.system.nixos) distroName;
systemName = config.system.name;
inherit (cfg.client) configurationLimit;
checkMountpoints = pkgs.writeShellScript "check-mountpoints" ''
if ! ${pkgs.util-linuxMinimal}/bin/findmnt /boot > /dev/null; then
echo "/boot is not a mounted partition. Is the path configured correctly?" >&2
exit 1
fi
'';
};
};
in
{
options.my.netboot = with lib.types; {
client = {
enable = mkBoolOpt' false "Whether network booting should be enabled.";
configurationLimit = mkOpt' ints.unsigned 10 "Max generations to show in boot menu.";
};
server = {
enable = mkBoolOpt' false "Whether a netboot server should be enabled.";
ip = mkOpt' str null "IP clients should connect to via TFTP.";
host = mkOpt' str config.networking.fqdn "Hostname clients should connect to over HTTP / NFS.";
allowedPrefixes = mkOpt' (listOf str) null "Prefixes clients should be allowed to connect from (NFS).";
installer = {
storeSize = mkOpt' str "16GiB" "Total allowed writable size of store.";
};
instances = mkOpt' (listOf str) [ ] "Systems to hold boot files for.";
};
};
config = mkMerge [
(mkIf cfg.client.enable {
systemd = {
services = {
mount-boot = {
description = "Mount /boot";
after = [ "systemd-networkd-wait-online.service" ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
};
path = with pkgs; [ gnused ldns nfs-utils ];
script = ''
get_cmdline() {
sed -rn "s/^.*$1=(\\S+).*\$/\\1/p" < /proc/cmdline
}
host="$(get_cmdline boothost)"
if [ -z "$host" ]; then
echo "boothost kernel parameter not found!" >&2
exit 1
fi
until [ -n "$(drill -Q $host)" ]; do
sleep 0.1
done
mkdir -p /boot
mount.nfs $host:/srv/netboot/systems/${config.system.name} /boot
'';
wantedBy = [ "remote-fs.target" ];
};
};
};
boot.supportedFilesystems.nfs = true;
boot.loader = {
grub.enable = false;
systemd-boot.enable = false;
};
system = {
build.installBootLoader = bootBuilder;
boot.loader.id = "ipxe-netboot";
};
})
(mkIf cfg.server.enable {
environment = {
etc = {
"netboot/menu.ipxe".source = menuFile;
"netboot/shell.efi".source = "${pkgs.edk2-uefi-shell}/shell.efi";
};
};
systemd = {
tmpfiles.settings."10-netboot" = genAttrs
(map (i: "/srv/netboot/systems/${i}") cfg.server.instances)
(p: {
d = {
user = "root";
group = "root";
mode = "0777";
};
});
services = {
netboot-update = {
description = "Update netboot images";
after = [ "systemd-networkd-wait-online.service" ];
serviceConfig.Type = "oneshot";
path = with pkgs; [
coreutils curl jq zstd gnutar
];
script = ''
update_nixos() {
latestShort="$(curl -s https://git.nul.ie/api/v1/repos/dev/nixfiles/tags/installer \
| jq -r .commit.sha | cut -c -7)"
if [ -f nixos-installer/tag.txt ] && [ "$(< nixos-installer/tag.txt)" = "$latestShort" ]; then
echo "NixOS installer is up to date"
return
fi
echo "Updating NixOS installer to $latestShort"
mkdir -p nixos-installer
fname="jackos-installer-netboot-$latestShort.tar.zst"
downloadUrl="$(curl -s https://git.nul.ie/api/v1/repos/dev/nixfiles/releases/tags/installer | \
jq -r ".assets[] | select(.name == \"$fname\").browser_download_url")"
curl -Lo /tmp/nixos-installer-netboot.tar.zst "$downloadUrl"
tar -C nixos-installer --zstd -xf /tmp/nixos-installer-netboot.tar.zst
truncate -s "${cfg.server.installer.storeSize}" nixos-installer/rootfs.ext4
rm /tmp/nixos-installer-netboot.tar.zst
echo "$latestShort" > nixos-installer/tag.txt
}
mkdir -p /srv/netboot
cd /srv/netboot
ln -sf ${menuFile} boot.ipxe
ln -sf "${pkgs.edk2-uefi-shell}/shell.efi" "efi-shell-${config.nixpkgs.localSystem.linuxArch}.efi"
update_nixos
'';
startAt = "06:00";
wantedBy = [ "network-online.target" ];
};
nbd-server = {
serviceConfig = {
PrivateUsers = mkForce false;
CacheDirectory = "netboot";
};
};
};
};
services = {
atftpd = {
enable = true;
root = tftpRoot;
};
nginx = {
virtualHosts."${cfg.server.host}" = {
locations."/" = {
root = "/srv/netboot";
extraConfig = ''
autoindex on;
'';
};
};
};
nbd.server = {
enable = true;
extraOptions = {
allowlist = true;
};
exports = {
nixos-installer = {
path = "/srv/netboot/nixos-installer/rootfs.ext4";
extraOptions = {
copyonwrite = true;
cowdir = "/var/cache/netboot";
sparse_cow = true;
};
};
};
};
nfs = {
server = {
enable = true;
exports = ''
/srv/netboot/systems ${concatMapStringsSep " " (p: "${p}(rw,all_squash)") cfg.server.allowedPrefixes}
'';
};
};
};
my = {
tmproot.persistence.config.directories = [
"/srv/netboot"
{ directory = "/var/cache/netboot"; mode = "0700"; }
];
};
})
];
}
@@ -0,0 +1,48 @@
From 7f75d320f6d8ac7ec5185b2145da87f698aec273 Mon Sep 17 00:00:00 2001
From: Michael Brown <mcb30@ipxe.org>
Date: Mon, 2 Sep 2024 12:24:57 +0100
Subject: [PATCH] [etherfabric] Fix use of uninitialised variable in
falcon_xaui_link_ok()
The link status check in falcon_xaui_link_ok() reads from the
FCN_XX_CORE_STAT_REG_MAC register only on production hardware (where
the FPGA version reads as zero), but modifies the value and writes
back to this register unconditionally. This triggers an uninitialised
variable warning on newer versions of gcc.
Fix by assuming that the register exists only on production hardware,
and so moving the "modify-write" portion of the "read-modify-write"
operation to also be covered by the same conditional check.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
---
src/drivers/net/etherfabric.c | 15 +++++++++------
1 file changed, 9 insertions(+), 6 deletions(-)
diff --git a/src/drivers/net/etherfabric.c b/src/drivers/net/etherfabric.c
index b40596beae7..be30b71f79f 100644
--- a/src/drivers/net/etherfabric.c
+++ b/src/drivers/net/etherfabric.c
@@ -2225,13 +2225,16 @@ falcon_xaui_link_ok ( struct efab_nic *efab )
sync = ( sync == FCN_XX_SYNC_STAT_DECODE_SYNCED );
link_ok = align_done && sync;
- }
- /* Clear link status ready for next read */
- EFAB_SET_DWORD_FIELD ( reg, FCN_XX_COMMA_DET, FCN_XX_COMMA_DET_RESET );
- EFAB_SET_DWORD_FIELD ( reg, FCN_XX_CHARERR, FCN_XX_CHARERR_RESET);
- EFAB_SET_DWORD_FIELD ( reg, FCN_XX_DISPERR, FCN_XX_DISPERR_RESET);
- falcon_xmac_writel ( efab, &reg, FCN_XX_CORE_STAT_REG_MAC );
+ /* Clear link status ready for next read */
+ EFAB_SET_DWORD_FIELD ( reg, FCN_XX_COMMA_DET,
+ FCN_XX_COMMA_DET_RESET );
+ EFAB_SET_DWORD_FIELD ( reg, FCN_XX_CHARERR,
+ FCN_XX_CHARERR_RESET );
+ EFAB_SET_DWORD_FIELD ( reg, FCN_XX_DISPERR,
+ FCN_XX_DISPERR_RESET );
+ falcon_xmac_writel ( efab, &reg, FCN_XX_CORE_STAT_REG_MAC );
+ }
has_phyxs = ( efab->phy_op->mmds & ( 1 << MDIO_MMD_PHYXS ) );
if ( link_ok && has_phyxs ) {
+68
View File
@@ -0,0 +1,68 @@
#!ipxe
set server http://@bootHost@
# Figure out if client is 64-bit capable
cpuid --ext 29 && set arch x86_64 || set arch i386
isset ${menu-default} || set menu-default exit
:start
menu Welcome to /dev/player0's humble iPXE boot menu
item --gap -- Operating Systems
iseq ${arch} x86_64 &&
item --key n nixos NixOS installer
# iseq ${arch} x86_64 &&
# item --key a archlinux Arch Linux (archiso x86_64)
# iseq ${arch} x86_64 &&
# item --key p alpine Alpine Linux
item --gap -- Other Options
item --key e efi_shell UEFI Shell
item --key x xyz netboot.xyz
item --key c config iPXE settings
item --key s shell Drop to iPXE shell
item --key r reboot Reboot
item --key q exit Exit (and continue to next boot device)
choose --timeout 0 --default ${menu-default} selected || goto cancel
goto ${selected}
:cancel
echo You cancelled the menu, dropping you to an iPXE shell
:shell
echo Type 'exit' to go back to the menu
shell
set menu-default nixos
goto start
:failed
echo Booting failed, dropping to shell
goto shell
:reboot
reboot
:exit
exit
:config
config
set menu-default config
goto start
:efi_shell
chain ${server}/efi-shell-${arch}.efi || goto failed
:xyz
chain --autofree https://boot.netboot.xyz || goto failed
:nixos
set cmdline nbd_export=nixos-installer
chain ${server}/nixos-installer/boot.ipxe || goto failed
:archlinux
# set mirrorurl https://arch.nul.ie/
chain ${server}/arch.ipxe || goto failed
:alpine
chain ${server}/alpine.ipxe || goto failed
+280
View File
@@ -0,0 +1,280 @@
#! @python3@/bin/python3 -B
# Based on `nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py`
import argparse
import datetime
import glob
import os
import os.path
import shutil
import subprocess
import sys
import json
from typing import NamedTuple, Dict, List
from dataclasses import dataclass
BOOT_MOUNT_POINT = '/boot'
STORE_DIR = 'nix'
# These values will be replaced with actual values during the package build
BOOTSPEC_TOOLS = '@bootspecTools@'
NIX = '@nix@'
DISTRO_NAME = '@distroName@'
SYSTEM_NAME = '@systemName@'
CONFIGURATION_LIMIT = int('@configurationLimit@')
CHECK_MOUNTPOINTS = "@checkMountpoints@"
@dataclass
class BootSpec:
init: str
initrd: str
kernel: str
kernelParams: List[str]
label: str
system: str
toplevel: str
specialisations: Dict[str, 'BootSpec']
sortKey: str
initrdSecrets: str | None = None
class SystemIdentifier(NamedTuple):
profile: str | None
generation: int
specialisation: str | None
def copy_if_not_exists(source: str, dest: str) -> None:
if not os.path.exists(dest):
shutil.copyfile(source, dest)
def generation_dir(profile: str | None, generation: int) -> str:
if profile:
return f'/nix/var/nix/profiles/system-profiles/{profile}-{generation}-link'
else:
return f'/nix/var/nix/profiles/system-{generation}-link'
def system_dir(i: SystemIdentifier) -> str:
d = generation_dir(i.profile, i.generation)
if i.specialisation:
return os.path.join(d, 'specialisation', i.specialisation)
else:
return d
def entry_key(i: SystemIdentifier) -> str:
pieces = [
'nixos',
i.profile or None,
'generation',
str(i.generation),
f'specialisation-{i.specialisation}' if i.specialisation else None,
]
return '-'.join(p for p in pieces if p)
def bootspec_from_json(bootspec_json: Dict) -> BootSpec:
specialisations = bootspec_json['org.nixos.specialisation.v1']
specialisations = {k: bootspec_from_json(v) for k, v in specialisations.items()}
systemdBootExtension = bootspec_json.get('org.nixos.systemd-boot', {})
sortKey = systemdBootExtension.get('sortKey', 'nixos')
return BootSpec(
**bootspec_json['org.nixos.bootspec.v1'],
specialisations=specialisations,
sortKey=sortKey
)
bootspecs = {}
def get_bootspec(profile: str | None, generation: int) -> BootSpec:
k = (profile, generation)
if k in bootspecs:
return bootspecs[k]
system_directory = system_dir(SystemIdentifier(profile, generation, None))
boot_json_path = os.path.realpath(f'{system_directory}/boot.json')
if os.path.isfile(boot_json_path):
boot_json_f = open(boot_json_path, 'r')
bootspec_json = json.load(boot_json_f)
else:
boot_json_str = subprocess.check_output([
f'{BOOTSPEC_TOOLS}/bin/synthesize',
'--version',
'1',
system_directory,
'/dev/stdout',
],
universal_newlines=True)
bootspec_json = json.loads(boot_json_str)
bs = bootspec_from_json(bootspec_json)
bootspecs[k] = bs
return bs
def copy_from_file(file: str, dry_run: bool = False) -> str:
store_file_path = os.path.realpath(file)
suffix = os.path.basename(store_file_path)
store_dir = os.path.basename(os.path.dirname(store_file_path))
dst_path = f'/{STORE_DIR}/{store_dir}-{suffix}'
if not dry_run:
copy_if_not_exists(store_file_path, f'{BOOT_MOUNT_POINT}{dst_path}')
return dst_path
MENU_ITEM = 'item {gen_key} {title} Generation {generation} {description}'
BOOT_ENTRY = ''':{gen_key}
kernel ${{server}}/systems/{system_name}{kernel} {kernel_params} boothost=${{boothost}}
initrd ${{server}}/systems/{system_name}{initrd}
boot
'''
def gen_entry(i: SystemIdentifier) -> (str, str):
bootspec = get_bootspec(i.profile, i.generation)
if i.specialisation:
bootspec = bootspec.specialisations[i.specialisation]
kernel = copy_from_file(bootspec.kernel)
initrd = copy_from_file(bootspec.initrd)
gen_key = entry_key(i)
title = '{name}{profile}{specialisation}'.format(
name=DISTRO_NAME,
profile=' [' + i.profile + ']' if i.profile else '',
specialisation=f' ({i.specialisation})' if i.specialisation else '')
kernel_params = f'init={bootspec.init} '
kernel_params = kernel_params + ' '.join(bootspec.kernelParams)
build_time = int(os.path.getctime(system_dir(i)))
build_date = datetime.datetime.fromtimestamp(build_time).strftime('%F')
return MENU_ITEM.format(
gen_key=gen_key,
title=title,
description=f'{bootspec.label}, built on {build_date}',
generation=i.generation,
), BOOT_ENTRY.format(
gen_key=gen_key,
generation=i.generation,
system_name=SYSTEM_NAME,
kernel=kernel,
kernel_params=kernel_params,
initrd=initrd,
)
def get_generations(profile: str | None = None) -> list[SystemIdentifier]:
gen_list = subprocess.check_output([
f'{NIX}/bin/nix-env',
'--list-generations',
'-p',
'/nix/var/nix/profiles/' + ('system-profiles/' + profile if profile else 'system')],
universal_newlines=True)
gen_lines = gen_list.split('\n')
gen_lines.pop()
configurationLimit = CONFIGURATION_LIMIT
configurations = [
SystemIdentifier(
profile=profile,
generation=int(line.split()[0]),
specialisation=None
)
for line in gen_lines
]
return configurations[-configurationLimit:]
def remove_old_files(gens: list[SystemIdentifier]) -> None:
known_paths = []
for gen in gens:
bootspec = get_bootspec(gen.profile, gen.generation)
known_paths.append(copy_from_file(bootspec.kernel, True))
known_paths.append(copy_from_file(bootspec.initrd, True))
for path in glob.iglob(f'{BOOT_MOUNT_POINT}/{STORE_DIR}/*'):
if not path in known_paths and not os.path.isdir(path):
os.unlink(path)
def get_profiles() -> list[str]:
if os.path.isdir('/nix/var/nix/profiles/system-profiles/'):
return [x
for x in os.listdir('/nix/var/nix/profiles/system-profiles/')
if not x.endswith('-link')]
else:
return []
MENU = '''#!ipxe
# Server hostname option
set boothost ${{66:string}}
set server http://${{boothost}}
:start
menu {distro} boot menu
item --gap -- Generations
{generation_items}
item --gap -- Other
item --key m main Main netboot menu
choose --timeout 5000 --default {menu_default} selected || goto cancel
goto ${{selected}}
:cancel
shell
goto start
:error
echo Booting failed, dropping to shell
shell
goto start
:main
chain ${{server}}/boot.ipxe || goto error
'''
def write_menu(gens: list[SystemIdentifier], default: SystemIdentifier) -> None:
gen_menu_items = []
gen_cmds = []
for g in gens:
bootspec = get_bootspec(g.profile, g.generation)
specialisations = [
SystemIdentifier(profile=g.profile, generation=g.generation, specialisation=s) for s in bootspec.specialisations]
for i in [g] + specialisations:
mi, cmds = gen_entry(i)
gen_menu_items.append(mi)
gen_cmds.append(cmds)
menu_file = f'{BOOT_MOUNT_POINT}/menu.ipxe'
with open(f'{menu_file}.tmp', 'w') as f:
f.write(MENU.format(
distro=DISTRO_NAME,
generation_items='\n'.join(gen_menu_items),
menu_default=entry_key(default),
))
print(file=f)
print('\n\n'.join(gen_cmds), file=f)
os.rename(f'{menu_file}.tmp', menu_file)
def install_bootloader(args: argparse.Namespace) -> None:
os.makedirs(f'{BOOT_MOUNT_POINT}/{STORE_DIR}', exist_ok=True)
gens = get_generations()
for profile in get_profiles():
gens += get_generations(profile)
gens = sorted(gens, key=lambda g: entry_key(g), reverse=True)
remove_old_files(gens)
for g in gens:
if os.path.dirname(get_bootspec(g.profile, g.generation).init) == os.path.realpath(args.default_config):
default = g
break
else:
assert False, 'No default generation found'
write_menu(gens, default)
def main() -> None:
parser = argparse.ArgumentParser(description=f'Update {DISTRO_NAME}-related netboot files')
parser.add_argument('default_config', metavar='DEFAULT-CONFIG', help=f'The default {DISTRO_NAME} config to boot')
args = parser.parse_args()
subprocess.check_call(CHECK_MOUNTPOINTS)
install_bootloader(args)
if __name__ == '__main__':
main()
+6 -14
View File
@@ -1,6 +1,6 @@
{ lib, pkgs, config, ... }: { lib, pkgs, config, ... }:
let let
inherit (lib) flatten optional mkIf mkDefault mkMerge; inherit (lib) flatten optional mkIf mkDefault mkMerge versionAtLeast;
in in
{ {
config = mkMerge [ config = mkMerge [
@@ -12,21 +12,13 @@ in
useNetworkd = mkDefault true; useNetworkd = mkDefault true;
}; };
systemd = {
additionalUpstreamSystemUnits = [
# TODO: NixOS has its own version of this, but with `network` instead of `networkd`. Is this just a typo? It
# hasn't been updated in 2 years...
"systemd-networkd-wait-online@.service"
];
};
services.resolved = { services.resolved = {
domains = [ config.networking.domain ]; settings.Resolve = {
Domains = [ config.networking.domain ];
# Explicitly unset fallback DNS (Nix module will not allow for a blank config) # Explicitly unset fallback DNS (Nix module will not allow for a blank config)
extraConfig = '' FallbackDNS = "";
FallbackDNS= Cache = "no-negative";
Cache=no-negative };
'';
}; };
} }
+24 -9
View File
@@ -4,11 +4,6 @@ let
inherit (lib.my) mkOpt'; inherit (lib.my) mkOpt';
cfg = config.my.nvme; cfg = config.my.nvme;
nvme-cli = pkgs.nvme-cli.override {
libnvme = pkgs.libnvme.overrideAttrs (o: {
patches = o.patches ++ [ ./libnvme-hostconf.patch ];
});
};
hostNQN = "nqn.2014-08.org.nvmexpress:uuid:${cfg.uuid}"; hostNQN = "nqn.2014-08.org.nvmexpress:uuid:${cfg.uuid}";
etc = prefix: { etc = prefix: {
@@ -28,14 +23,32 @@ in
config = mkIf (cfg.uuid != null) { config = mkIf (cfg.uuid != null) {
environment = { environment = {
systemPackages = [ systemPackages = [
nvme-cli pkgs.nvme-cli
]; ];
etc = etc ""; etc = etc "";
}; };
boot.initrd.systemd = mkIf (cfg.boot.nqn != null) { boot = mkIf (cfg.boot.nqn != null) {
initrd = {
availableKernelModules = [ "rdma_cm" "iw_cm" "ib_cm" "nvme_core" "nvme_rdma" ];
kernelModules = [ "nvme-fabrics" ];
systemd = {
contents = etc "/etc/"; contents = etc "/etc/";
extraBin.nvme = "${nvme-cli}/bin/nvme"; extraBin = with pkgs; {
dmesg = "${util-linux}/bin/dmesg";
ip = "${iproute2}/bin/ip";
nvme = "${nvme-cli}/bin/nvme";
};
network = {
enable = true;
wait-online.enable = true;
};
settings.Manager = {
DefaultTimeoutStartSec = 20;
DefaultDeviceTimeoutSec = 20;
};
services.connect-nvme = { services.connect-nvme = {
description = "Connect NVMe-oF"; description = "Connect NVMe-oF";
@@ -45,7 +58,7 @@ in
serviceConfig = { serviceConfig = {
Type = "oneshot"; Type = "oneshot";
ExecStart = "${nvme-cli}/bin/nvme connect -t rdma -a ${cfg.boot.address} -n ${cfg.boot.nqn}"; ExecStart = "${pkgs.nvme-cli}/bin/nvme connect -t rdma -a ${cfg.boot.address} -n ${cfg.boot.nqn} -q ${hostNQN}";
Restart = "on-failure"; Restart = "on-failure";
RestartSec = 10; RestartSec = 10;
}; };
@@ -54,5 +67,7 @@ in
}; };
}; };
}; };
};
};
} }
+12 -4
View File
@@ -1,7 +1,7 @@
{ lib, pkgs, config, ... }: { lib, pkgs, config, ... }:
let let
inherit (builtins) isList; inherit (builtins) isList;
inherit (lib) mkMerge mkIf mkDefault mapAttrsToList concatMapStringsSep concatStringsSep; inherit (lib) mkMerge mkIf mkDefault mapAttrsToList concatMapStringsSep concatStringsSep getExe;
inherit (lib.my) mkBoolOpt' mkOpt'; inherit (lib.my) mkBoolOpt' mkOpt';
# Yoinked from nixos/modules/services/networking/pdns-recursor.nix # Yoinked from nixos/modules/services/networking/pdns-recursor.nix
@@ -165,7 +165,7 @@ let
extraSettingsOpt = with lib.types; mkOpt' (nullOr str) null "Path to extra settings (e.g. for secrets)."; extraSettingsOpt = with lib.types; mkOpt' (nullOr str) null "Path to extra settings (e.g. for secrets).";
baseAuthSettings = pkgs.writeText "pdns.conf" (settingsToLines cfg.auth.settings); baseAuthSettings = pkgs.writeText "pdns.conf" (settingsToLines cfg.auth.settings);
baseRecursorSettings = pkgs.writeText "pdns-recursor.conf" (settingsToLines config.services.pdns-recursor.settings); baseRecursorSettings = (pkgs.formats.yaml { }).generate "pdns-recursor.yaml" config.services.pdns-recursor.settings;
generateSettings = type: base: dst: if (cfg."${type}".extraSettingsFile != null) then '' generateSettings = type: base: dst: if (cfg."${type}".extraSettingsFile != null) then ''
oldUmask="$(umask)" oldUmask="$(umask)"
umask 006 umask 006
@@ -174,6 +174,14 @@ let
'' else '' '' else ''
cp "${base}" "${dst}" cp "${base}" "${dst}"
''; '';
generateYamlSettings = type: base: dst: if (cfg."${type}".extraSettingsFile != null) then ''
oldUmask="$(umask)"
umask 006
${getExe pkgs.yaml-merge} "${base}" "${cfg."${type}".extraSettingsFile}" > "${dst}"
umask "$oldUmask"
'' else ''
cp "${base}" "${dst}"
'';
namedConf = pkgs.writeText "pdns-named.conf" '' namedConf = pkgs.writeText "pdns-named.conf" ''
options { options {
@@ -315,9 +323,9 @@ in
(mkIf cfg.recursor.enable { (mkIf cfg.recursor.enable {
systemd.services.pdns-recursor = { systemd.services.pdns-recursor = {
preStart = '' preStart = ''
${generateSettings "recursor" baseRecursorSettings "/run/pdns-recursor/recursor.conf"} ${generateYamlSettings "recursor" baseRecursorSettings "/run/pdns-recursor/recursor.yml"}
''; '';
serviceConfig.ExecStart = [ "" "${pkgs.pdns-recursor}/bin/pdns_recursor --config-dir=/run/pdns-recursor" ]; serviceConfig.ExecStart = [ "" "${pkgs.pdns-recursor}/bin/pdns_recursor --config-dir=/run/pdns-recursor --daemon=no --write-pid=no --disable-syslog --log-timestamp=no" ];
}; };
services.pdns-recursor = { services.pdns-recursor = {
+1 -6
View File
@@ -11,8 +11,7 @@ in
config = mkIf cfg.enable { config = mkIf cfg.enable {
services = { services = {
getty.autologinUser = mkDefault uname; getty.autologinUser = mkDefault uname;
kmscon.autologinUser = mkDefault uname; resolved.settings.Resolve.LLMNR = mkDefault "false";
resolved.llmnr = mkDefault "false";
}; };
systemd = { systemd = {
timers = { timers = {
@@ -35,10 +34,6 @@ in
}; };
documentation.nixos.enable = mkDefault' false; documentation.nixos.enable = mkDefault' false;
environment.systemPackages = with pkgs; [
tcpdump
];
}; };
meta.buildDocsInSandbox = false; meta.buildDocsInSandbox = false;
+139 -17
View File
@@ -2,7 +2,7 @@
let let
inherit (lib) inherit (lib)
optionalString concatStringsSep concatMap concatMapStringsSep mkIf mkDefault mkMerge mkForce mkVMOverride optionalString concatStringsSep concatMap concatMapStringsSep mkIf mkDefault mkMerge mkForce mkVMOverride
mkAliasDefinitions; mkAliasDefinitions mapAttrsToList filterAttrs;
inherit (lib.my) mkOpt' mkBoolOpt' mkVMOverride'; inherit (lib.my) mkOpt' mkBoolOpt' mkVMOverride';
cfg = config.my.tmproot; cfg = config.my.tmproot;
@@ -145,8 +145,18 @@ in
# Also the script runs before impermanence does. # Also the script runs before impermanence does.
"/var/lib/nixos" "/var/lib/nixos"
"/var/lib/systemd" "/var/lib/systemd"
"/var/cache/man"
{ directory = "/root/.cache/nix"; mode = "0700"; } { directory = "/root/.cache/nix"; mode = "0700"; }
# Including these unconditionally due to infinite recursion problems...
{
directory = "/etc/lvm/archive";
mode = "0700";
}
{
directory = "/etc/lvm/backup";
mode = "0700";
}
]; ];
files = [ files = [
"/etc/machine-id" "/etc/machine-id"
@@ -260,18 +270,6 @@ in
my.tmproot.persistence.config.files = my.tmproot.persistence.config.files =
concatMap (k: [ k.path "${k.path}.pub" ]) config.services.openssh.hostKeys; concatMap (k: [ k.path "${k.path}.pub" ]) config.services.openssh.hostKeys;
}) })
(mkIf config.services.lvm.enable {
my.tmproot.persistence.config.directories = [
{
directory = "/etc/lvm/archive";
mode = "0700";
}
{
directory = "/etc/lvm/backup";
mode = "0700";
}
];
})
(mkIf (config.security.acme.certs != { }) { (mkIf (config.security.acme.certs != { }) {
my.tmproot.persistence.config.directories = [ my.tmproot.persistence.config.directories = [
{ {
@@ -339,13 +337,13 @@ in
(persistSimpleSvc "jackett") (persistSimpleSvc "jackett")
(persistSimpleSvc "radarr") (persistSimpleSvc "radarr")
(persistSimpleSvc "sonarr") (persistSimpleSvc "sonarr")
(mkIf config.services.jellyseerr.enable { (mkIf config.services.seerr.enable {
my.tmproot.persistence.config.directories = [ my.tmproot.persistence.config.directories = [
{ {
directory = "/var/lib/jellyseerr"; directory = "/var/lib/seerr";
mode = "0750"; mode = "0750";
user = "jellyseerr"; user = "seerr";
group = "jellyseerr"; group = "seerr";
} }
]; ];
}) })
@@ -492,6 +490,130 @@ in
} }
]; ];
}) })
(mkIf config.services.wastebin.enable {
my.tmproot.persistence.config.directories = [ "/var/lib/private/wastebin" ];
})
(mkIf config.services.photoprism.enable {
my.tmproot.persistence.config.directories = [
{
directory = config.services.photoprism.storagePath;
mode = "0750";
user = "photoprism";
group = "photoprism";
}
];
})
(mkIf config.services.mautrix-whatsapp.enable {
my.tmproot.persistence.config.directories = [
{
directory = "/var/lib/mautrix-whatsapp";
mode = "0750";
user = "mautrix-whatsapp";
group = "mautrix-whatsapp";
}
];
})
{
my.tmproot.persistence.config.directories = mapAttrsToList (n: i: {
directory = "/var/lib/${i.dataDir}";
mode = "0750";
user = "mautrix-meta-${n}";
group = "mautrix-meta";
}) (filterAttrs (_: i: i.enable) config.services.mautrix-meta.instances);
}
(mkIf config.services.unifi.enable {
my.tmproot.persistence.config.directories = [
{
directory = "/var/lib/unifi";
mode = "0750";
user = "unifi";
group = "unifi";
}
{
directory = "/var/cache/unifi";
mode = "0750";
user = "unifi";
group = "unifi";
}
];
})
(persistSimpleSvc "octoprint")
(mkIf (config.services.borgbackup.jobs != { }) {
my.tmproot.persistence.config.directories = [
"/var/lib/borgbackup"
"/var/cache/borgbackup"
];
services.borgbackup.package = pkgs.borgbackup.overrideAttrs (o: {
makeWrapperArgs = o.makeWrapperArgs ++ [
"--set-default BORG_BASE_DIR /var/lib/borgbackup"
"--set-default BORG_CONFIG_DIR /var/lib/borgbackup/config"
"--set-default BORG_CACHE_DIR /var/cache/borgbackup"
];
});
})
(mkIf (config.services ? "bluesky-pds" && config.services.bluesky-pds.enable) {
my.tmproot.persistence.config.directories = [
{
directory = "/var/lib/pds";
mode = "0750";
user = "pds";
group = "pds";
}
];
})
(mkIf config.services.home-assistant.enable {
my.tmproot.persistence.config.directories = [
{
directory = config.services.home-assistant.configDir;
mode = "0750";
user = "hass";
group = "hass";
}
];
})
(mkIf config.services.frigate.enable {
my.tmproot.persistence.config.directories = [
{
directory = "/var/lib/frigate";
mode = "0755";
user = "frigate";
group = "frigate";
}
{
directory = "/var/cache/frigate";
mode = "0755";
user = "frigate";
group = "frigate";
}
];
})
(mkIf config.services.copyparty.enable {
my.tmproot.persistence.config.directories = [
{
directory = "/var/lib/copyparty";
mode = "0755";
user = "copyparty";
group = "copyparty";
}
{
directory = "/var/cache/copyparty";
mode = "0755";
user = "copyparty";
group = "copyparty";
}
];
})
(mkIf config.services.terraria.enable {
my.tmproot.persistence.config.directories = [
{
directory = config.services.terraria.dataDir;
mode = "0755";
user = "terraria";
group = "terraria";
}
];
})
])) ]))
]); ]);

Some files were not shown because too many files have changed in this diff Show More