Compare commits

...

104 Commits

Author SHA1 Message Date
383c7bb15e [Gluetun] Switches to Wireguard config 2026-02-24 16:40:02 -08:00
a4bb91e68e [Wallabag] Adds wallabag 2026-02-24 16:40:02 -08:00
13f301c4fb [Obelisk] Adds Reflex FRP binary caches to the system 2026-02-12 15:53:26 -08:00
114a1ae125 [notes] Update Syncthing config for Proxima 2026-02-06 09:47:12 -08:00
9a4ab98506 [Jellyfin] Update to 10.11.6 2026-02-03 15:42:41 -08:00
bf0d4a11d2 [nix] Fixes deprecated function usage. 2026-02-02 17:18:04 -08:00
0950758532 [3d printing] Fix FreeCAD launcher 2026-01-27 23:20:31 -08:00
31907ff47b [Haskell] Adds support to the LSP. [neovim] Merges and checks in lazyvim.json 2026-01-26 12:44:23 -08:00
a985e8a0da [Browser,Firefox,Librewolf] Switch back to Firefox as default browser for better sync and features 2026-01-22 10:02:31 -08:00
8add79d14c [flake] Update 2026-01-09 16:19:43 -08:00
93523c54f2 [altair] Cleans up hardware config 2026-01-09 16:15:41 -08:00
c07dfe4259 [neovim] Check in extras config. 2026-01-06 15:29:51 -08:00
978b7ac2b7 [flake] update to 25.11 2026-01-02 12:37:18 -08:00
d1ccaa1c57 [hyprland] Adds a keybind for saving screenshots to files 2026-01-02 12:36:06 -08:00
1c098a032b [neovim] Switch to tab-completion so that I don't get confused when switching editors 2025-12-30 11:10:30 -08:00
b951779a92 [audio] Adds script to inhibit sleep while media is playing. 2025-12-30 11:05:10 -08:00
889d0b1057 [nvim] Adds a commented section about images in markdown docs, though it won't work in Foot tty. 2025-12-20 12:17:00 -08:00
64cac2b167 [printing] Adds Brother printer 2025-12-14 17:26:25 -08:00
2f278b5ecb [nvim] Adds link to do documentation for RenderMarkdown plugin 2025-12-12 12:25:51 -08:00
a0448def04 [desktop] Adds inkscape and groups some similar apps 2025-12-08 16:15:27 -08:00
edb0f18989 [nvim] Updates a few plugins to new versions/repositories 2025-12-08 16:06:55 -08:00
cfde735570 [nvim] Removes copilot 2025-11-21 12:18:06 -08:00
9bbb4aa2dc [astronomy] Added to Altair, removed from Vega 2025-11-21 12:18:06 -08:00
9818771f7c [dm-companion] Fixes deployment script? 2025-11-21 12:09:29 -08:00
f7af96c497 [scrutiny] Switches to UUIDs, removes missing device. 2025-11-21 12:09:29 -08:00
499b0f4334 [astronomy] Adds Stellarium, KStars and Celestia to Vega 2025-10-21 11:58:35 -07:00
879ad11d96 [flake] update 2025-10-19 20:18:27 -07:00
640eaec8a1 [3d printing] Adds Orynt 2025-10-16 20:10:02 -07:00
898e1bdde0 [3d printing] Adds openscad 2025-10-15 17:42:32 -07:00
a6d4c40beb [notes, syncthing] Adds proxima to syncing. 2025-10-14 15:32:00 -07:00
a13e8cea19 [docker dev] Remove rootless mode because some apps (Bambu Studio) do not work in rootless mode 2025-10-07 16:59:44 -07:00
3b8e38e702 [immich] Adds but disabled Immich because it's crashing on start-up 2025-09-25 17:23:01 -07:00
05c001081e [Containers] Adds GPU support for docker 2025-09-22 20:17:01 -07:00
3e2e3aca21 [Flake] Update 2025-09-19 14:50:05 -07:00
1837a545a4 [3d printing] Adds freecad 2025-09-19 14:32:14 -07:00
5c7649d3df [3d printing] Adds slicers, blender 2025-09-16 20:59:34 -07:00
3bb9ebf875 [desktop] Removes QuteBrowser and adds Chromium 2025-09-16 20:59:34 -07:00
ee3b7c2c53 [wow.blazestar.net] Adds static site 2025-09-15 17:08:03 -07:00
746e31dca2 [nextcloud] Move back to a stable version after some bugs 2025-09-15 17:08:03 -07:00
9a59e60044 [nextcloud] Remove unused env vars 2025-09-15 17:08:03 -07:00
646221721e [Tandoor] Adds tandoor service 2025-09-15 17:08:03 -07:00
554b2863f3 [notes] Fixes alias. [discord] Adds skeleton for PTT hotkey passthrough 2025-09-10 12:05:13 -07:00
5829dc294e [Flake] Updates dependencies 2025-09-02 15:18:59 -07:00
0645912626 [hyprctl] Tweaks for Cyberpunk 2025-09-01 17:49:50 -07:00
430a041724 Adds Nexus-Mods app. 2025-09-01 17:48:04 -07:00
80512c29ea [notes] Adds alias for editing notes 2025-08-21 10:32:21 -07:00
392d6fe537 [discord] Attempts to fix the keybinding issue 2025-08-21 10:31:05 -07:00
6d1e715d9c [neovim] Add link-openining to Obsidian 2025-08-18 11:22:44 -07:00
94b8065dba [gaming] Update warcraft logs client 2025-08-15 22:09:02 -07:00
ea902faf43 [neovim] Cleans up some Snacks config 2025-08-03 11:10:49 -07:00
3d3fa1ed68 [eww] Remove launcher I never use. 2025-08-03 10:36:52 -07:00
cd4367e252 [gaming] Updates Raider.io launcher, still doesn't run properly. 2025-08-03 10:36:52 -07:00
cf4d54ebfd [Webkooks] Switches the NPM deploy to rsync the directory instead of link it because docker will follow the link and then not update. 2025-07-31 17:09:56 -07:00
3b46856b66 [Uptime Kuma] Adds Uptime Kuma to blazestar.net 2025-07-31 16:05:15 -07:00
1d1702bd9b [webhook] Updates the NPM install hooks to do a new directory on each deploy. Clean will come later 2025-07-31 15:34:45 -07:00
8a566715db [habits] Removes Beaver Habits because it seems underdeveloped 2025-07-25 13:43:27 -07:00
19d8c5c097 [habits] Adds beaver habits. [oidc] Sets up OIDC auth forwarding, it works, but not sure which header. 2025-07-25 12:22:33 -07:00
b3c6e951ee [mcp] Removes specific kernel packages for ZFS, preferring the default which should be compatible. 2025-07-18 18:11:05 -07:00
44ac6ce262 [shell] Adds fuser util 2025-07-18 18:10:26 -07:00
1c2cd59f1f [web-containers] Adds an explicit service in case we want to have two services. 2025-07-18 17:54:41 -07:00
ca4d4d714c Enables android dev on altair 2025-07-14 15:29:56 -07:00
8791432964 [goatcounter] Fixes instance for blazestar.net 2025-07-14 15:14:13 -07:00
638f34c2d3 [goatcounter] Adds instance for blazestar.net 2025-07-10 15:35:57 -07:00
743ce58b14 [graphics] Modularizes graphics config. Applies it to vega. 2025-07-01 16:13:08 -07:00
0a1d7a24e7 [dm-companion] Fixes deployment script to apply migrations. 2025-06-28 20:21:41 -07:00
81cb09176c [nvim] Use snacks as the primary picker, no relying on extras 2025-06-27 20:31:26 -07:00
7f04c3aa4c [nvim] Remove less from treesitter installed grammars 2025-06-27 20:01:11 -07:00
bded723261 [nix] Updates again because signal isn't building properly on the last version 2025-06-27 18:16:06 -07:00
0776b9d7e5 [eww] Use the correct network interface for Vega 2025-06-27 17:54:53 -07:00
0ac07e4256 Ensure some grammars are installed for treesitter 2025-06-26 17:46:20 -07:00
cedf51580e [dm-companion] Serves index for all unrecognized files, sets some cache directives 2025-06-26 16:32:39 -07:00
1dc7d7b355 [matrix] Got Blazestar.net working, but federation and cross-server joining isn't quite right. 2025-06-25 15:49:25 -07:00
b1510c3670 [containers] Disables some containers, adds some docker config. 2025-06-24 18:32:15 -07:00
f4dd4583db [nix] Modularized the container backend so I can easily switch it with an option 2025-06-24 17:31:30 -07:00
c74e40e69e [nix,flake] Moves some container files around. Also updates the flake lock. [synapse] Gets the federation working 2025-06-24 16:57:38 -07:00
514746686f [matrix] Moves secrets into sops 2025-06-24 14:29:08 -07:00
286701ba83 [blazestar.net] Sets up auto-deploy of an npm-based app. 2025-06-24 14:28:59 -07:00
1bfec397b5 [goatcounter] Adds back the rewrite for count.js 2025-06-22 10:37:49 -07:00
84d05be93b [goatcounter] Removes the special rewrite rules. Leaves them as comments for reference 2025-06-17 16:00:53 -07:00
03b3fe16b1 [goatcounter] Adds goatcounter.terakoda.com 2025-06-17 15:34:18 -07:00
cda32ea550 [dm-companion] Sets up auto-deploy and moves it to terakoda.com 2025-06-15 11:00:50 -07:00
32e10284d0 [desktop] Fixes XDG mime types for default browser 2025-06-11 15:16:46 -07:00
b9c439f5a9 [nvim] Some extra todo states 2025-06-11 15:12:28 -07:00
449662db8c [desktop] Adds gimp 2025-06-11 10:36:42 -07:00
6601377ece [terakoda.com] Moves www2 to the main site. 2025-06-09 16:04:11 -07:00
d432ef8014 [webhook] Switches match rule to just use an IP whitelist. 2025-06-09 16:04:11 -07:00
9acaae9277 [homemanager] Fixes env vars 2025-06-09 15:47:16 -07:00
f46c5948e5 [flake] Update 2025-06-09 10:15:09 -07:00
1d94f2574c [desktop] Set Librewolf as the default browser. 2025-06-08 20:57:13 -07:00
0a64c5cd4b [hypridle] Tweaks timings 2025-06-06 22:07:14 -07:00
2f44b67e9e [nvim] Remove augment plugin 2025-06-04 21:30:17 -07:00
f8378354fb [webhook] Adds webhook for redeploying npm projects 2025-06-04 17:35:31 -07:00
104cd2fd2e [arr] Adds bazarr. 2025-06-03 23:47:43 -07:00
075613c5a5 [ghost] Removes ghost demo. 2025-06-03 15:15:50 -07:00
dd8ec9035e [ghost] Adds ghost demo. 2025-06-03 15:15:28 -07:00
e5861f8e6b [dm-companion] Routes the API through the same host as the frontend 2025-06-03 15:15:28 -07:00
1ae798e30c [swaylock] Increase pixelation to 20 2025-05-30 16:31:16 -07:00
fec3b3a1e2 [oauth-proxy] Making some progress on this. It's not working, but it's now redirecting and getting 'invalid status code' 2025-05-30 10:25:00 -07:00
9411f87dbc [timetagger,traefik] Adds timetagger back, and attempts to put it behind an oauth proxy, but the traefik config isn't quite right. 2025-05-29 17:05:53 -07:00
07123a0fc2 [lock,idle] Fixes the swaylock script to work for multiple monitors and fixes it's invocation 2025-05-29 12:36:03 -07:00
5299c6d72c [shell] Adds mprocs 2025-05-29 10:26:58 -07:00
aa6402cf7c [idle;lock] Switches to swaylock 2025-05-28 12:23:14 -07:00
efab3866c2 [vega] Removes lock because it isn't working. [shell] Fixes aliases 2025-05-28 11:32:36 -07:00
b56d904c4e [vega] Switches back to stable. 2025-05-27 10:23:46 -07:00
81 changed files with 2333 additions and 1260 deletions

60
flake.lock generated
View File

@@ -7,68 +7,32 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1748226808, "lastModified": 1767910483,
"narHash": "sha256-GaBRgxjWO1bAQa8P2+FDxG4ANBVhjnSjBms096qQdxo=", "narHash": "sha256-MOU5YdVu4DVwuT5ztXgQpPuRRBjSjUGIdUzOQr9iQOY=",
"owner": "nix-community", "owner": "nix-community",
"repo": "home-manager", "repo": "home-manager",
"rev": "83665c39fa688bd6a1f7c43cf7997a70f6a109f9", "rev": "82fb7dedaad83e5e279127a38ef410bcfac6d77c",
"type": "github"
},
"original": {
"owner": "nix-community",
"ref": "release-25.05",
"repo": "home-manager",
"type": "github"
}
},
"home-manager-unstable": {
"inputs": {
"nixpkgs": [
"nixpkgs-unstable"
]
},
"locked": {
"lastModified": 1748227609,
"narHash": "sha256-SaSdslyo6UGDpPUlmrPA4dWOEuxCy2ihRN9K6BnqYsA=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "d23d20f55d49d8818ac1f1b2783671e8a6725022",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "nix-community", "owner": "nix-community",
"ref": "release-25.11",
"repo": "home-manager", "repo": "home-manager",
"type": "github" "type": "github"
} }
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1748162331, "lastModified": 1767799921,
"narHash": "sha256-rqc2RKYTxP3tbjA+PB3VMRQNnjesrT0pEofXQTrMsS8=", "narHash": "sha256-r4GVX+FToWVE2My8VVZH4V0pTIpnu2ZE8/Z4uxGEMBE=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "7c43f080a7f28b2774f3b3f43234ca11661bf334", "rev": "d351d0653aeb7877273920cd3e823994e7579b0b",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "nixos", "owner": "nixos",
"ref": "nixos-25.05", "ref": "nixos-25.11",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-unstable": {
"locked": {
"lastModified": 1748190013,
"narHash": "sha256-R5HJFflOfsP5FBtk+zE8FpL8uqE7n62jqOsADvVshhE=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "62b852f6c6742134ade1abdd2a21685fd617a291",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-unstable",
"repo": "nixpkgs", "repo": "nixpkgs",
"type": "github" "type": "github"
} }
@@ -76,9 +40,7 @@
"root": { "root": {
"inputs": { "inputs": {
"home-manager": "home-manager", "home-manager": "home-manager",
"home-manager-unstable": "home-manager-unstable",
"nixpkgs": "nixpkgs", "nixpkgs": "nixpkgs",
"nixpkgs-unstable": "nixpkgs-unstable",
"sops-nix": "sops-nix" "sops-nix": "sops-nix"
} }
}, },
@@ -89,11 +51,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1747603214, "lastModified": 1767826491,
"narHash": "sha256-lAblXm0VwifYCJ/ILPXJwlz0qNY07DDYdLD+9H+Wc8o=", "narHash": "sha256-WSBENPotD2MIhZwolL6GC9npqgaS5fkM7j07V2i/Ur8=",
"owner": "Mic92", "owner": "Mic92",
"repo": "sops-nix", "repo": "sops-nix",
"rev": "8d215e1c981be3aa37e47aeabd4e61bb069548fd", "rev": "ea3adcb6d2a000d9a69d0e23cad1f2cacb3a9fbe",
"type": "github" "type": "github"
}, },
"original": { "original": {

View File

@@ -2,20 +2,11 @@
description = "System Configuration"; description = "System Configuration";
inputs = { inputs = {
nixpkgs = { nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-25.11";
url = "github:nixos/nixpkgs?ref=nixos-25.05";
};
home-manager = { home-manager = {
url = "github:nix-community/home-manager?ref=release-25.05"; url = "github:nix-community/home-manager?ref=release-25.11";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
}; };
nixpkgs-unstable = {
url = "github:nixos/nixpkgs?ref=nixos-unstable";
};
home-manager-unstable = {
url = "github:nix-community/home-manager";
inputs.nixpkgs.follows = "nixpkgs-unstable";
};
sops-nix = { sops-nix = {
url = "github:Mic92/sops-nix"; url = "github:Mic92/sops-nix";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
@@ -23,7 +14,11 @@
}; };
outputs = outputs =
{ self, nixpkgs, ... }@inputs: {
self,
nixpkgs,
...
}@inputs:
let let
local = import ./lib; local = import ./lib;
mkNixosConfig = mkNixosConfig =
@@ -38,10 +33,12 @@
modules = [ modules = [
home-manager.nixosModules.home-manager home-manager.nixosModules.home-manager
{ {
home-manager.useGlobalPkgs = true; home-manager = {
home-manager.useUserPackages = true; useGlobalPkgs = true;
home-manager.extraSpecialArgs = { useUserPackages = true;
inherit inputs local; extraSpecialArgs = {
inherit inputs local;
};
}; };
} }
path path
@@ -58,15 +55,13 @@
}; };
vega = mkNixosConfig { vega = mkNixosConfig {
path = ./system/hosts/vega; path = ./system/hosts/vega;
nixpkgs = inputs.nixpkgs-unstable;
home-manager = inputs.home-manager-unstable;
}; };
mcp = mkNixosConfig { mcp = mkNixosConfig {
path = ./system/hosts/mcp; path = ./system/hosts/mcp;
}; };
}; };
features = { features = {
development = (import ./home-manager/features/development/development.nix); development = import ./home-manager/features/development/development.nix;
}; };
}; };
} }

View File

@@ -8,7 +8,11 @@
discord = { discord = {
name = "Discord"; name = "Discord";
# Custom options to reduce flickering under wayland. # Custom options to reduce flickering under wayland.
exec = "discord --enable-features=UseOzonePlatform --ozone-platform=wayland --disable-gpu"; exec = "env ELECTRON_OZONE_PLATFORM_HINT= discord --enable-features=UseOzonePlatform --ozone-platform=wayland --disable-gpu";
}; };
}; };
wayland.windowManager.hyprland.settings.bind = [
# Pass Mouse4 through to discord
# ", mouse:275, pass, class:^discord$"
];
} }

View File

@@ -0,0 +1,64 @@
{
pkgs,
...
}:
let
freecad-wrapped = pkgs.symlinkJoin {
name = "freecad-wrapped";
paths = [ pkgs.freecad ];
buildInputs = [ pkgs.makeWrapper ];
postBuild = ''
wrapProgram $out/bin/freecad \
--prefix MESA_LOADER_DRIVER_OVERRIDE : zink \
--prefix __EGL_VENDOR_LIBRARY_FILENAMES : ${pkgs.mesa}/share/glvnd/egl_vendor.d/50_mesa.json
'';
};
bambu-studio-wrapped = pkgs.symlinkJoin {
name = "bambu-studio-wrapped";
paths = [ pkgs.bambu-studio ];
buildInputs = [ pkgs.makeWrapper ];
postBuild = ''
wrapProgram $out/bin/bambu-studio \
--prefix MESA_LOADER_DRIVER_OVERRIDE : zink \
--prefix __EGL_VENDOR_LIBRARY_FILENAMES : ${pkgs.mesa}/share/glvnd/egl_vendor.d/50_mesa.json
'';
};
in
{
home.packages = with pkgs; [
bambu-studio-wrapped
LycheeSlicer
orca-slicer
blender
freecad-wrapped
openscad
];
xdg.desktopEntries.orynt3d =
let
orynt3d-appimage = pkgs.fetchurl {
name = "orynt3d-appimage";
url = "https://files.orynt3d.com/client/Orynt3D-0.15.3.AppImage";
sha256 = "0j10myj06ff4frsd4yv7z3lb3qgw3ha70hc5hdc9idbryica801y";
};
in
{
name = "Orynt3D";
exec = "env __EGL_VENDOR_LIBRARY_FILENAMES=/run/opengl-driver/share/glvnd/egl_vendor.d/10_nvidia.json ${pkgs.appimage-run}/bin/appimage-run ${orynt3d-appimage}";
terminal = false;
type = "Application";
# icon = "";
comment = "3D model viewer and organizer";
categories = [
"Science"
"Development"
];
};
# Options to get Bambu Studio to run:
# __GLX_VENDOR_LIBRARY_NAME=mesa __EGL_VENDOR_LIBRARY_FILENAMES=/nix/store/js9cfbjvlsls14nddk39fw74vyvlhz4l-mesa-25.0.7/share/glvnd/egl_vendor.d/50_mesa.json MESA_LOADER_DRIVER_OVERRIDE=zink GALLIUM_DRIVER=zink WEBKIT_DISABLE_DMABUF_RENDERER=1 bambu-studio
}

View File

@@ -0,0 +1,8 @@
{ pkgs, ... }:
{
home.packages = with pkgs; [
stellarium
kstars
celestia
];
}

View File

@@ -1,7 +1,41 @@
{ pkgs, ... }: { pkgs, ... }:
with pkgs;
let
# A script that runs as long as media is playing.
isMediaPlaying = writeShellApplication {
name = "isMediaPlaying";
runtimeInputs = [
playerctl
];
text = ''
set -e
while [ "$(playerctl status)" = "Playing" ]; do
echo -n "."
sleep 1
done
'';
};
# A script that prevents the system from going to sleep while media is playing
mediaCaffeine = writeShellApplication {
name = "media-caffeine";
runtimeInputs = [
isMediaPlaying
systemd
];
text = ''
set -e
systemd-inhibit --what=sleep --why="Media is playing" --mode=block isMediaPlaying
'';
};
in
{ {
home.packages = with pkgs; [ home.packages = with pkgs; [
pulseaudio # for pactl and other tools pulseaudio # for pactl and other tools
pavucontrol # GUI volume control with lots of options pavucontrol # GUI volume control with lots of options
mediaCaffeine
]; ];
} }

View File

@@ -0,0 +1,11 @@
{ pkgs, ... }:
{
imports = [
../apps/element.nix
../apps/discord.nix
];
home.packages = with pkgs; [
signal-desktop
];
}

View File

@@ -6,6 +6,7 @@
nixfmt-rfc-style # Formatter nixfmt-rfc-style # Formatter
nil # Language Server nil # Language Server
statix # Lints and suggestions for Nix
]; ];
home.shellAliases = { home.shellAliases = {
@@ -14,4 +15,3 @@
rebuild-boot = "sudo nixos-rebuild boot --flake ~/system-config --show-trace --print-build-logs --verbose"; rebuild-boot = "sudo nixos-rebuild boot --flake ~/system-config --show-trace --print-build-logs --verbose";
}; };
} }

View File

@@ -3,5 +3,4 @@
(include "./primary-statusbar.yuck") (include "./primary-statusbar.yuck")
(include "./secondary-statusbar.yuck") (include "./secondary-statusbar.yuck")
(include "./system-monitor.yuck") (include "./system-monitor.yuck")
(include "./launcher.yuck")
(include "./vertical-statusbar.yuck") (include "./vertical-statusbar.yuck")

View File

@@ -1,74 +0,0 @@
(defwindow launcher
:monitor '[ "<primary>", "DP-2", 0 ]'
:geometry (geometry
:x "100px"
:y "100px"
:anchor "top left"
)
:stacking "bottom"
:exclusive false
:focusable false
(box
:class "launcher-window stand-alone"
:orientation "v"
:spacing 4
:visible { arraylength(jq(workspaces-json-dp2, "map(select(.active and (.has_windows | not)))")) > 0 }
(box
:orientation "v"
:halign "start"
:spacing 4
:space-evenly false
(label
:text "Apps"
:halign "start"
)
(box
:orientation "h"
:halign "start"
:spacing 4
:space-evenly false
(button
:onclick "firefox"
(image
:class "launcher-icon"
:icon "firefox"
:icon-size "dialog"
)
)
)
)
(box
:orientation "v"
:halign "start"
:spacing 4
:space-evenly false
(label
:text "Games"
:halign "start"
)
(box
:orientation "h"
:halign "start"
:spacing 4
:space-evenly false
(button
;; :onclick "env LUTRIS_SKIP_INIT=1 lutris lutris:rungameid/1"
:onclick "/home/drew/.local/bin/wow.sh >/tmp/wow.log 2>&1"
(image
:class "launcher-icon"
:image-width 48
:path "/home/drew/.local/share/icons/hicolor/128x128/apps/lutris_battlenet.png"
)
)
;; (button
;; :onclick "steam steam://rungameid/1145350"
;; (image
;; :class "launcher-icon"
;; :icon "steam_icon_1145350"
;; :icon-size "dialog"
;; )
;;)
)
)
)
)

View File

@@ -45,7 +45,7 @@
(system-monitor-perf-gpu) (system-monitor-perf-gpu)
) )
(disks-vega) (disks-vega)
(system-monitor-net :interface "enp3s0") (system-monitor-net :interface "wlp5s0")
(system-monitor-audio) (system-monitor-audio)
) )
) )

View File

@@ -16,7 +16,7 @@ let
# ''; # '';
warcraftLogsUploader = pkgs.fetchurl { warcraftLogsUploader = pkgs.fetchurl {
name = "warcraftlogs-client"; name = "warcraftlogs-client";
url = "https://github.com/RPGLogs/Uploaders-warcraftlogs/releases/download/v8.16.56/warcraftlogs-v8.16.56.AppImage"; url = "https://github.com/RPGLogs/Uploaders-warcraftlogs/releases/download/v8.17.47/warcraftlogs-v8.17.47.AppImage";
sha256 = "1aypr3ffy6lq0qj64d48c7n54nfs72404xb2kpxsw5slqh66imw6"; sha256 = "1aypr3ffy6lq0qj64d48c7n54nfs72404xb2kpxsw5slqh66imw6";
}; };
warcraftLogsIcon = pkgs.fetchurl { warcraftLogsIcon = pkgs.fetchurl {
@@ -27,7 +27,7 @@ let
raiderioClient = pkgs.fetchurl { raiderioClient = pkgs.fetchurl {
name = "raiderio-client"; name = "raiderio-client";
url = "https://raider.io/client/download/linux"; url = "https://raider.io/client/download/linux";
sha256 = "1iny8zhp12x40mnxxr7p6kbyyvxf16373d2qa8idxs3hw5fz7gnx"; sha256 = "0wcw53bgr9dr02x1ci2jlnc5irpiqxqxgs2hpbrsnj67q50nvlm9";
}; };
raiderioIcon = pkgs.fetchurl { raiderioIcon = pkgs.fetchurl {
name = "raiderio-icon"; name = "raiderio-icon";
@@ -47,10 +47,14 @@ in
}) })
protonup-ng protonup-ng
protonplus protonplus
protontricks
vulkan-tools # useful for debugging Vulkan issues vulkan-tools # useful for debugging Vulkan issues
# WoW addon updater # WoW addon updater
wowup-cf wowup-cf
# Nexus Mod Manager
nexusmods-app-unfree
]; ];
# xdg.dataFile."applications/wowup-cf.desktop" = { # xdg.dataFile."applications/wowup-cf.desktop" = {
@@ -81,7 +85,7 @@ in
categories = [ "Game" ]; categories = [ "Game" ];
}; };
xdg.desktopEntries.raiderio = local.electronDesktopEntry { xdg.desktopEntries.raiderio = {
name = "Raider.io"; name = "Raider.io";
exec = "${pkgs.appimage-run}/bin/appimage-run ${raiderioClient}"; exec = "${pkgs.appimage-run}/bin/appimage-run ${raiderioClient}";
terminal = false; terminal = false;
@@ -100,11 +104,16 @@ in
# Make sure WoW spawns on the right monitor and that Battlenet floats so it renders correctly # Make sure WoW spawns on the right monitor and that Battlenet floats so it renders correctly
"monitor 1,title:^World of Warcraft$" "monitor 1,title:^World of Warcraft$"
"fullscreen,title:^World of Warcraft$"
"monitor 1,title:^Battle.net$" "monitor 1,title:^Battle.net$"
"float,title:^Battle.net$" "float,title:^Battle.net$"
# Make Balatro into a regular window. # Make Balatro into a regular window.
"monitor 1,title:^Balatro$" "monitor 1,title:^Balatro$"
"tile,title:^Balatro$" "tile,title:^Balatro$"
# Load Cyberpunk 2077 on the right monitor.
"monitor 1,class:steam_app_1091500"
"fullscreen,class:steam_app_1091500"
]; ];
} }

View File

@@ -4,23 +4,23 @@
enable = true; enable = true;
settings = { settings = {
general = { general = {
lock_cmd = "pidof hyprlock || hyprlock"; # avoid starting multiple hyprlock instances. lock_cmd = "pidof swaylock || ~/.config/swaylock/swaylock.sh"; # avoid starting multiple lock instances.
before_sleep_cmd = "loginctl lock-session"; # lock before suspend. before_sleep_cmd = "loginctl lock-session"; # lock before suspend.
after_sleep_cmd = "hyprctl dispatch dpms on"; # to avoid having to press a key twice to turn on the display. after_sleep_cmd = "hyprctl dispatch dpms on"; # to avoid having to press a key twice to turn on the display.
}; };
listener = [ listener = [
{ {
timeout = 150; # 2.5min. timeout = 300; # 5min
on-timeout = "brightnessctl -s set 10"; # set monitor backlight to minimum, avoid 0 on OLED monitor. on-timeout = "brightnessctl -s set 10"; # set monitor backlight to minimum, avoid 0 on OLED monitor.
on-resume = "brightnessctl -r"; # monitor backlight restore. on-resume = "brightnessctl -r"; # monitor backlight restore.
} }
{ {
timeout = 300; # 5min timeout = 330; # 5.5 min
on-timeout = "loginctl lock-session"; # lock screen when timeout has passed on-timeout = "loginctl lock-session"; # lock screen when timeout has passed
} }
{ {
timeout = 330; # 5.5min timeout = 600; # 10 min
on-timeout = "hyprctl dispatch dpms off"; # screen off when timeout has passed on-timeout = "hyprctl dispatch dpms off"; # screen off when timeout has passed
on-resume = "hyprctl dispatch dpms on && brightnessctl -r"; # screen on when activity is detected after timeout has fired. on-resume = "hyprctl dispatch dpms on && brightnessctl -r"; # screen on when activity is detected after timeout has fired.
} }

View File

@@ -1,6 +1,7 @@
{ pkgs, ... }: { pkgs, ... }:
{ {
imports = [ imports = [
./swaylock.nix
./hypr/hypridle.nix ./hypr/hypridle.nix
]; ];
@@ -35,7 +36,7 @@
"$terminal" = "foot"; "$terminal" = "foot";
"$menu" = "rofi -show combi -combi-modes drun,ssh,run -theme ~/.config/rofi/launcher/style.rasi"; "$menu" = "rofi -show combi -combi-modes drun,ssh,run -theme ~/.config/rofi/launcher/style.rasi";
"$browser" = "firefox"; "$browser" = "firefox --new-window";
exec-once = [ exec-once = [
"nm-applet" "nm-applet"
@@ -286,6 +287,7 @@
"$mainMod, B, exec, $browser" "$mainMod, B, exec, $browser"
"$mainMod, D, exec, $menu" "$mainMod, D, exec, $menu"
"$mainMod + SHIFT, S, exec, hyprshot -m region --clipboard-only" "$mainMod + SHIFT, S, exec, hyprshot -m region --clipboard-only"
"$mainMod + CTRL + SHIFT, S, exec, hyprshot -m region -o ~/Pictures"
"$mainMod, C, exec, swaync-client -t" "$mainMod, C, exec, swaync-client -t"
"$mainMod + L_CONTROL, Q, exec, /home/drew/.config/rofi/powermenu/powermenu.sh" "$mainMod + L_CONTROL, Q, exec, /home/drew/.config/rofi/powermenu/powermenu.sh"
@@ -426,7 +428,7 @@
}; };
programs.hyprlock = { programs.hyprlock = {
enable = true; enable = false;
settings = { settings = {
general = { general = {
disable_loading_bar = true; disable_loading_bar = true;

View File

@@ -0,0 +1,7 @@
{ pkgs, ... }:
{
home.packages = with pkgs; [
gimp3
inkscape
];
}

View File

@@ -13,7 +13,7 @@
home = { home = {
packages = with pkgs; [ packages = with pkgs; [
# Desktop Applications # Desktop Applications
signal-desktop gimp3
# Common utilities # Common utilities
feh feh
@@ -50,8 +50,8 @@
programs = { programs = {
# browsers # browsers
firefox.enable = true; firefox.enable = true;
qutebrowser.enable = true;
librewolf.enable = true; librewolf.enable = true;
chromium.enable = true;
}; };
# GTK settings # GTK settings
@@ -83,4 +83,18 @@
platformTheme.name = "adwaita"; platformTheme.name = "adwaita";
style.name = "adwaita-dark"; style.name = "adwaita-dark";
}; };
# Default apps
xdg.mimeApps = {
enable = true;
defaultApplications = {
"text/html" = [ "firefox.desktop" ];
"default-web-browser" = [ "firefox.desktop" ];
"x-scheme-handler/http" = [ "firefox.desktop" ];
"x-scheme-handler/https" = [ "firefox.desktop" ];
"x-scheme-handler/about" = [ "firefox.desktop" ];
"x-scheme-handler/unknown" = [ "firefox.desktop" ];
};
};
home.sessionVariables.DEFAULT_BROWSER = "${pkgs.firefox}/bin/firefox";
} }

View File

@@ -0,0 +1,18 @@
{
"extras": [
"lazyvim.plugins.extras.coding.mini-comment",
"lazyvim.plugins.extras.coding.mini-surround",
"lazyvim.plugins.extras.editor.snacks_picker",
"lazyvim.plugins.extras.lang.astro",
"lazyvim.plugins.extras.lang.haskell",
"lazyvim.plugins.extras.lang.json",
"lazyvim.plugins.extras.lang.markdown",
"lazyvim.plugins.extras.lang.nix",
"lazyvim.plugins.extras.lang.rust",
"lazyvim.plugins.extras.lang.tailwind",
"lazyvim.plugins.extras.lang.toml",
"lazyvim.plugins.extras.lang.typescript"
],
"install_version": 8,
"version": 8
}

View File

@@ -4,3 +4,6 @@
-- Creates a shortcut for adding the directory of the current buffer when specifying a file -- Creates a shortcut for adding the directory of the current buffer when specifying a file
vim.cmd("cnoreabbrev %. %:h<Tab>") vim.cmd("cnoreabbrev %. %:h<Tab>")
-- Set snacks as the preferred picker.
vim.g.lazyvim_picker = "snacks"

View File

@@ -1,4 +0,0 @@
return {
"augmentcode/augment.vim",
enable = true,
}

View File

@@ -31,7 +31,7 @@ return {
cmp.show() cmp.show()
end, end,
}, },
["<C-enter>"] = { "select_and_accept" }, ["<Tab>"] = { "select_and_accept", "snippet_forward", "fallback" },
}, },
}, },
} }

View File

@@ -1,14 +0,0 @@
return {
{
"zbirenbaum/copilot.lua",
opts = {
filetypes = {
markdown = false,
help = false,
},
suggestion = {
enabled = false,
},
},
},
}

View File

@@ -1,5 +1,5 @@
return { return {
-- Maeson installs it's own binaries that are incompatible with NixOS. -- Maeson installs it's own binaries that are incompatible with NixOS.
{ "williamboman/mason.nvim", enabled = false }, { "mason-org/mason.nvim", enabled = false },
{ "williamboman/mason-lspconfig.nvim", enabled = false }, { "mason-org/mason-lspconfig.nvim", enabled = false },
} }

View File

@@ -3,9 +3,14 @@ return {
"neovim/nvim-lspconfig", "neovim/nvim-lspconfig",
opts = { opts = {
servers = { servers = {
-- Lua
lua_ls = {}, lua_ls = {},
-- Nix
nil_ls = {}, nil_ls = {},
-- Typescript
vtsls = {}, vtsls = {},
-- Haskell
hls = {},
}, },
codelens = { codelens = {
enable = true, enable = true,

View File

@@ -1,3 +1,4 @@
-- https://github.com/MeanderingProgrammer/render-markdown.nvim?tab=readme-ov-file#setup
return { return {
"MeanderingProgrammer/render-markdown.nvim", "MeanderingProgrammer/render-markdown.nvim",
opts = { opts = {

View File

@@ -1,10 +1,38 @@
return { return {
{ {
"echasnovski/mini.surround", "nvim-mini/mini.surround",
enable = true, enable = true,
keys = function(_, keys)
-- Populate the keys based on the user's options
local opts = LazyVim.opts("mini.surround")
local mappings = {
{ opts.mappings.add, desc = "Add Surrounding", mode = { "n", "v" } },
{ opts.mappings.delete, desc = "Delete Surrounding" },
{ opts.mappings.find, desc = "Find Right Surrounding" },
{ opts.mappings.find_left, desc = "Find Left Surrounding" },
{ opts.mappings.highlight, desc = "Highlight Surrounding" },
{ opts.mappings.replace, desc = "Replace Surrounding" },
{ opts.mappings.update_n_lines, desc = "Update `MiniSurround.config.n_lines`" },
}
mappings = vim.tbl_filter(function(m)
return m[1] and #m[1] > 0
end, mappings)
return vim.list_extend(mappings, keys)
end,
opts = {
mappings = {
add = "gsa", -- Add surrounding in Normal and Visual modes
delete = "gsd", -- Delete surrounding
find = "gsf", -- Find surrounding (to the right)
find_left = "gsF", -- Find surrounding (to the left)
highlight = "gsh", -- Highlight surrounding
replace = "gsr", -- Replace surrounding
update_n_lines = "gsn", -- Update `n_lines`
},
},
}, },
{ {
"echasnovski/mini.comment", "nvim-mini/mini.comment",
enable = true, enable = true,
}, },
} }

View File

@@ -1,22 +1,7 @@
return { return {
"epwalsh/obsidian.nvim", "obsidian-nvim/obsidian.nvim",
version = "*", -- recommended, use latest release instead of latest commit version = "*", -- recommended, use latest release instead of latest commit
lazy = true,
ft = "markdown", ft = "markdown",
-- Replace the above line with this if you only want to load obsidian.nvim for markdown files in your vault:
-- event = {
-- -- If you want to use the home shortcut '~' here you need to call 'vim.fn.expand'.
-- -- E.g. "BufReadPre " .. vim.fn.expand "~" .. "/my-vault/*.md"
-- -- refer to `:h file-pattern` for more examples
-- "BufReadPre path/to/my-vault/*.md",
-- "BufNewFile path/to/my-vault/*.md",
-- },
dependencies = {
-- Required.
"nvim-lua/plenary.nvim",
-- For the picker
"nvim-telescope/telescope.nvim",
},
opts = { opts = {
workspaces = { workspaces = {
{ {
@@ -54,8 +39,10 @@ return {
checkboxes = { checkboxes = {
[" "] = { char = "", hl_group = "ObsidianTodo" }, [" "] = { char = "", hl_group = "ObsidianTodo" },
["x"] = { char = "", hl_group = "ObsidianDone" }, ["x"] = { char = "", hl_group = "ObsidianDone" },
["/"] = { char = "", hl_group = "ObsidianDone" }, ["/"] = { char = "", hl_group = "ObsidianTodo" },
[">"] = { char = "", hl_group = "ObsidianDone" }, [">"] = { char = "»", hl_group = "ObsidianRightArrow" },
["~"] = { char = "»", hl_group = "ObsidianTilde" },
["!"] = { char = "", hl_group = "ObsidianDone" },
}, },
}, },
@@ -78,5 +65,9 @@ return {
end end
end end
end, end,
follow_url_func = function(url)
vim.ui.open(url) -- Use the built-in open, need Neovim 0.10.0+
end,
}, },
} }

View File

@@ -1,37 +1,15 @@
return { return {
"folke/snacks.nvim", "folke/snacks.nvim",
---@type snacks.Config opts = function(_, opts)
opts = { vim.tbl_deep_extend("force", opts, {
picker = { picker = {
smart = { smart = {
-- Remove the "recent" picker so we don't get things from other directories.
multi = { "buffers", "files" },
matcher = {
-- sort even when the search string is empty
sort_empty = true,
-- Enable frecensy for matchers. This puts more common files near the top
frecency = false,
-- Make sure files in the current directory are prioritized
cwd_bonus = true,
-- Give more weight to files that are more recent
history_bonus = true,
},
},
},
},
keys = {
{
"<leader><space>",
function()
Snacks.picker.smart({
-- Remove the "recent" picker so we don't get things from other directories. -- Remove the "recent" picker so we don't get things from other directories.
multi = { "buffers", "files" }, multi = { "buffers", "files" },
matcher = { matcher = {
-- sort even when the search string is empty -- sort even when the search string is empty
sort_empty = false, sort_empty = true,
-- Enable frecensy for matchers. This puts more common files near -- Enable frecensy for matchers. This puts more common files near the top
-- the top This includes files that aren't open and can put files I
-- am done with above open files, so it's off.
frecency = false, frecency = false,
-- Make sure files in the current directory are prioritized -- Make sure files in the current directory are prioritized
cwd_bonus = true, cwd_bonus = true,
@@ -40,261 +18,48 @@ return {
-- Give more weight to places where the filename is part of the match -- Give more weight to places where the filename is part of the match
filename_bonus = true, filename_bonus = true,
}, },
}) },
sources = {
explorer = {
layout = { layout = { position = "right" } },
},
},
-- This only supports the Kitty graphics protocol.
-- See
-- https://github.com/folke/snacks.nvim/blob/main/docs/image.md
-- https://github.com/obsidian-nvim/obsidian.nvim/wiki/Images
-- image = {
-- resolve = function(path, src)
-- if require("obsidian.api").path_is_note(path) then
-- return require("obsidian.api").resolve_image_path(src)
-- end
-- end,
-- },
},
})
Snacks.toggle({
name = "Color Column",
get = function()
return vim.o.colorcolumn == "80"
end,
set = function(state)
if state then
vim.o.colorcolumn = "80"
vim.cmd([[highlight ColorColumn guibg=#202020]])
else
vim.o.colorcolumn = ""
vim.cmd([[highlight ColorColumn guibg=None]])
end
end,
}):map("<leader>ut", { desc = "Toggle Color Column" })
end,
keys = {
{
"<leader><space>",
function()
Snacks.picker.smart()
end, end,
desc = "Smart Find Files", desc = "Smart Find Files",
}, },
-- The rest of these are just default bindings. Setting the one binding above seems to override the others.
{
"<leader>,",
function()
Snacks.picker.buffers()
end,
desc = "Buffers",
},
{ "<leader>/", LazyVim.pick("grep"), desc = "Grep (Root Dir)" },
{
"<leader>:",
function()
Snacks.picker.command_history()
end,
desc = "Command History",
},
{
"<leader>n",
function()
Snacks.picker.notifications()
end,
desc = "Notification History",
},
-- find
{
"<leader>fb",
function()
Snacks.picker.buffers()
end,
desc = "Buffers",
},
{
"<leader>fB",
function()
Snacks.picker.buffers({ hidden = true, nofile = true })
end,
desc = "Buffers (all)",
},
{ "<leader>fc", LazyVim.pick.config_files(), desc = "Find Config File" },
{ "<leader>ff", LazyVim.pick("files"), desc = "Find Files (Root Dir)" },
{ "<leader>fF", LazyVim.pick("files", { root = false }), desc = "Find Files (cwd)" },
{
"<leader>fg",
function()
Snacks.picker.git_files()
end,
desc = "Find Files (git-files)",
},
{ "<leader>fr", LazyVim.pick("oldfiles"), desc = "Recent" },
{
"<leader>fR",
function()
Snacks.picker.recent({ filter = { cwd = true } })
end,
desc = "Recent (cwd)",
},
{
"<leader>fp",
function()
Snacks.picker.projects()
end,
desc = "Projects",
},
-- git
{
"<leader>gd",
function()
Snacks.picker.git_diff()
end,
desc = "Git Diff (hunks)",
},
{
"<leader>gs",
function()
Snacks.picker.git_status()
end,
desc = "Git Status",
},
{
"<leader>gS",
function()
Snacks.picker.git_stash()
end,
desc = "Git Stash",
},
-- Grep
{
"<leader>sb",
function()
Snacks.picker.lines()
end,
desc = "Buffer Lines",
},
{
"<leader>sB",
function()
Snacks.picker.grep_buffers()
end,
desc = "Grep Open Buffers",
},
{ "<leader>sg", LazyVim.pick("live_grep"), desc = "Grep (Root Dir)" },
{ "<leader>sG", LazyVim.pick("live_grep", { root = false }), desc = "Grep (cwd)" },
{
"<leader>sp",
function()
Snacks.picker.lazy()
end,
desc = "Search for Plugin Spec",
},
{ "<leader>sw", LazyVim.pick("grep_word"), desc = "Visual selection or word (Root Dir)", mode = { "n", "x" } },
{
"<leader>sW",
LazyVim.pick("grep_word", { root = false }),
desc = "Visual selection or word (cwd)",
mode = { "n", "x" },
},
-- search
{
'<leader>s"',
function()
Snacks.picker.registers()
end,
desc = "Registers",
},
{
"<leader>s/",
function()
Snacks.picker.search_history()
end,
desc = "Search History",
},
{
"<leader>sa",
function()
Snacks.picker.autocmds()
end,
desc = "Autocmds",
},
{
"<leader>sc",
function()
Snacks.picker.command_history()
end,
desc = "Command History",
},
{
"<leader>sC",
function()
Snacks.picker.commands()
end,
desc = "Commands",
},
{
"<leader>sd",
function()
Snacks.picker.diagnostics()
end,
desc = "Diagnostics",
},
{
"<leader>sD",
function()
Snacks.picker.diagnostics_buffer()
end,
desc = "Buffer Diagnostics",
},
{
"<leader>sh",
function()
Snacks.picker.help()
end,
desc = "Help Pages",
},
{
"<leader>sH",
function()
Snacks.picker.highlights()
end,
desc = "Highlights",
},
{
"<leader>si",
function()
Snacks.picker.icons()
end,
desc = "Icons",
},
{
"<leader>sj",
function()
Snacks.picker.jumps()
end,
desc = "Jumps",
},
{
"<leader>sk",
function()
Snacks.picker.keymaps()
end,
desc = "Keymaps",
},
{
"<leader>sl",
function()
Snacks.picker.loclist()
end,
desc = "Location List",
},
{
"<leader>sM",
function()
Snacks.picker.man()
end,
desc = "Man Pages",
},
{
"<leader>sm",
function()
Snacks.picker.marks()
end,
desc = "Marks",
},
{
"<leader>sR",
function()
Snacks.picker.resume()
end,
desc = "Resume",
},
{
"<leader>sq",
function()
Snacks.picker.qflist()
end,
desc = "Quickfix List",
},
{
"<leader>su",
function()
Snacks.picker.undo()
end,
desc = "Undotree",
},
-- ui
{
"<leader>uC",
function()
Snacks.picker.colorschemes()
end,
desc = "Colorschemes",
},
}, },
} }

View File

@@ -1,32 +0,0 @@
-- https://www.lazyvim.org/extras/coding/mini-surround#minisurround
return {
"echasnovski/mini.surround",
keys = function(_, keys)
-- Populate the keys based on the user's options
local opts = LazyVim.opts("mini.surround")
local mappings = {
{ opts.mappings.add, desc = "Add Surrounding", mode = { "n", "v" } },
{ opts.mappings.delete, desc = "Delete Surrounding" },
{ opts.mappings.find, desc = "Find Right Surrounding" },
{ opts.mappings.find_left, desc = "Find Left Surrounding" },
{ opts.mappings.highlight, desc = "Highlight Surrounding" },
{ opts.mappings.replace, desc = "Replace Surrounding" },
{ opts.mappings.update_n_lines, desc = "Update `MiniSurround.config.n_lines`" },
}
mappings = vim.tbl_filter(function(m)
return m[1] and #m[1] > 0
end, mappings)
return vim.list_extend(mappings, keys)
end,
opts = {
mappings = {
add = "gsa", -- Add surrounding in Normal and Visual modes
delete = "gsd", -- Delete surrounding
find = "gsf", -- Find surrounding (to the right)
find_left = "gsF", -- Find surrounding (to the left)
highlight = "gsh", -- Highlight surrounding
replace = "gsr", -- Replace surrounding
update_n_lines = "gsn", -- Update `n_lines`
},
},
}

View File

@@ -0,0 +1,23 @@
return {
"nvim-treesitter/nvim-treesitter",
opts = {
ensure_installed = {
"astro",
"bash",
"html",
"css",
"javascript",
"json",
"lua",
"markdown",
"markdown_inline",
"python",
"query",
"regex",
"tsx",
"typescript",
"vim",
"yaml",
},
},
}

View File

@@ -9,6 +9,7 @@
extraPackages = with pkgs; [ extraPackages = with pkgs; [
gcc # For treesitter complation gcc # For treesitter complation
tree-sitter # For treesitter binaries
ripgrep # Search support ripgrep # Search support
wayclip # Clipboard support wayclip # Clipboard support
fd # finder for telescope fd # finder for telescope

View File

@@ -4,6 +4,10 @@
obsidian obsidian
]; ];
home.shellAliases = {
"notes" = "(cd ~/Documents/Notes && nvim)";
};
services.syncthing = { services.syncthing = {
enable = true; enable = true;
tray = { tray = {
@@ -21,6 +25,7 @@
"altair" "altair"
"mcp" "mcp"
"vega" "vega"
"proxima"
]; ];
}; };
}; };
@@ -52,6 +57,14 @@
]; ];
compression = "always"; compression = "always";
}; };
proxima = {
id = "NWZL6LY-ULJQMZE-EWY3MQU-XPDAFQB-LTIBZV7-GPKIABJ-WBJE36F-SK6LVAY";
name = "Proxima";
addresses = [
"relay://syncthing.blazestar.net:22067"
];
compression = "always";
};
}; };
options = { options = {
localAnnounceEnabled = false; localAnnounceEnabled = false;

View File

@@ -11,7 +11,7 @@ dir="${HOME}/.config/rofi/powermenu"
# CMDs # CMDs
uptime="$(uptime | awk -F ' ' '{print $2}' | sed -e 's/,//g')" uptime="$(uptime | awk -F ' ' '{print $2}' | sed -e 's/,//g')"
host=`hostname` host=$(hostname)
# Options # Options
shutdown='󰐥' shutdown='󰐥'
@@ -24,89 +24,94 @@ no=''
# Rofi CMD # Rofi CMD
rofi_cmd() { rofi_cmd() {
rofi -dmenu \ rofi -dmenu \
-p "Uptime: $uptime" \ -p "Uptime: $uptime" \
-mesg "Uptime: $uptime" \ -mesg "Uptime: $uptime" \
-theme ${dir}/style.rasi -theme ${dir}/style.rasi
} }
# Confirmation CMD # Confirmation CMD
confirm_cmd() { confirm_cmd() {
rofi -theme-str 'window {location: center; anchor: center; fullscreen: false; width: 350px;}' \ rofi -theme-str 'window {location: center; anchor: center; fullscreen: false; width: 350px;}' \
-theme-str 'mainbox {children: [ "message", "listview" ];}' \ -theme-str 'mainbox {children: [ "message", "listview" ];}' \
-theme-str 'listview {columns: 2; lines: 1;}' \ -theme-str 'listview {columns: 2; lines: 1;}' \
-theme-str 'element-text {horizontal-align: 0.5;}' \ -theme-str 'element-text {horizontal-align: 0.5;}' \
-theme-str 'textbox {horizontal-align: 0.5;}' \ -theme-str 'textbox {horizontal-align: 0.5;}' \
-dmenu \ -dmenu \
-p 'Confirmation' \ -p 'Confirmation' \
-mesg 'Are you Sure?' \ -mesg 'Are you Sure?' \
-theme ${dir}/style.rasi -theme ${dir}/style.rasi
} }
# Ask for confirmation # Ask for confirmation
confirm_exit() { confirm_exit() {
echo -e "$yes\n$no" | confirm_cmd echo -e "$yes\n$no" | confirm_cmd
} }
# Pass variables to rofi dmenu # Pass variables to rofi dmenu
run_rofi() { run_rofi() {
echo -e "$lock\n$suspend\n$logout\n$reboot\n$shutdown" | rofi_cmd echo -e "$lock\n$suspend\n$logout\n$reboot\n$shutdown" | rofi_cmd
} }
# Execute Command # Execute Command
run_cmd() { run_cmd() {
selected="$(confirm_exit)" selected="$(confirm_exit)"
if [[ "$selected" == "$yes" ]]; then if [[ "$selected" == "$yes" ]]; then
if [[ $1 == '--shutdown' ]]; then if [[ $1 == '--shutdown' ]]; then
systemctl poweroff systemctl poweroff
elif [[ $1 == '--reboot' ]]; then elif [[ $1 == '--reboot' ]]; then
systemctl reboot systemctl reboot
elif [[ $1 == '--suspend' ]]; then elif [[ $1 == '--suspend' ]]; then
mpc -q pause mpc -q pause
amixer set Master mute amixer set Master mute
systemctl suspend systemctl suspend
elif [[ $1 == '--logout' ]]; then elif [[ $1 == '--logout' ]]; then
if [[ "$DESKTOP_SESSION" == 'openbox' ]]; then if [[ "$DESKTOP_SESSION" == 'openbox' ]]; then
openbox --exit openbox --exit
elif [[ "$DESKTOP_SESSION" == 'bspwm' ]]; then elif [[ "$DESKTOP_SESSION" == 'bspwm' ]]; then
bspc quit bspc quit
elif [[ "$DESKTOP_SESSION" == 'i3' ]]; then elif [[ "$DESKTOP_SESSION" == 'i3' ]]; then
i3-msg exit i3-msg exit
elif [[ "$DESKTOP_SESSION" == 'plasma' ]]; then elif [[ "$DESKTOP_SESSION" == 'plasma' ]]; then
qdbus org.kde.ksmserver /KSMServer logout 0 0 0 qdbus org.kde.ksmserver /KSMServer logout 0 0 0
elif [[ "$DESKTOP_SESSION" == 'plasma' ]]; then elif [[ "$DESKTOP_SESSION" == 'plasma' ]]; then
qdbus org.kde.ksmserver /KSMServer logout 0 0 0 qdbus org.kde.ksmserver /KSMServer logout 0 0 0
elif command -v hyprctl &>/dev/null; then elif [[ "$DESKTOP_SESSION" == 'hyprland-uwsm' ]]; then
hyprctl dispatch exit hyprctl dispatch exit
fi elif [[ "$DESKTOP_SESSION" == 'hyprland' ]]; then
fi hyprctl dispatch exit
else fi
exit 0 fi
fi else
exit 0
fi
} }
# Actions # Actions
chosen="$(run_rofi)" chosen="$(run_rofi)"
case ${chosen} in case ${chosen} in
$shutdown) $shutdown)
run_cmd --shutdown run_cmd --shutdown
;; ;;
$reboot) $reboot)
run_cmd --reboot run_cmd --reboot
;; ;;
$lock) $lock)
if [[ -x '/usr/bin/betterlockscreen' ]]; then loginctl lock-session
betterlockscreen -l # if [[ -x '/usr/bin/betterlockscreen' ]]; then
elif [[ -x '/usr/bin/i3lock' ]]; then # betterlockscreen -l
i3lock # elif [[ -x '/usr/bin/i3lock' ]]; then
elif command -v hyprlock &>/dev/null; then # i3lock
hyprlock # elif command -v hyprlock &>/dev/null; then
fi # hyprlock
;; # elif command -v swaylock &>/dev/null; then
$suspend) # swaylock
run_cmd --suspend # fi
;; ;;
$logout) $suspend)
run_cmd --logout run_cmd --suspend
;; ;;
$logout)
run_cmd --logout
;;
esac esac

View File

@@ -15,17 +15,19 @@
htop htop
btop btop
neofetch neofetch
killall psmisc # fuser, killal, pstree
# Files # Archives
zip zip
xz xz
unzip unzip
p7zip p7zip
unrar-wrapper
# File manipulation
file file
tree tree
yazi # File manager yazi # File manager
ueberzugpp # for image previews
w3m # terminal browser for image previews w3m # terminal browser for image previews
dysk # better disk info dysk # better disk info
ripgrep # better grep ripgrep # better grep
@@ -46,20 +48,20 @@
# Other # Other
jq jq
mprocs # Manage multiple long-running processes
]; ];
home.shellAliases = { home.shellAliases = {
"p?" = "ps ax | grep"; "p?" = "ps ax | rg";
# Dysk is basically just better. # Dysk is basically just better.
"df" = "echo 'Don't you want `dysk`?'"; "df" = "echo 'Do you mean: dysk?'";
"grep" = "echo 'Don't you want `rg`?'"; "grep" = "echo 'Do you mean: rg?'";
"find" = "echo 'Don't you want `fd`?'"; "find" = "echo 'Do you mean: fd'";
"cat" = "bat"; "cat" = "bat";
"ls" = "eza"; "ls" = "eza";
"http" = "echo 'Don't you want `xh`?'"; "http" = "echo 'Do you mean: xh'";
"du" = "echo 'Don't you want `dust` or `dua`?'"; "du" = "echo 'Do you mean: dust or dua?'";
"ranger" = "echo 'Don't you want `yazi`?'"; "ranger" = "echo 'Do you mean: yazi'";
}; };
programs.zsh = { programs.zsh = {

View File

@@ -1,21 +1,12 @@
{ {
config, config,
lib, lib,
pkgs,
... ...
}: }:
{ {
imports = [ imports = [
./swaync.nix ./swaync.nix
]; ./swaylock.nix
home.packages = with pkgs; [
hyprlock # lock screen
# swayidle # lock on idle
# swayosd # volume pop-up
# swaynotificationcenter # notifications
# hyprpolkitagent # Privilege managent
# hyprshot # Screenshot utility
]; ];
services.swayosd = { services.swayosd = {
@@ -23,16 +14,6 @@
topMargin = 0.7; topMargin = 0.7;
}; };
services.swayidle = {
enable = true;
events = [
{
event = "before-sleep";
command = "hyprlock";
}
];
};
# Enable sway config generation for home.pointerCursor # Enable sway config generation for home.pointerCursor
home.pointerCursor.sway.enable = true; home.pointerCursor.sway.enable = true;

View File

@@ -0,0 +1,37 @@
{ pkgs, ... }:
{
programs.swaylock = {
enable = true;
package = pkgs.swaylock-effects;
settings = {
clock = true;
indicator = true;
effect-pixelate = 20;
};
};
home.file.".config/swaylock/swaylock.sh" = {
executable = true;
# A script that will take the wallpaper folder for each monitor (see
# wallpaper.nix) and use a random image as the background on each monitor.
text = ''
#!/usr/bin/env sh
wallpaper_dir="$HOME/Pictures/Wallpaper"
cmd="swaylock"
for monitor in $(hyprctl monitors | awk '/^Monitor/ { print $2 }'); do
dir="$wallpaper_dir/$monitor"
if [ -d "$dir" ]; then
image=$(find "$dir" -type f | shuf -n 1)
if [ -n "$image" ]; then
cmd="$cmd --image \"$monitor:$image\""
fi
fi
done
# Eval to handle the quoted image paths correctly
eval $cmd
'';
};
}

View File

@@ -13,6 +13,7 @@
nerd-fonts.jetbrains-mono nerd-fonts.jetbrains-mono
libsixel # For working with images in terminals libsixel # For working with images in terminals
ueberzugpp # for image previews
]; ];
# Allow Home Manager to set fonts. # Allow Home Manager to set fonts.

View File

@@ -12,6 +12,7 @@ monitors:
map (mon: { map (mon: {
name = mon; name = mon;
value = { value = {
# Each monitor gets a folder under wallpaper that containes the current images.
path = "~/Pictures/Wallpaper/${mon}"; path = "~/Pictures/Wallpaper/${mon}";
}; };
}) monitors }) monitors

View File

@@ -11,11 +11,23 @@ focalboard:
offen: offen:
secret: ENC[AES256_GCM,data:sH2siPc/QH1O2M7ZlJwqhqlHRIeLIG9r,iv:eD29ALx2ji0rm1t9j6RulTZT3f6VLK7dxpPOze3qDKA=,tag:zqJTgT2UeA/ecBS4VremUw==,type:str] secret: ENC[AES256_GCM,data:sH2siPc/QH1O2M7ZlJwqhqlHRIeLIG9r,iv:eD29ALx2ji0rm1t9j6RulTZT3f6VLK7dxpPOze3qDKA=,tag:zqJTgT2UeA/ecBS4VremUw==,type:str]
smtp-token: ENC[AES256_GCM,data:ZTfe65g3JykPvG2l0AN8UQ==,iv:GTruGo/vcP+imfJyqB3NX9ic8dz5jvTEh6SF+OeqMDM=,tag:kgwd59pG/WUt8OAaVzi39Q==,type:str] smtp-token: ENC[AES256_GCM,data:ZTfe65g3JykPvG2l0AN8UQ==,iv:GTruGo/vcP+imfJyqB3NX9ic8dz5jvTEh6SF+OeqMDM=,tag:kgwd59pG/WUt8OAaVzi39Q==,type:str]
traefik:
oauth2-client-secret: ENC[AES256_GCM,data:p7/6OsN2ytBj8mQiK0YL7J6NYLtMHOXIIs/6+bIDpsU=,iv:k6jLZifJEFLYKSFMkyn/kA7iBE+EFB8O/3/3fyTh1SY=,tag:6s49O2+tdlZoXyAGEamuMQ==,type:str]
oauth2-plugin-secret: ENC[AES256_GCM,data:sArqwKHAdW35o5kD7DGfXSYCXFUXqvKQdoVnXutsNLw=,iv:qWf597QS3BqkVQkeAb99HbpDB0kUhdD+qKdpUPZEB0o=,tag:vXnb93npaklItWkMZ+/M9Q==,type:str]
protonvpn:
private_key: ENC[AES256_GCM,data:41pfbR1klj1F24v3HlCCA4ofW2sCEnyE5TH8iX4Ug8D+kmwstTaj5RG2Zz8=,iv:P6XyQnDVoOmdkP8ilBR9DyfqPZA6GsQ6VUwY/tSGhx4=,tag:Bzgdv29lbk/gYlADPZMGVA==,type:str]
deploy-key:
mcp: ENC[AES256_GCM,data:eQcX8xdz5qZ6nU8ISOvZo+ZtP4Z/ePd+/ZZReX1BKvTUqGQPPFxConbLMwFzvzpD6xAUbA1MLkcR/bT98QbNx6LJYlhbofuDUg2DI79RB0fcrAcj/wUV0YPhmgofUdYYDaimH5A2PSvtmKfB3CtKKuA5HNeLymoXeLEpFzbckkGhzPee/CHiUmxayogp6za6btsDJsiT8hdHbrzyD2S6fhMJrzX+PlRzT32M/6eaFuFWE8EUO1gbkRlNfKPXw/EM2GXWJfR4qXfN2YKIKigqrtlAAoxnrUbp5EBrn/hGHS2ZYZXeRUFr3avFjcI0bLX423PWRHAylfQCPxgYVEtbcRv11CAFmq4rfFl0ZdvnAKbTLNmWcrQNijBATZPaAdQzgKPDHs8pwPUFR9Tcg8pZNbzw0mK9kPolniAOL2PBKUHv6LP/uEkB1E6Pxc7yms0kGpeJyo7hrFVOiVAckCey+SI9dpbJMSB3md070I1xk6Ik7PywrWh2QDeUtOU1U28UkYgnJ/9MJelWsNlUX9SR,iv:oCNeanaV/7UZ3dhmq4ZmJUZ5hb61AnHpHCfskM2Jsm8=,tag:F2uJKN5beM/rfiBMSyUP7w==,type:str]
matrix:
syncv3:
db-password: ENC[AES256_GCM,data:N/IO0k/2BZpmaDTbKZmSgZNzmdk=,iv:p0jGjJ9mTCh5FPM/Oe1vxusYvlyg14UeggE5ynpDVL8=,tag:tZbddwxJf6wSH6L1QRUQVg==,type:str]
secret: ENC[AES256_GCM,data:KZjYxjUxGgkY1I5jGF7XMEhkHK+khDaQzxugoKxpLsROmVs722tFfbUAxhp71llam55gy9+eUWGxIPlmvOySlw==,iv:OoThGcT08Z11kpnAMQ7w59wj5JheNFGEk1jfFENsmy0=,tag:8EeKT7dh2/a52Amf6LsL1w==,type:str]
blazestar-registration-token: ENC[AES256_GCM,data:TB3bR+E4H4c2l9pRcEOAZr35+vBVaJUcuCs9K0Pjd0aW+M35x5LgZ8+F99Y=,iv:e28sie6LSI5UX41BPb+yN+3n+Yw9Ssfsqe4zppwbPkU=,tag:cQPgZcRFbYSiZnmPVtZxHg==,type:str]
tandoor:
secret_key: ENC[AES256_GCM,data:nl7S2fS1wENrT5k2iZfLEAGc99lCUktgwR5L5KklF69BNVKQkW1rUgb3aIv50VpXZa+3OxV/vdPmG9NhKMy96I5+Dno=,iv:FFyGQBARz0B5zrONZELzUMsOIn8TWrDNTKGsAHPlS7w=,tag:/c4MnDfLXQpBZDqVxZ0DTg==,type:str]
immich:
database: ENC[AES256_GCM,data:1fjOQsLZcq/T+r+AkzomWwCQWw==,iv:c4pn2rC+3xkxLJ7uAdhnTE6zVTRQkfuKK3tjUyDhfAw=,tag:kvk7DOv6X/+RDxfPxVak7w==,type:str]
sops: sops:
kms: []
gcp_kms: []
azure_kv: []
hc_vault: []
age: age:
- recipient: age1yvdzvuvu5wqztcx6ll2xk6x547uuyqy735tjjdd7zftkz53jsf9qf5ahue - recipient: age1yvdzvuvu5wqztcx6ll2xk6x547uuyqy735tjjdd7zftkz53jsf9qf5ahue
enc: | enc: |
@@ -35,8 +47,7 @@ sops:
by9aNFY4dXNxaWxnTXFTQS9reHhuQWMKh5rZ93nFtBV9EpFVRp+E+GXZ6xzVy2Jw by9aNFY4dXNxaWxnTXFTQS9reHhuQWMKh5rZ93nFtBV9EpFVRp+E+GXZ6xzVy2Jw
vFh4deGcAb60q4odSaeWfk1Dr7L9Ua69oK9omjbCNUt+P7Kwlfca7Q== vFh4deGcAb60q4odSaeWfk1Dr7L9Ua69oK9omjbCNUt+P7Kwlfca7Q==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
lastmodified: "2025-04-28T23:33:42Z" lastmodified: "2026-02-25T00:28:13Z"
mac: ENC[AES256_GCM,data:cZkRcGV5/CPPVUdTDekwC8UjO6K348sBsS7NvR8wnoXS0AmSZsqN594nkvoc0VccM55Hwnm4jZxY56OV+UFMya1IRIkTo6LJRb88/CgZ8bjz30ACe33FKgJfCugimUDKsekbgNX1UFg1DVbqYK9/N4fcEBSxV3Xmzy5QGnQ/8KU=,iv:EprUHNtU5w7569ADMOxw+izDAL22A5OrB12T9iyHxKU=,tag:kRvyUEZwd/RttKdFOY2bJQ==,type:str] mac: ENC[AES256_GCM,data:hDmqObrtfoVkQqz8JPkqlyXMbiuyBophjdZNLvTFrZw3pAVNCuzsH4zxFBOaxJttkzLc65DWDHDeEIBY5YZam1GLFFXUQ5E3Dxno7hnyzOoM2ipgDTOacI0gbKJAWgGUF3LNDdqVoREA9LC91LoNUJoNmzpTSFtuLb7ORuwCrH4=,iv:8+W3n1Cr6woEiPU9ECaMYM64HNmFHr2AIw6UohCJi00=,tag:7drkZiPAUHaEx5PagXA9JQ==,type:str]
pgp: []
unencrypted_suffix: _unencrypted unencrypted_suffix: _unencrypted
version: 3.9.4 version: 3.11.0

View File

@@ -0,0 +1,5 @@
{ ... }:
{
programs.adb.enable = true;
users.users.drew.extraGroups = [ "adbusers" ];
}

View File

@@ -3,13 +3,18 @@
virtualisation = { virtualisation = {
docker = { docker = {
enable = true; enable = true;
rootless = { # rootless = {
enable = true; # enable = true;
setSocketVariable = true; # setSocketVariable = true;
}; # };
}; };
# Do not use Podman in Wayland/Hyperland. It will crash the session. Very annoying.
# podman.enable = true;
}; };
hardware.nvidia-container-toolkit.enable = true;
home-manager.users.drew = home-manager.users.drew =
{ ... }: { ... }:
{ {

View File

@@ -0,0 +1,12 @@
{ pkgs, ... }:
{
services.flatpak.enable = true;
systemd.services.flatpak-repo = {
wantedBy = [ "multi-user.target" ];
path = [ pkgs.flatpak ];
script = ''
flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
'';
};
}

View File

@@ -0,0 +1,50 @@
{ config, lib, ... }:
{
options =
with lib;
with types;
{
graphics = {
enable = mkEnableOption "graphics support";
driverChannel = mkOption {
type = str;
# Default to production because I often want new features, but I don't want bleeding edge.
default = "production";
description = "Driver channel to use (in order of oldest to newest): stable, beta, latest";
};
};
};
config = lib.mkIf config.graphics.enable {
# Graphics settings
hardware.graphics = {
enable = true;
enable32Bit = true;
};
services.xserver.videoDrivers = [ "nvidia" ];
hardware.nvidia = {
package = config.boot.kernelPackages.nvidiaPackages."${config.graphics.driverChannel}";
modesetting.enable = true;
# Nvidia power management. Experimental, and can cause sleep/suspend to fail.
# Enable this if you have graphical corruption issues or application crashes after waking
# up from sleep. This fixes it by saving the entire VRAM memory to /tmp/ instead
# of just the bare essentials.
powerManagement.enable = true;
# Fine-grained power management for PRIME. Turns off GPU when not in use.
# Experimental and only works on modern Nvidia GPUs (Turing or newer).
# Requires offload to be enabled.
# powerManagement.finegrained = false;
# Use the open-source driver?
open = false;
# Enable the nvidia-settings menu?
nvidiaSettings = true;
};
};
}

View File

@@ -0,0 +1,21 @@
{ pkgs, ... }:
{
services.printing = {
enable = true;
drivers = [ pkgs.brlaser ];
};
hardware.printers = {
ensurePrinters = [
{
name = "Brother_HL-L2370DW_series";
location = "Home";
deviceUri = "dnssd://Brother%20HL-L2370DW%20series._ipp._tcp.local/?uuid=e3248000-80ce-11db-8000-3c2af4f28c38";
model = "drv:///brlaser.drv/brl2370d.ppd";
ppdOptions = {
PageSize = "Letter";
};
}
];
ensureDefaultPrinter = "Brother_HL-L2370DW_series";
};
}

View File

@@ -0,0 +1,127 @@
{ config, lib, ... }:
{
options =
with lib;
with types;
{
virtualisation.web-containers = {
enable = mkEnableOption "web containers";
containers = mkOption {
type = lazyAttrsOf (submodule {
options =
let
strOpt = mkOption { type = str; };
intOpt = mkOption { type = int; };
boolOpt = mkOption {
type = bool;
default = false;
};
strList = mkOption {
type = listOf str;
default = [ ];
};
attrOpt = mkOption {
type = attrsOf str;
default = { };
};
in
{
image = strOpt;
hostname = strOpt;
port = intOpt;
homepageOpts = attrOpt;
dependsOn = strList;
domain = strOpt;
volumes = strList;
environment = attrOpt;
environmentFiles = strList;
public = boolOpt;
user = mkOption {
type = nullOr str;
default = null;
};
extraOptions = strList;
oauthProxy = boolOpt;
extraLabels = attrOpt;
};
});
default = { };
description = "";
};
};
};
config = {
virtualisation.oci-containers.containers = lib.mkIf config.virtualisation.web-containers.enable (
let
hostRule = host: domain: "Host(`${host}.${domain}`)";
localNet = "192.168.0.0/16";
dockerNet = "10.88.0.0/16";
localNetRule = "(ClientIP(`${localNet}`) || ClientIP(`${dockerNet}`))";
localHostRule = host: domain: "${localNetRule} && ${hostRule host domain}";
mkContainer =
key:
{
image,
hostname,
port,
homepageOpts,
dependsOn,
domain,
volumes,
environment,
environmentFiles,
public,
user,
extraOptions,
oauthProxy,
extraLabels,
}:
let
fqn = "${hostname}.${domain}";
serviceName = builtins.replaceStrings [ "." ] [ "-" ] fqn;
routerRule = if public then hostRule hostname domain else localHostRule hostname domain;
homepageLabels =
if homepageOpts == { } then
{ }
else
{
"homepage.group" = "${homepageOpts.group}";
"homepage.name" = "${homepageOpts.name}";
"homepage.icon" = "${homepageOpts.icon}";
"homepage.href" = "https://${fqn}";
"homepage.description" = "${homepageOpts.description}";
};
oauthLabels =
if oauthProxy then
{ "traefik.http.routers.${serviceName}.middlewares" = "oidc-auth@file"; }
else
{ };
in
{
inherit
image
dependsOn
volumes
environment
environmentFiles
user
extraOptions
;
autoStart = true;
labels = {
"traefik.enable" = "true";
"traefik.http.routers.${serviceName}.rule" = "${routerRule}";
"traefik.http.routers.${serviceName}.service" = "${serviceName}";
"traefik.http.routers.${serviceName}.entrypoints" = "web,websecure";
"traefik.http.services.${serviceName}.loadbalancer.server.port" = "${toString port}";
}
// oauthLabels
// homepageLabels
// extraLabels;
};
in
builtins.mapAttrs mkContainer config.virtualisation.web-containers.containers
);
};
}

View File

@@ -9,6 +9,8 @@
../../features/gc.nix ../../features/gc.nix
../../features/gui.nix ../../features/gui.nix
../../features/container-dev.nix ../../features/container-dev.nix
../../features/android-dev.nix
../../features/flatpak.nix
]; ];
nixpkgs.config.allowUnfree = true; nixpkgs.config.allowUnfree = true;

View File

@@ -8,6 +8,8 @@ in
{ {
imports = imports =
map (x: ../../../home-manager + x) [ map (x: ../../../home-manager + x) [
"/features/astronomy.nix"
"/features/chat.nix"
"/features/development/development.nix" "/features/development/development.nix"
"/features/development/docker.nix" "/features/development/docker.nix"
"/features/development/haskell.nix" "/features/development/haskell.nix"
@@ -16,8 +18,10 @@ in
"/features/development/vscode.nix" "/features/development/vscode.nix"
"/features/eww" "/features/eww"
"/features/gaming.nix" "/features/gaming.nix"
"/features/image-editing.nix"
"/features/linux-desktop.nix" "/features/linux-desktop.nix"
"/features/notes.nix" "/features/notes.nix"
"/features/3d-printing.nix"
] ]
++ [ ++ [
(import ../../../home-manager/features/wallpaper.nix monitors) (import ../../../home-manager/features/wallpaper.nix monitors)
@@ -39,13 +43,15 @@ in
userEmail = "drew.haven@gmail.com"; userEmail = "drew.haven@gmail.com";
}; };
# Set up eww here because it's based on the monitor configuration
wayland.windowManager.hyprland.settings = { wayland.windowManager.hyprland.settings = {
exec-once = [ exec-once = [
"sleep 2 && eww open-many primary-statusbar secondary-statusbar launcher" # Set up eww here because it's based on the monitor configuration
"sleep 2 && eww open-many primary-statusbar secondary-statusbar"
# Set DP-2 as the primary monitor, otherwise it defaults to DP-1 because it's first in the list.
"xrandr --output DP-2 --primary"
]; ];
windowrulev2 = [ windowrulev2 = [
# Rofi doesn't center properly when I have the two asymetric monitors # Rofi doesn't center properly when I have the two asymetric monitors, so we need hyprland to manage it.
"center, class:Rofi" "center, class:Rofi"
]; ];
}; };

View File

@@ -13,38 +13,46 @@
(modulesPath + "/installer/scan/not-detected.nix") (modulesPath + "/installer/scan/not-detected.nix")
]; ];
# Bootloader. # Bootloader.
boot.loader.systemd-boot.enable = true; boot = {
boot.loader.efi.canTouchEfiVariables = true; loader = {
systemd-boot.enable = true;
efi.canTouchEfiVariables = true;
};
boot.initrd.availableKernelModules = [ initrd = {
"xhci_pci" availableKernelModules = [
"ahci" "xhci_pci"
"nvme" "ahci"
"usbhid" "nvme"
"usb_storage" "usbhid"
"sd_mod" "usb_storage"
]; "sd_mod"
boot.initrd.kernelModules = [ ]; ];
boot.kernelModules = [ "kvm-intel" ]; kernelModules = [ ];
boot.extraModulePackages = [ ]; };
kernelModules = [ "kvm-intel" ];
fileSystems."/" = { extraModulePackages = [ ];
device = "/dev/disk/by-uuid/343c0ac5-3973-49b3-964a-6ad90c36b89c";
fsType = "ext4";
}; };
fileSystems."/boot" = { fileSystems = {
device = "/dev/disk/by-uuid/5F99-043D"; "/" = {
fsType = "vfat"; device = "/dev/disk/by-uuid/343c0ac5-3973-49b3-964a-6ad90c36b89c";
options = [ fsType = "ext4";
"fmask=0077" };
"dmask=0077"
];
};
fileSystems."/home" = { "/boot" = {
device = "/dev/disk/by-uuid/28f4fb41-9414-4504-a767-c2e8bf5eb2c8"; device = "/dev/disk/by-uuid/5F99-043D";
fsType = "ext4"; fsType = "vfat";
options = [
"fmask=0077"
"dmask=0077"
];
};
"/home" = {
device = "/dev/disk/by-uuid/28f4fb41-9414-4504-a767-c2e8bf5eb2c8";
fsType = "ext4";
};
}; };
swapDevices = [ ]; swapDevices = [ ];
@@ -58,37 +66,47 @@
# networking.interfaces.wlo1.useDHCP = lib.mkDefault true; # networking.interfaces.wlo1.useDHCP = lib.mkDefault true;
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
# Graphics settings
hardware.graphics = {
enable = true;
enable32Bit = true;
};
services.xserver.videoDrivers = [ "nvidia" ]; services.xserver.videoDrivers = [ "nvidia" ];
hardware.nvidia = { hardware = {
package = config.boot.kernelPackages.nvidiaPackages.beta; cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
graphics = {
enable = true;
enable32Bit = true;
};
nvidia = {
# Other options include:
# stable - Current stable
# production - Same as stable
# latest - Bleeding edge
# beta - latest beta
#
# See https://nixos.wiki/wiki/Nvidia
#
# Current versions can be found in https://github.com/NixOS/nixpkgs/blob/nixos-unstable/pkgs/os-specific/linux/nvidia-x11/default.nix
#
package = config.boot.kernelPackages.nvidiaPackages.beta;
modesetting.enable = true; modesetting.enable = true;
# Nvidia power management. Experimental, and can cause sleep/suspend to fail. # Nvidia power management. Experimental, and can cause sleep/suspend to fail.
# Enable this if you have graphical corruption issues or application crashes after waking # Enable this if you have graphical corruption issues or application crashes after waking
# up from sleep. This fixes it by saving the entire VRAM memory to /tmp/ instead # up from sleep. This fixes it by saving the entire VRAM memory to /tmp/ instead
# of just the bare essentials. # of just the bare essentials.
powerManagement.enable = true; powerManagement.enable = true;
# Fine-grained power management for PRIME. Turns off GPU when not in use. # Fine-grained power management for PRIME. Turns off GPU when not in use.
# Experimental and only works on modern Nvidia GPUs (Turing or newer). # Experimental and only works on modern Nvidia GPUs (Turing or newer).
# Requires offload to be enabled. # Requires offload to be enabled.
# powerManagement.finegrained = false; # powerManagement.finegrained = false;
# Use the open-source driver? # Use the open-source driver?
open = false; open = false;
# Enable the nvidia-settings menu? # Enable the nvidia-settings menu?
nvidiaSettings = true; nvidiaSettings = true;
};
}; };
# Add a udev rule to prevent the mouse from waking the system. Note that it # Add a udev rule to prevent the mouse from waking the system. Note that it

View File

@@ -1,4 +1,4 @@
{ config, pkgs, ... }: { pkgs, ... }:
{ {
imports = [ imports = [
./vars.nix ./vars.nix
@@ -16,9 +16,6 @@
boot.loader.systemd-boot.enable = true; boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true; boot.loader.efi.canTouchEfiVariables = true;
# Set the kernel to be compatible with ZFS
boot.kernelPackages = config.boot.zfs.package.latestCompatibleLinuxPackages;
networking.hostName = "mcp"; # Define your hostname. networking.hostName = "mcp"; # Define your hostname.
networking.hostId = "5e292f2d"; # Define a host ID for ZFS with `head -c 8 /etc/machine-id` networking.hostId = "5e292f2d"; # Define a host ID for ZFS with `head -c 8 /etc/machine-id`
# networking.wireless.enable = true; # Enables wireless support via wpa_supplicant. # networking.wireless.enable = true; # Enables wireless support via wpa_supplicant.
@@ -62,6 +59,7 @@
"networkmanager" "networkmanager"
"wheel" "wheel"
"docker-registry" "docker-registry"
"docker"
]; ];
shell = pkgs.zsh; shell = pkgs.zsh;
# Enable linger so that systemd services run for this user are started and # Enable linger so that systemd services run for this user are started and
@@ -96,7 +94,8 @@
port = 5000; port = 5000;
openFirewall = true; openFirewall = true;
# Bind to the podman network so Traefik can route to it. # Bind to the podman network so Traefik can route to it.
# Note that it may fail to start if this network has not been created yet. # Note that it may fail to start if this network has not been created yet,
# so this has to be manually restarted when the system boots.
listenAddress = "10.88.0.1"; listenAddress = "10.88.0.1";
}; };
@@ -107,8 +106,10 @@
enabledCollectors = [ "systemd" ]; enabledCollectors = [ "systemd" ];
port = 9002; port = 9002;
# Open the firewall, but only listen on the internal address # Open the firewall, but only listen on the internal address
# TODO: Add some form authentication
openFirewall = true; openFirewall = true;
# Bind to the podman network so Traefik can route to it.
# Note that it may fail to start if this network has not been created yet,
# so this has to be manually restarted when the system boots.
listenAddress = "10.88.0.1"; listenAddress = "10.88.0.1";
}; };
}; };

View File

@@ -1,332 +1,183 @@
# Started from https://nixos.wiki/wiki/Podman {
{ config, pkgs, ... }: config,
pkgs,
lib,
...
}:
{ {
# Additional configuration # Additional configuration
imports = [ imports = [
./containers/havenisms.com
./containers/blazestar.net
# Docker containers
./containers/dm-companion.nix
./containers/freshrss.nix ./containers/freshrss.nix
./containers/gitea.nix ./containers/gitea.nix
./containers/goatcounter.nix
./containers/grafana.nix ./containers/grafana.nix
./containers/jobhunt.nix # ./containers/jobhunt.nix
./containers/mariadb.nix ./containers/mariadb.nix
./containers/media-system.nix
./containers/nextcloud.nix ./containers/nextcloud.nix
./containers/offen.nix # ./containers/offen.nix
./containers/pocket-id.nix ./containers/pocket-id.nix
./containers/prometheus.nix ./containers/prometheus.nix
./containers/public-homepage.nix ./containers/public-homepage.nix
./containers/searxng.nix ./containers/searxng.nix
./containers/shared-postgres.nix ./containers/shared-postgres.nix
./containers/synapse.nix # ./containers/timetagger.nix
./containers/traefik.nix ./containers/traefik.nix
./containers/users.nix ./containers/users.nix
# NixOS Containers
./static-site-hooks.nix
]; ];
# Enable common container config files in /etc/containers options.local = with lib; {
virtualisation.containers.enable = true; container-backend = mkOption {
virtualisation = { type = with types; uniq str;
podman = { default = "docker";
enable = true; example = "docker";
description = "Which backend to use for containers: docker or podman";
# Create a `docker` alias for podman, to use it as a drop-in replacement };
dockerCompat = true; container-socket = mkOption {
type = with types; uniq str;
# Required for containers under podman-compose to be able to talk to each other. default = "/var/run/docker.sock";
defaultNetwork.settings.dns_enabled = true; example = "/var/run/docker.sock";
description = "Path to the container management deamon's socket.";
extraPackages = [ pkgs.zfs ];
}; };
}; };
# Useful other development tools config = {
environment.systemPackages = with pkgs; [ # local = {
dive # look into docker image layers # container-backend = "docker";
podman-tui # status of containers in the terminal # container-socket = "/var/run/docker.sock";
docker-compose # start group of containers for dev # };
#podman-compose # start group of containers for dev local = {
]; container-backend = "podman";
container-socket = "/var/run/podman/podman.sock";
};
virtualisation.oci-containers.backend = "podman"; # Enable common container config files in /etc/containers
virtualisation.oci-containers.containers =
let virtualisation = {
inherit (import ./containers/lib.nix config) containers.enable = true;
hostRuleHavenisms oci-containers.backend = config.local.container-backend;
localHostRuleHavenisms
havenisms; docker = lib.mkIf (config.local.container-backend == "docker") {
in enable = true;
{ # Enable rootless so that I can run containers as other users for security.
jellyfin = { rootless = {
image = "lscr.io/linuxserver/jellyfin"; enable = true;
autoStart = true; # Set this to make the default DOCKER_HOST be the rootless version for normal users.
extraOptions = [ setSocketVariable = true;
"--device=/dev/dri:/dev/dri" daemon = {
"-l=traefik.enable=true" settings = {
"-l=traefik.http.routers.jellyfin.rule=${hostRuleHavenisms "jellyfin"}" default-address-pools = [
"-l=traefik.http.services.jellyfin.loadbalancer.server.port=8096" {
"-l=homepage.group=Apps" base = "10.88.0.0/16";
"-l=homepage.name=Jellyfin" size = 24;
"-l=homepage.icon=jellyfin.svg" }
"-l=homepage.href=https://jellyfin.${havenisms}" ];
"-l=homepage.description=Media player" };
"-l=homepage.widget.type=jellyfin" };
"-l=homepage.widget.key={{HOMEPAGE_FILE_JELLYFIN_KEY}}" };
"-l=homepage.widget.url=http://jellyfin:8096" daemon = {
"-l=homepage.widget.enableBlocks=true" settings = {
]; default-address-pools = [
volumes = [ {
"/tank/media/collection:/data" base = "10.88.0.0/16";
"/tank/config/jellyfin:/config" size = 24;
]; }
# environment = { ];
# TZ = vars.timeZone; };
# PUID = "994";
# UMASK = "002";
# GUID = "993";
# };
};
deluge = {
image = "linuxserver/deluge:latest";
autoStart = true;
dependsOn = [
"gluetun"
];
extraOptions = [
"--network=container:gluetun"
"-l=homepage.group=Arr"
"-l=homepage.name=Deluge"
"-l=homepage.icon=deluge.svg"
"-l=homepage.href=https://deluge.${havenisms}"
"-l=homepage.description=Torrent client"
"-l=homepage.widget.type=deluge"
"-l=homepage.widget.password={{HOMEPAGE_FILE_DELUGE_PASSWORD}}"
"-l=homepage.widget.url=http://gluetun:8112"
];
volumes = [
"/tank/media:/data"
"/tank/config/deluge:/config"
];
};
qbittorrent = {
image = "linuxserver/qbittorrent:latest";
autoStart = true;
dependsOn = [
"gluetun"
];
extraOptions = [
"--network=container:gluetun"
"-l=homepage.group=Arr"
"-l=homepage.name=qBitTorrent"
"-l=homepage.icon=qbittorrent.svg"
"-l=homepage.href=https://torrents.${havenisms}"
"-l=homepage.description=Torrent client"
"-l=homepage.widget.type=qbittorrent"
"-l=homepage.widget.url=http://torrents.${havenisms}"
];
volumes = [
"/tank/media/Downloads:/downloads"
"/tank/config/qbittorrent:/config"
];
environment = {
PUID = "911";
PGID = "911";
UMASK = "002";
}; };
}; };
gluetun = { podman = lib.mkIf (config.local.container-backend == "podman") {
image = "qmcgaw/gluetun:latest"; enable = true;
autoStart = true;
extraOptions = [ # Create a `docker` alias for podman, to use it as a drop-in replacement
# add network admin capability. dockerCompat = true;
"--cap-add=NET_ADMIN"
"--device=/dev/net/tun:/dev/net/tun" # Required for containers under podman-compose to be able to talk to each other.
"-l=traefik.enable=true" defaultNetwork.settings.dns_enabled = true;
"-l=traefik.http.routers.torrents.rule=${localHostRuleHavenisms "torrents"}"
"-l=traefik.http.routers.torrents.service=torrents" extraPackages = [ pkgs.zfs ];
"-l=traefik.http.services.torrents.loadbalancer.server.port=8080"
"-l=homepage.group=Infra"
"-l=homepage.name=GlueTun"
"-l=homepage.icon=gluetun.svg"
"-l=homepage.href=https://torrents.${havenisms}"
"-l=homepage.description=VPN killswitch"
"-l=homepage.widget.type=gluetun"
"-l=homepage.widget.url=http://gluetun:8000"
];
ports = [
"127.0.0.1:8083:8000"
];
environmentFiles = [
"/tank/config/gluetun/vpn.env"
];
environment = {
VPN_SERVICE_PROVIDER = "protonvpn";
UMASK = "002";
};
};
prowlarr = {
image = "lscr.io/linuxserver/prowlarr";
autoStart = true;
extraOptions = [
"-l=traefik.enable=true"
"-l=traefik.http.routers.prowlarr.rule=${localHostRuleHavenisms "prowlarr"}"
"-l=traefik.http.services.prowlarr.loadbalancer.server.port=9696"
"-l=homepage.group=Arr"
"-l=homepage.name=Prowlarr"
"-l=homepage.icon=prowlarr.svg"
"-l=homepage.href=https://prowlarr.${havenisms}"
"-l=homepage.description=Torrent indexer"
];
volumes = [
"/tank/config/prowlarr:/config"
];
environment = {
UMASK = "002";
};
};
# Currently broken and doesn't work. :(
# flaresolverr = {
# image = "ghcr.io/flaresolverr/flaresolverr:latest";
# autoStart = true;
# extraOptions = [
# "-l=homepage.group=Infra"
# "-l=homepage.name=FlareSolverr"
# "-l=homepage.icon=flaresolverr.svg"
# "-l=homepage.href=https://flaresolverr.${domain}"
# "-l=homepage.description=Cloudflare bypass"
# ];
# volumes = [
# "/tank/config/flaresolverr:/config"
# ];
# environment = {
# UMASK = "002";
# };
# };
radarr = {
image = "lscr.io/linuxserver/radarr";
autoStart = true;
extraOptions = [
"-l=traefik.enable=true"
"-l=traefik.http.routers.radarr.rule=${localHostRuleHavenisms "radarr"}"
"-l=traefik.http.services.radarr.loadbalancer.server.port=7878"
"-l=homepage.group=Arr"
"-l=homepage.name=Radarr"
"-l=homepage.icon=radarr.svg"
"-l=homepage.href=https://radarr.${havenisms}"
"-l=homepage.description=Movie acquisition"
"-l=homepage.widget.type=radarr"
"-l=homepage.widget.url=http://radarr:7878"
"-l=homepage.widget.key={{HOMEPAGE_FILE_RADARR_KEY}}"
];
volumes = [
"/tank/media:/data"
"/tank/config/radarr:/config"
];
environment = {
UMASK = "002";
};
};
sonarr = {
image = "lscr.io/linuxserver/sonarr";
autoStart = true;
extraOptions = [
"-l=traefik.enable=true"
"-l=traefik.http.routers.sonarr.rule=${localHostRuleHavenisms "sonarr"}"
"-l=traefik.http.services.sonarr.loadbalancer.server.port=8989"
"-l=homepage.group=Arr"
"-l=homepage.name=Sonarr"
"-l=homepage.icon=sonarr.svg"
"-l=homepage.href=https://sonarr.${havenisms}"
"-l=homepage.description=Show acquisition"
"-l=homepage.widget.type=sonarr"
"-l=homepage.widget.url=http://sonarr:8989"
"-l=homepage.widget.key={{HOMEPAGE_FILE_SONARR_KEY}}"
];
volumes = [
"/tank/media:/data"
"/tank/config/sonarr:/config"
];
environment = {
UMASK = "002";
};
};
readarr = {
# The Linuxserver version of this image doesn't have a latest tag. Odd.
image = "lscr.io/linuxserver/readarr:develop";
autoStart = true;
extraOptions = [
"-l=traefik.enable=true"
"-l=traefik.http.routers.readarr.rule=${localHostRuleHavenisms "readarr"}"
"-l=traefik.http.services.readarr.loadbalancer.server.port=8787"
"-l=homepage.group=Arr"
"-l=homepage.name=Readarr"
"-l=homepage.icon=readarr.svg"
"-l=homepage.href=https://readarr.${havenisms}"
"-l=homepage.description=E-book acquisition"
"-l=homepage.widget.type=readarr"
"-l=homepage.widget.url=http://readarr.havenisms.com:8787"
"-l=homepage.widget.key={{HOMEPAGE_FILE_READARR_KEY}}"
];
volumes = [
"/tank/media:/data"
"/tank/config/readarr:/config"
];
environment = {
UMASK = "002";
};
};
homepage = {
image = "ghcr.io/gethomepage/homepage:latest";
autoStart = true;
extraOptions = [
"-l=traefik.enable=true"
"-l=traefik.http.routers.homepage.rule=${localHostRuleHavenisms "start"}"
"-l=traefik.http.services.homepage.loadbalancer.server.port=3000"
];
volumes = [
"/tank/config/homepage:/app/config"
"/tank/secrets/deluge.pass:/app/config/secrets/deluge.pass"
"/tank/secrets/jellyfin.key:/app/config/secrets/jellyfin.key"
"/tank/secrets/radarr.key:/app/config/secrets/radarr.key"
"/tank/secrets/sonarr.key:/app/config/secrets/sonarr.key"
"/var/run/podman/podman.sock:/var/run/docker.sock:ro"
];
environment = {
HOMEPAGE_FILE_JELLYFIN_KEY = "/app/config/secrets/jellyfin.key";
HOMEPAGE_FILE_RADARR_KEY = "/app/config/secrets/radarr.key";
HOMEPAGE_FILE_SONARR_KEY = "/app/config/secrets/sonarr.key";
HOMEPAGE_FILE_READARR_KEY = "/app/config/secrets/readarr.key";
HOMEPAGE_FILE_DELUGE_PASSWORD = "/app/config/secrets/deluge.pass";
};
};
scrutiny = {
image = "ghcr.io/analogj/scrutiny:master-omnibus";
autoStart = true;
extraOptions = [
"-l=traefik.enable=true"
"-l=traefik.http.routers.scrutiny.rule=${localHostRuleHavenisms "scrutiny"}"
"-l=traefik.http.services.scrutiny.loadbalancer.server.port=8080"
"-l=homepage.group=Infra"
"-l=homepage.name=Scrutiny"
"-l=homepage.icon=scrutiny-light.png"
"-l=homepage.href=https://scrutiny.${havenisms}"
"-l=homepage.description=S.M.A.R.T. monitoring"
"-l=homepage.widget.type=scrutiny"
"-l=homepage.widget.url=http://scrutiny:8080"
"--cap-add=SYS_RAWIO"
"--device=/dev/sda:/dev/sda"
"--device=/dev/sdb:/dev/sdb"
"--device=/dev/sdc:/dev/sdc"
"--device=/dev/sdd:/dev/sdd"
];
volumes = [
"/run/udev:/run/udev:ro"
"/tank/config/scrutiny/config:/opt/scrutiny/config"
"/tank/config/scrutiny/influxdb:/opt/scrutiny/influxdb"
];
};
valkey = {
image = "docker.io/valkey/valkey:7-alpine";
autoStart = true;
volumes = [
"/tank/config/valkey:/usr/local/etc/valkey"
];
}; };
}; };
# Useful other development tools
environment.systemPackages = with pkgs; [
dive # look into docker image layers
docker-compose # start group of containers for dev
];
virtualisation.oci-containers.containers =
let
inherit (import ./containers/lib.nix config)
localHostRuleHavenisms
havenisms
;
in
{
homepage = {
image = "ghcr.io/gethomepage/homepage:latest";
autoStart = true;
extraOptions = [
"-l=traefik.enable=true"
"-l=traefik.http.routers.homepage.rule=${localHostRuleHavenisms "start"}"
"-l=traefik.http.services.homepage.loadbalancer.server.port=3000"
];
volumes = [
"/tank/config/homepage:/app/config"
"/tank/secrets/deluge.pass:/app/config/secrets/deluge.pass"
"/tank/secrets/jellyfin.key:/app/config/secrets/jellyfin.key"
"/tank/secrets/radarr.key:/app/config/secrets/radarr.key"
"/tank/secrets/sonarr.key:/app/config/secrets/sonarr.key"
"${config.local.container-socket}:/var/run/docker.sock:ro"
];
environment = {
HOMEPAGE_FILE_JELLYFIN_KEY = "/app/config/secrets/jellyfin.key";
HOMEPAGE_FILE_RADARR_KEY = "/app/config/secrets/radarr.key";
HOMEPAGE_FILE_SONARR_KEY = "/app/config/secrets/sonarr.key";
HOMEPAGE_FILE_READARR_KEY = "/app/config/secrets/readarr.key";
HOMEPAGE_FILE_DELUGE_PASSWORD = "/app/config/secrets/deluge.pass";
};
};
scrutiny = {
image = "ghcr.io/analogj/scrutiny:master-omnibus";
autoStart = true;
extraOptions = [
"-l=traefik.enable=true"
"-l=traefik.http.routers.scrutiny.rule=${localHostRuleHavenisms "scrutiny"}"
"-l=traefik.http.services.scrutiny.loadbalancer.server.port=8080"
"-l=homepage.group=Infra"
"-l=homepage.name=Scrutiny"
"-l=homepage.icon=scrutiny-light.png"
"-l=homepage.href=https://scrutiny.${havenisms}"
"-l=homepage.description=S.M.A.R.T. monitoring"
"-l=homepage.widget.type=scrutiny"
"-l=homepage.widget.url=http://scrutiny:8080"
"--cap-add=SYS_RAWIO"
"--device=/dev/disk/by-id/wwn-0x5000cca26fca1aed:/dev/disk/by-id/wwn-0x5000cca26fca1aed"
"--device=/dev/disk/by-id/wwn-0x5000cca26fef696c:/dev/disk/by-id/wwm-0x5000cca26fef696c"
"--device=/dev/disk/by-id/wwn-0x5000cca270db1d0e:/dev/disk/by-id/wwn-0x5000cca270db1d0e"
# "--device=/dev/sdd:/dev/sdd" Removing this one while the disk is down
];
volumes = [
"/run/udev:/run/udev:ro"
"/tank/config/scrutiny/config:/opt/scrutiny/config"
"/tank/config/scrutiny/influxdb:/opt/scrutiny/influxdb"
];
};
valkey = {
image = "docker.io/valkey/valkey:7-alpine";
autoStart = true;
volumes = [
"/tank/config/valkey:/usr/local/etc/valkey"
];
};
};
};
} }

View File

@@ -0,0 +1,104 @@
{ config, ... }:
let
inherit (import ../lib.nix config) mkContainer blazestar;
matrixHost = "matrix";
serviceName = "matrix-blazestar-net";
dbPath = "/var/lib/matrix";
port = 8448;
in
{
sops.secrets = {
"matrix/blazestar-registration-token" = {
restartUnits = [ "${config.local.container-backend}-matrix-blazestar-net.service" ];
};
};
sops.templates."matrix-blazestar-net.env".content = ''
TUWUNEL_REGISTRATION_TOKEN=${config.sops.placeholder."matrix/blazestar-registration-token"}
'';
# This isn't using any of my usual helpers because I wanted to set a custom
# serviceName in Traefik that is different from the hostname to avoid
# conflicts with the havenisms.com server.
virtualisation.oci-containers.containers."${serviceName}" = {
# The 1.1.0 version has an issue with the compression being incorrectly tagged.
# See: https://github.com/matrix-construct/tuwunel/issues/79
image = "ghcr.io/matrix-construct/tuwunel:v1.0.0-release-all-x86_64-linux-gnu";
autoStart = true;
volumes = [
"matrix-blazestar-net-db:${dbPath}"
];
environment = {
TUWUNEL_PORT = toString port;
TUWUNEL_ADDRESS = "0.0.0.0"; # It'll bind to localhost by default with Podman
TUWUNEL_SERVER_NAME = "blazestar.net";
TUWUNEL_ALLOW_REGISTRATION = "true";
TUWUNEL_ALLOW_CHECK_FOR_UPDATES = "true";
TUWUNEL_ALLOW_FEDERATION = "true";
TUWUNEL_DATABASE_BACKEND = "rocksdb";
TUWUNEL_DATABASE_PATH = dbPath;
TUWUNEL_WELL_KNOWN = ''
{
client=https://${matrixHost}.blazestar.net,
server=${matrixHost}.blazestar.net:443
}
'';
TUWUNEL_TRUSTED_SERVERS = ''["matrix.org", "chat.havenisms.com"]'';
};
environmentFiles = [
config.sops.templates."matrix-blazestar-net.env".path
];
labels = {
"traefik.enable" = "true";
"traefik.http.routers.${serviceName}.rule" = "Host(`${matrixHost}.${blazestar}`)";
"traefik.http.services.${serviceName}.loadbalancer.server.port" = "${toString port}";
# Redirect well-known requests to this host.
"traefik.http.routers.${matrixHost}-blazestar-net-well-known.rule" =
"Host(`blazestar.net`) && PathPrefix(`/.well-known/matrix`)";
"traefik.http.routers.${matrixHost}-blazestar-net-well-known.service" = serviceName;
};
};
# virtualisation.oci-containers.containers.matrix-blazestar-net =
# mkContainer {
# image = "registry.gitlab.com/famedly/conduit/matrix-conduit:latest";
# hostName = hostname;
# domain = blazestar;
# port = port;
# ports = [
# "8449:6167"
# ];
# volumes = [
# "chat-blazestar-net-db:${dbPath}"
# ];
# environment = {
# CONDUIT_PORT = "6167";
# CONDUIT_SERVER_NAME = "blazestar.net";
# CONDUIT_ALLOW_REGISTRATION = "true";
# CONDUIT_DATABASE_BACKEND = "rocksdb";
# CONDUIT_DATABASE_PATH = dbPath;
# CONDUIT_ALLOW_CHECK_FOR_UPDATES = "true";
# CONDUIT_ALLOW_FEDERATION = "true";
# CONDUIT_MAX_REQUEST_SIZE = "20000000";
# CONDUIT_TRUSTED_SERVERS = "[\"matrix.org\"]";
# CONDUIT_MAX_CONCURRENT_REQUESTS = "100";
# CONDUIT_WELL_KNOWN_CLIENT = "https://${hostname}.blazestar.net";
# CONDUIT_WELL_KNOWN_SERVER = "${hostname}.blazestar.net:443";
# CONDUIT_CONFIG = ""; # Ignore the config file
# };
# extraLabels = {
# "traefik.http.routers.${hostname}-blazestar-net-well-known.rule" =
# "Host(`blazestar.net`) && PathPrefix(`/.well-known`)";
# "traefik.http.routers.${hostname}-blazestar-net-well-known.service" = "${hostname}-blazestar-net";
# };
# };
virtualisation.oci-containers.containers.chat = mkContainer {
image = "vectorim/element-web:latest";
hostName = "chat";
port = 8080;
domain = blazestar;
environment = {
ELEMENT_WEB_PORT = "8080";
};
};
}

View File

@@ -0,0 +1,7 @@
{ ... }:
{
imports = [
./chat.nix
./uptime.nix
];
}

View File

@@ -0,0 +1,15 @@
{ config, ... }:
let
inherit (import ../lib.nix config) blazestar;
in
{
virtualisation.web-containers.containers.uptime = {
image = "louislam/uptime-kuma:1";
hostname = "uptime";
domain = blazestar;
port = 3001;
volumes = [
"uptime-kuma:/app/data"
];
};
}

View File

@@ -1,7 +1,8 @@
{ config, ... }: { config, ... }:
let let
inherit (import ./lib.nix config) mkContainer mkMariaDbContainer havenisms; inherit (import ./lib.nix config) mkContainer mkMariaDbContainer havenisms;
in { in
{
imports = [ imports = [
(mkMariaDbContainer { (mkMariaDbContainer {
name = "bookstack"; name = "bookstack";
@@ -14,12 +15,12 @@ in {
sops.secrets = { sops.secrets = {
bookstack_app_key = { bookstack_app_key = {
restartUnits = [ "podman-bookstack.service" ]; restartUnits = [ "${config.local.container-backend}-bookstack.service" ];
mode = "0400"; mode = "0400";
owner = config.users.users.bookstack.name; owner = config.users.users.bookstack.name;
}; };
bookstack_db = { bookstack_db = {
restartUnits = [ "podman-bookstack-mariadb.service" ]; restartUnits = [ "${config.local.container-backend}-bookstack-mariadb.service" ];
mode = "0400"; mode = "0400";
owner = config.users.users.bookstack.name; owner = config.users.users.bookstack.name;
}; };

View File

@@ -0,0 +1,108 @@
{ config, pkgs, ... }:
let
inherit (import ./lib.nix config) mkContainer localHostRule terakoda;
nginxConf = pkgs.writeText "dm-companion-nginx.conf" ''
user nginx;
worker_processes auto;
# error.log is symlinked to /dev/stderr
error_log /var/log/nginx/error.log notice;
pid /run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
# access.log is symlinked to /dev/stdout
access_log /var/log/nginx/access.log main;
sendfile on;
keepalive_timeout 65;
gzip on;
server {
listen 80;
server_name dm.blazestar.net;
root /usr/share/nginx/html;
# X-Frame-Options is to prevent from clickJacking attack
add_header X-Frame-Options SAMEORIGIN;
# disable content-type sniffing on some browsers.
add_header X-Content-Type-Options nosniff;
# This header enables the Cross-site scripting (XSS) filter
add_header X-XSS-Protection "1; mode=block";
add_header Referrer-Policy "no-referrer-when-downgrade";
# Enables response header of "Vary: Accept-Encoding"
# This lets the cache have different entries depending on the encoding, e.g. compression
gzip_vary on;
# Serve static files separately.
location ~ ^/(robots.txt|manifest.json) {
expires modified 1y;
add_header Cache-Control "public";
access_log off;
}
location / {
try_files $uri $uri/ /index.html;
index index.html;
expires -1;
add_header Cache-Control "no-store, no-cache, must-revalidate";
}
}
}
'';
in
{
virtualisation.oci-containers.containers = {
dm-companion-pocketbase =
let
hostName = "dm-pocketbase";
in
mkContainer {
inherit hostName;
image = "docker.havenisms.com/lazy-dm/pocketbase";
domain = terakoda;
port = 8080;
volumes = [
"/tank/web/dm.terakoda.com/pb_data:/pb/pb_data"
"/tank/web/dm.terakoda.com/pb_migrations:/pb/pb_migrations:ro"
];
environment = { };
extraLabels = {
"traefik.http.routers.${hostName}-api.rule" =
"PathPrefix(`/api`) && ${localHostRule "dm" terakoda}";
"traefik.http.routers.${hostName}-api.service" = "${hostName}";
};
};
dm-companion = mkContainer {
image = "nginx:alpine";
hostName = "dm";
domain = terakoda;
port = 80;
dependsOn = [
"dm-companion-pocketbase"
];
volumes = [
"/tank/web/dm.terakoda.com/deployed:/usr/share/nginx/html:ro"
"${nginxConf}:/etc/nginx/nginx.conf:ro"
];
};
};
}

View File

@@ -1,7 +1,8 @@
{ config, ... }: { config, ... }:
let let
inherit (import ./lib.nix config) mkContainer mkPostgresContainer terakoda; inherit (import ./lib.nix config) mkContainer mkPostgresContainer terakoda;
in { in
{
imports = [ imports = [
(mkPostgresContainer { (mkPostgresContainer {
name = "focalboard"; name = "focalboard";
@@ -14,21 +15,26 @@ in {
sops.secrets = { sops.secrets = {
"focalboard/database" = { "focalboard/database" = {
restartUnits = [ "podman-focalboard.service" "podman-focalboard-postgres.service" ]; restartUnits = [
"${config.local.container-backend}-focalboard.service"
"${config.local.container-backend}-focalboard-postgres.service"
];
mode = "0400"; mode = "0400";
owner = config.users.users.focalboard.name; owner = config.users.users.focalboard.name;
}; };
}; };
sops.templates."focalboard-config.json" = { sops.templates."focalboard-config.json" = {
restartUnits = [ "podman-focalboard.service" ]; restartUnits = [ "${config.local.container-backend}-focalboard.service" ];
owner = config.users.users.focalboard.name; owner = config.users.users.focalboard.name;
content = builtins.toJSON { content = builtins.toJSON {
# Defaults from https://github.com/mattermost-community/focalboard/blob/main/config.json # Defaults from https://github.com/mattermost-community/focalboard/blob/main/config.json
"serverRoot" = "https://focalboard.terakoda.com"; "serverRoot" = "https://focalboard.terakoda.com";
"port" = 8000; "port" = 8000;
"dbtype" = "postgres"; "dbtype" = "postgres";
"dbconfig" = "postgres://focalboard:${config.sops.placeholder."focalboard/database"}@focalboard-postgres/focalboard?sslmode=disable&connect_timeout=10"; "dbconfig" = "postgres://focalboard:${
config.sops.placeholder."focalboard/database"
}@focalboard-postgres/focalboard?sslmode=disable&connect_timeout=10";
"useSSL" = true; "useSSL" = true;
"prometheus_address" = ":9092"; "prometheus_address" = ":9092";
"session_expire_time" = 2592000; "session_expire_time" = 2592000;

View File

@@ -5,10 +5,10 @@ in
{ {
sops.secrets = { sops.secrets = {
"gitea/db_password" = { "gitea/db_password" = {
restartUnits = [ "podman-gitea.service" ]; restartUnits = [ "${config.local.container-backend}-gitea.service" ];
}; };
"gitea/registration_token" = { "gitea/registration_token" = {
restartUnits = [ "podman-gitea-runner.service" ]; restartUnits = [ "${config.local.container-backend}-gitea-runner.service" ];
}; };
}; };
@@ -66,7 +66,7 @@ in
]; ];
volumes = [ volumes = [
# The runner will spawn new containers to run the actions # The runner will spawn new containers to run the actions
"/var/run/podman/podman.sock:/var/run/docker.sock:ro" "${config.local.container-socket}:/var/run/docker.sock:ro"
]; ];
}; };
} }

View File

@@ -0,0 +1,58 @@
{ config, ... }:
let
inherit (import ./lib.nix config)
terakoda
blazestar
hostRule
;
in
{
imports = [
../../../features/web-containers.nix
];
virtualisation.web-containers = {
enable = true;
containers = {
goatcounter-terakoda = {
image = "arp242/goatcounter";
hostname = "goatcounter";
domain = terakoda;
public = true;
port = 8080;
volumes = [
"goatcounter-data:/home/goatcounter/goatcounter-data"
];
extraLabels = {
# "traefik.http.middlewares.strip-analytics.stripprefix.prefixes" = "/analytics";
# "traefik.http.routers.www-terakoda-com-goatcounter.middlewares" = "strip-analytics";
# Host the script on www.terakoda.com so that it is easy to fetch
"traefik.http.routers.www-terakoda-com-goatcounter.rule" =
"PathPrefix(`/count`) && ${hostRule "www" terakoda}";
"traefik.http.routers.www-terakoda-com-goatcounter.entrypoints" = "websecure";
"traefik.http.routers.www-terakoda-com-goatcounter.service" = "goatcounter-terakoda-com";
};
};
goatcounter-blazestar = {
image = "arp242/goatcounter";
hostname = "goatcounter";
domain = blazestar;
public = true;
port = 8080;
volumes = [
"goatcounter-data-blazestar:/home/goatcounter/goatcounter-data"
];
extraLabels = {
# "traefik.http.middlewares.strip-analytics.stripprefix.prefixes" = "/analytics";
# "traefik.http.routers.www-blazestar-net-goatcounter.middlewares" = "strip-analytics";
# Host the script on www.blazestar.net so that it is easy to fetch
"traefik.http.routers.www-blazestar-net-goatcounter.rule" =
"PathPrefix(`/count`) && ${hostRule "www" blazestar}";
"traefik.http.routers.www-blazestar-net-goatcounter.entrypoints" = "websecure";
"traefik.http.routers.www-blazestar-net-goatcounter.service" = "goatcounter-blazestar-net@docker";
};
};
};
};
}

View File

@@ -0,0 +1,84 @@
{ config, ... }:
let
inherit (import ../lib.nix config) hostRule havenisms;
syncRule = "(PathPrefix(`/client/`) || PathPrefix(`/_matrix/client/unstable/org.matrix.msc3575/sync`))";
wellKnownRule = "(Host(`havenisms.com`) || Host(`chat.havenisms.com`)) && PathPrefix(`/.well-known`)";
in
{
sops.secrets = {
"matrix/syncv3/db-password" = {
restartUnits = [ "${config.local.container-backend}-matrix-sliding-sync.service" ];
};
"matrix/syncv3/secret" = {
restartUnits = [ "${config.local.container-backend}-matrix-sliding-sync.service" ];
};
};
sops.templates."matrix-sliding-sync.env".content = ''
SYNCV3_SERVER=http://synapse:8008
SYNCV3_DB=postgres://syncv3:${
config.sops.placeholder."matrix/syncv3/db-password"
}@db:5432/syncv3?sslmode=disable
SYNCV3_SECRET=${config.sops.placeholder."matrix/syncv3/secret"}
SYNCV3_BINDADDR=:8009
'';
virtualisation.oci-containers.containers = {
synapse = {
image = "docker.io/matrixdotorg/synapse:latest";
autoStart = true;
dependsOn = [
"db"
];
volumes = [
"/tank/config/synapse/data:/data"
];
extraOptions = [
"-l=traefik.enable=true"
"-l=traefik.http.routers.synapse.rule=${hostRule "chat" havenisms} && !(${syncRule} || ${wellKnownRule})"
"-l=traefik.http.routers.synapse.service=synapse"
"-l=traefik.http.services.synapse.loadbalancer.server.port=8008"
# Federation forwarding
"-l=traefik.http.routers.synapse-federation.rule=${hostRule "chat" havenisms}"
"-l=traefik.http.routers.synapse-federation.service=synapse-federation"
"-l=traefik.http.routers.synapse-federation.entrypoints=matrix-federation"
"-l=traefik.http.services.synapse-federation.loadbalancer.server.port=8448"
];
};
matrix-sliding-sync = {
image = "ghcr.io/matrix-org/sliding-sync:latest";
dependsOn = [
"db"
"synapse"
];
environmentFiles = [
config.sops.templates."matrix-sliding-sync.env".path
];
extraOptions = [
"-l=traefik.enable=true"
"-l=traefik.http.routers.syncv3.rule=${hostRule "chat" havenisms} && ${syncRule}"
"-l=traefik.http.services.syncv3.loadbalancer.server.port=8009"
];
};
# This server helps to serve the .well-known files that are required by clients to find the sync server.
matrix-well-known = {
image = "nginx";
dependsOn = [ "synapse" ];
volumes = [
"/tank/config/synapse/static-files:/usr/share/nginx/html:ro"
];
extraOptions = [
"-l=traefik.enable=true"
"-l=traefik.http.middlewares.strip-well-known.stripprefix.prefixes=/.well-known"
"-l=traefik.http.routers.matrix-well-known.rule=${wellKnownRule}"
"-l=traefik.http.routers.matrix-well-known.middlewares=strip-well-known"
"-l=traefik.http.services.matrix-well-known.loadbalancer.server.port=80"
];
};
};
}

View File

@@ -0,0 +1,11 @@
{ ... }:
{
imports = [
./chat.nix
# Currently disabled because it doesn't start up properly
# ./immich.nix
./storyden.nix
./tandoor.nix
./wallabag.nix
];
}

View File

@@ -0,0 +1,73 @@
{ config, ... }:
let
inherit (import ../lib.nix config) havenisms mkPostgresContainer;
in
{
imports = [
(mkPostgresContainer {
# Immich wants a custom build of postgres with the vectors extensions.
image = "ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:c44be5f2871c59362966d71eab4268170eb6f5653c0e6170184e72b38ffdf107";
name = "immich";
directory = "/tank/immich/db";
uid = config.users.users.immich.uid;
gid = config.users.groups.immich.gid;
passwordSecret = "immich/database";
})
];
sops.secrets = {
"immich/database" = {
restartUnits = [
"${config.local.container-backend}-immich-db.service"
];
mode = "0400";
owner = config.users.users.immich.name;
};
};
sops.templates."immich.env" = {
restartUnits = [ "${config.local.container-backend}-immich.service" ];
owner = config.users.users.immich.name;
content = ''
DB_HOSTNAME=immich-postgres
DB_PASSWORD=${config.sops.placeholder."immich/database"}
DB_USERNAME=immich
DB_DATABASE_NAME=immich
REDIS_HOSTNAME=immich-redis
IMMICH_LOG_LEVEL=verbose
'';
};
virtualisation.web-containers.containers.immich = {
image = "ghcr.io/immich-app/immich-server:release";
hostname = "immich";
domain = havenisms;
port = 2283;
volumes = [
"/tank/photos/immich:/data"
"/etc/localtime:/etc/localtime:ro"
];
dependsOn = [
"immich-redis"
"immich-postgres"
];
environmentFiles = [
"${config.sops.templates."immich.env".path}"
];
};
virtualisation.oci-containers.containers = {
"immich-redis" = {
image = "docker.io/valkey/valkey";
};
"immich-machine-learning" = {
image = "ghcr.io/immich-app/immich-machine-learning:release";
volumes = [
"model-cache:/cache"
];
environmentFiles = [
"${config.sops.templates."immich.env".path}"
];
};
};
}

View File

@@ -0,0 +1,17 @@
{ config, ... }:
let
inherit (import ../lib.nix config) havenisms;
in
{
virtualisation.web-containers.containers.storyden = {
image = "ghcr.io/southclaws/storyden";
port = 8000;
hostname = "storyden";
domain = havenisms;
environment = {
PUBLIC_WEB_ADDRESS = "https://storyden.${havenisms}";
PUBLIC_API_ADDRESS = "https://storyden.${havenisms}";
};
};
}

View File

@@ -0,0 +1,30 @@
{ config, ... }:
let
inherit (import ../lib.nix config) havenisms;
in
{
sops.secrets = {
"tandoor/secret_key" = {
restartUnits = [ "${config.local.container-backend}-tandoor.service" ];
};
};
sops.templates."tandoor.env".content = ''
SECRET_KEY="${config.sops.placeholder."tandoor/secret_key"}"
DB_ENGINE=django.db.backends.sqlite3
'';
virtualisation.web-containers.containers.tandoor = {
image = "vabene1111/recipes";
hostname = "recipes";
domain = havenisms;
port = 80;
volumes = [
"/tank/tandoor-recipes/mediafiles:/opt/recipes/mediafiles"
"/tank/tandoor-recipes/staticfiles:/opt/recipes/staticfiles"
];
environmentFiles = [
config.sops.templates."tandoor.env".path
];
};
}

View File

@@ -0,0 +1,19 @@
{ config, ... }:
let
inherit (import ../lib.nix config) havenisms;
in
{
virtualisation.web-containers.containers.wallabag = {
image = "wallabag/wallabag";
hostname = "wallabag";
domain = havenisms;
port = 80;
volumes = [
"wallabag-data:/var/www/wallabag/data"
"wallabag-images:/var/www/wallabag/web/assets/images"
];
environment = {
SYMFONY__ENV__DOMAIN_NAME = "https://wallabag.${havenisms}";
};
};
}

View File

@@ -21,41 +21,63 @@ in
havenisms havenisms
blazestar blazestar
terakoda terakoda
terakoda_net; terakoda_net
;
mkContainer = { mkContainer =
image, {
hostName, image,
port, hostName,
homepageOpts ? {}, port,
dependsOn ? [], homepageOpts ? { },
domain ? havenisms, dependsOn ? [ ],
ports ? [], domain ? havenisms,
volumes ? [], ports ? [ ],
environment ? {}, volumes ? [ ],
environmentFiles ? [], environment ? { },
public ? false, environmentFiles ? [ ],
user ? null, public ? false,
extraOptions ? [], user ? null,
}: extraOptions ? [ ],
oauthProxy ? false,
extraLabels ? { },
}:
let let
routerRule = if public then hostRule hostName domain else localHostRule hostName domain; routerRule = if public then hostRule hostName domain else localHostRule hostName domain;
homepageLabels = if homepageOpts == {} then {} else { homepageLabels =
"homepage.group" = "${homepageOpts.group}"; if homepageOpts == { } then
"homepage.name" = "${homepageOpts.name}"; { }
"homepage.icon" = "${homepageOpts.icon}"; else
"homepage.href" = "https://${hostName}.${domain}"; {
"homepage.description" = "${homepageOpts.description}"; "homepage.group" = "${homepageOpts.group}";
}; "homepage.name" = "${homepageOpts.name}";
"homepage.icon" = "${homepageOpts.icon}";
"homepage.href" = "https://${hostName}.${domain}";
"homepage.description" = "${homepageOpts.description}";
};
oauthLabels =
if oauthProxy then { "traefik.http.routers.${hostName}.middlewares" = "oidc-auth@file"; } else { };
in in
{ {
inherit image dependsOn volumes environment environmentFiles ports user extraOptions; inherit
image
dependsOn
volumes
environment
environmentFiles
ports
user
extraOptions
;
autoStart = true; autoStart = true;
labels = { labels = {
"traefik.enable" = "true"; "traefik.enable" = "true";
"traefik.http.routers.${hostName}.rule" = "${routerRule}"; "traefik.http.routers.${hostName}.rule" = "${routerRule}";
"traefik.http.services.${hostName}.loadbalancer.server.port" = "${toString port}"; "traefik.http.services.${hostName}.loadbalancer.server.port" = "${toString port}";
} // homepageLabels; }
// oauthLabels
// homepageLabels
// extraLabels;
}; };
# Creates a MariaDB container for a specific app. It should be safe to give # Creates a MariaDB container for a specific app. It should be safe to give
@@ -64,60 +86,65 @@ in
# user. # user.
# #
# Note that this returns a _module_ so that it can be imported and provide many different config values. # Note that this returns a _module_ so that it can be imported and provide many different config values.
mkMariaDbContainer = { mkMariaDbContainer =
name, {
uid, name,
gid, uid,
passwordSecret, gid,
directory, passwordSecret,
}: directory,
{ config, ... }: }:
{ { config, ... }:
virtualisation.oci-containers.containers."${name}-mariadb" = { {
image = "lscr.io/linuxserver/mariadb:latest"; virtualisation.oci-containers.containers."${name}-mariadb" = {
autoStart = true; image = "lscr.io/linuxserver/mariadb:latest";
ports = [ "3306:3306" ]; autoStart = true;
volumes = [ ports = [ "3306:3306" ];
"${directory}:/config" volumes = [
"${config.sops.secrets.mariadb_root_password.path}:/run/secrets/mariadb_root_password" "${directory}:/config"
"${config.sops.secrets."${passwordSecret}".path}:/run/secrets/mariadb_password" "${config.sops.secrets.mariadb_root_password.path}:/run/secrets/mariadb_root_password"
]; "${config.sops.secrets."${passwordSecret}".path}:/run/secrets/mariadb_password"
environment = { ];
PUID = "${toString uid}"; environment = {
PGID = "${toString gid}"; PUID = "${toString uid}";
MYSQL_USER = name; PGID = "${toString gid}";
MYSQL_DATABASE = name; MYSQL_USER = name;
FILE__MYSQL_ROOT_PASSWORD = "/run/secrets/mariadb_root_password"; MYSQL_DATABASE = name;
FILE__MYSQL_PASSWORD = "/run/secrets/mariadb_password"; FILE__MYSQL_ROOT_PASSWORD = "/run/secrets/mariadb_root_password";
FILE__MYSQL_PASSWORD = "/run/secrets/mariadb_password";
};
}; };
}; };
};
mkPostgresContainer = { mkPostgresContainer =
name, {
uid, name,
gid, uid,
passwordSecret, gid,
directory, passwordSecret,
containerName ? "${name}-postgres", directory,
databaseName ? name, containerName ? "${name}-postgres",
username ? name, databaseName ? name,
}: { config, ... }: { username ? name,
virtualisation.oci-containers.containers."${containerName}" = { image ? "postgres",
image = "postgres"; }:
autoStart = true; { config, ... }:
volumes = [ {
# Note that data must be mounted at this location to persist. virtualisation.oci-containers.containers."${containerName}" = {
# See https://github.com/docker-library/docs/blob/master/postgres/README.md#pgdata inherit image;
"${directory}:/var/lib/postgresql/data" autoStart = true;
"${config.sops.secrets."${passwordSecret}".path}:/run/secrets/postgres_password" volumes = [
]; # Note that data must be mounted at this location to persist.
user = "${toString uid}:${toString gid}"; # See https://github.com/docker-library/docs/blob/master/postgres/README.md#pgdata
environment = { "${directory}:/var/lib/postgresql/data"
POSTGRES_USER = username; "${config.sops.secrets."${passwordSecret}".path}:/run/secrets/postgres_password"
POSTGRES_DB = databaseName; ];
POSTGRES_PASSWORD_FILE = "/run/secrets/postgres_password"; user = "${toString uid}:${toString gid}";
environment = {
POSTGRES_USER = username;
POSTGRES_DB = databaseName;
POSTGRES_PASSWORD_FILE = "/run/secrets/postgres_password";
};
}; };
}; };
};
} }

View File

@@ -1,8 +1,8 @@
# Common config for all mariadb containers # Common config for all mariadb containers
{ ... }: { config, ... }:
{ {
sops.secrets."mariadb_root_password" = { sops.secrets."mariadb_root_password" = {
restartUnits = [ "podman-mariadb.service" ]; restartUnits = [ "${config.local.container-backend}-mariadb.service" ];
mode = "0440"; mode = "0440";
group = "mariadb"; group = "mariadb";
}; };

View File

@@ -0,0 +1,280 @@
{ config, ... }:
let
inherit (import ./lib.nix config)
hostRuleHavenisms
localHostRuleHavenisms
havenisms
mkContainer
;
gluetun_env = "gluetun-proton-vpn-wireguard.env";
in
{
sops.secrets = {
"protonvpn/private_key" = {
restartUnits = [ "${config.local.container-backend}-gluetun.service" ];
};
};
# Example Wireguard config file:
# # Key for MCP Wireguard
# # Bouncing = 13
# # NetShield = 1
# # Moderate NAT = off
# # NAT-PMP (Port Forwarding) = on
# # VPN Accelerator = on
# PrivateKey = ${config.sops.placeholder."protonvpn/private_key"}
# Address = 10.2.0.2/32
# DNS = 10.2.0.1
#
# [Peer]
# # US-CA#906
# PublicKey = 2xvxhMK0AalXOMq6Dh0QMVJ0Cl3WQTmWT5tdeb8SpR0=
# AllowedIPs = 0.0.0.0/0, ::/0
# Endpoint = 79.127.185.166:51820
#
# PersistentKeepalive = 25
sops.templates.${gluetun_env}.content = ''
VPN_SERVICE_PROVIDER=protonvpn
VPN_TYPE=wireguard
WIREGUARD_PRIVATE_KEY="${config.sops.placeholder."protonvpn/private_key"}"
SERVER_COUNTRIES="United States,United Kingdom,Netherlands,Switzerland,Sweden"
VPN_PORT_FORWARDING=on
'';
virtualisation.oci-containers.containers = {
jellyfin = {
image = "lscr.io/linuxserver/jellyfin:10.11.6";
autoStart = true;
extraOptions = [
"--device=/dev/dri:/dev/dri"
"-l=traefik.enable=true"
"-l=traefik.http.routers.jellyfin.rule=${hostRuleHavenisms "jellyfin"}"
"-l=traefik.http.services.jellyfin.loadbalancer.server.port=8096"
"-l=homepage.group=Apps"
"-l=homepage.name=Jellyfin"
"-l=homepage.icon=jellyfin.svg"
"-l=homepage.href=https://jellyfin.${havenisms}"
"-l=homepage.description=Media player"
"-l=homepage.widget.type=jellyfin"
"-l=homepage.widget.key={{HOMEPAGE_FILE_JELLYFIN_KEY}}"
"-l=homepage.widget.url=http://jellyfin:8096"
"-l=homepage.widget.enableBlocks=true"
];
volumes = [
"/tank/media/collection:/data"
"/tank/config/jellyfin:/config"
];
# environment = {
# TZ = vars.timeZone;
# PUID = "994";
# UMASK = "002";
# GUID = "993";
# };
};
deluge = {
image = "lscr.io/linuxserver/deluge:latest";
autoStart = true;
dependsOn = [
"gluetun"
];
extraOptions = [
"--network=container:gluetun"
"-l=homepage.group=Arr"
"-l=homepage.name=Deluge"
"-l=homepage.icon=deluge.svg"
"-l=homepage.href=https://deluge.${havenisms}"
"-l=homepage.description=Torrent client"
"-l=homepage.widget.type=deluge"
"-l=homepage.widget.password={{HOMEPAGE_FILE_DELUGE_PASSWORD}}"
"-l=homepage.widget.url=http://gluetun:8112"
];
volumes = [
"/tank/media:/data"
"/tank/config/deluge:/config"
];
};
qbittorrent = {
image = "lscr.io/linuxserver/qbittorrent:latest";
autoStart = true;
dependsOn = [
"gluetun"
];
extraOptions = [
"--network=container:gluetun"
"-l=homepage.group=Arr"
"-l=homepage.name=qBitTorrent"
"-l=homepage.icon=qbittorrent.svg"
"-l=homepage.href=https://torrents.${havenisms}"
"-l=homepage.description=Torrent client"
"-l=homepage.widget.type=qbittorrent"
"-l=homepage.widget.url=http://torrents.${havenisms}"
];
volumes = [
"/tank/media/Downloads:/downloads"
"/tank/config/qbittorrent:/config"
];
environment = {
PUID = "911";
PGID = "911";
UMASK = "002";
};
};
gluetun = {
image = "qmcgaw/gluetun:latest";
autoStart = true;
extraOptions = [
# add network admin capability.
"--cap-add=NET_ADMIN"
"--device=/dev/net/tun:/dev/net/tun"
"-l=traefik.enable=true"
"-l=traefik.http.routers.torrents.rule=${localHostRuleHavenisms "torrents"}"
"-l=traefik.http.routers.torrents.service=torrents"
"-l=traefik.http.services.torrents.loadbalancer.server.port=8080"
"-l=homepage.group=Infra"
"-l=homepage.name=GlueTun"
"-l=homepage.icon=gluetun.svg"
"-l=homepage.href=https://torrents.${havenisms}"
"-l=homepage.description=VPN killswitch"
"-l=homepage.widget.type=gluetun"
"-l=homepage.widget.url=http://gluetun:8000"
];
ports = [
"127.0.0.1:8083:8000"
];
environmentFiles = [
config.sops.templates.${gluetun_env}.path
];
};
prowlarr = {
image = "lscr.io/linuxserver/prowlarr";
autoStart = true;
extraOptions = [
"-l=traefik.enable=true"
"-l=traefik.http.routers.prowlarr.rule=${localHostRuleHavenisms "prowlarr"}"
"-l=traefik.http.services.prowlarr.loadbalancer.server.port=9696"
"-l=homepage.group=Arr"
"-l=homepage.name=Prowlarr"
"-l=homepage.icon=prowlarr.svg"
"-l=homepage.href=https://prowlarr.${havenisms}"
"-l=homepage.description=Torrent indexer"
];
volumes = [
"/tank/config/prowlarr:/config"
];
environment = {
UMASK = "002";
};
};
# Currently broken and doesn't work. :(
# flaresolverr = {
# image = "ghcr.io/flaresolverr/flaresolverr:latest";
# autoStart = true;
# extraOptions = [
# "-l=homepage.group=Infra"
# "-l=homepage.name=FlareSolverr"
# "-l=homepage.icon=flaresolverr.svg"
# "-l=homepage.href=https://flaresolverr.${domain}"
# "-l=homepage.description=Cloudflare bypass"
# ];
# volumes = [
# "/tank/config/flaresolverr:/config"
# ];
# environment = {
# UMASK = "002";
# };
# };
radarr = {
image = "lscr.io/linuxserver/radarr";
autoStart = true;
extraOptions = [
"-l=traefik.enable=true"
"-l=traefik.http.routers.radarr.rule=${localHostRuleHavenisms "radarr"}"
"-l=traefik.http.services.radarr.loadbalancer.server.port=7878"
"-l=homepage.group=Arr"
"-l=homepage.name=Radarr"
"-l=homepage.icon=radarr.svg"
"-l=homepage.href=https://radarr.${havenisms}"
"-l=homepage.description=Movie acquisition"
"-l=homepage.widget.type=radarr"
"-l=homepage.widget.url=http://radarr:7878"
"-l=homepage.widget.key={{HOMEPAGE_FILE_RADARR_KEY}}"
];
volumes = [
"/tank/media:/data"
"/tank/config/radarr:/config"
];
environment = {
UMASK = "002";
};
};
sonarr = {
image = "lscr.io/linuxserver/sonarr";
autoStart = true;
extraOptions = [
"-l=traefik.enable=true"
"-l=traefik.http.routers.sonarr.rule=${localHostRuleHavenisms "sonarr"}"
"-l=traefik.http.services.sonarr.loadbalancer.server.port=8989"
"-l=homepage.group=Arr"
"-l=homepage.name=Sonarr"
"-l=homepage.icon=sonarr.svg"
"-l=homepage.href=https://sonarr.${havenisms}"
"-l=homepage.description=Show acquisition"
"-l=homepage.widget.type=sonarr"
"-l=homepage.widget.url=http://sonarr:8989"
"-l=homepage.widget.key={{HOMEPAGE_FILE_SONARR_KEY}}"
];
volumes = [
"/tank/media:/data"
"/tank/config/sonarr:/config"
];
environment = {
UMASK = "002";
};
};
readarr = {
# The Linuxserver version of this image doesn't have a latest tag. Odd.
image = "lscr.io/linuxserver/readarr:develop";
autoStart = true;
extraOptions = [
"-l=traefik.enable=true"
"-l=traefik.http.routers.readarr.rule=${localHostRuleHavenisms "readarr"}"
"-l=traefik.http.services.readarr.loadbalancer.server.port=8787"
"-l=homepage.group=Arr"
"-l=homepage.name=Readarr"
"-l=homepage.icon=readarr.svg"
"-l=homepage.href=https://readarr.${havenisms}"
"-l=homepage.description=E-book acquisition"
"-l=homepage.widget.type=readarr"
"-l=homepage.widget.url=http://readarr.havenisms.com:8787"
"-l=homepage.widget.key={{HOMEPAGE_FILE_READARR_KEY}}"
];
volumes = [
"/tank/media:/data"
"/tank/config/readarr:/config"
];
environment = {
UMASK = "002";
};
};
bazarr = mkContainer {
# The Linuxserver version of this image doesn't have a latest tag. Odd.
image = "lscr.io/linuxserver/bazarr:latest";
port = 6767;
hostName = "bazarr";
homepageOpts = {
group = "Arr";
name = "Bazarr";
icon = "bazarr.svg";
description = "Subtitles";
};
volumes = [
"/tank/media:/data"
"/tank/config/bazarr:/config"
];
environment = {
UMASK = "002";
};
};
};
}

View File

@@ -4,7 +4,7 @@ let
in in
{ {
virtualisation.oci-containers.containers.nextcloud = { virtualisation.oci-containers.containers.nextcloud = {
image = "docker.io/library/nextcloud:latest"; image = "docker.io/library/nextcloud:31";
extraOptions = [ extraOptions = [
"-l=traefik.enable=true" "-l=traefik.enable=true"
"-l=traefik.http.routers.nextcloud.rule=${hostRule "cloud" havenisms}" "-l=traefik.http.routers.nextcloud.rule=${hostRule "cloud" havenisms}"
@@ -20,13 +20,5 @@ in
volumes = [ volumes = [
"/tank/nextcloud:/var/www/html" "/tank/nextcloud:/var/www/html"
]; ];
environment = {
POSTGRES_HOST = "db";
POSTGRES_DB = "nextcloud";
POSTGRES_USER = "nextcloud";
# TODO: Secrets
POSTGRES_PASSWORD = "nextcloud123";
};
}; };
} }

View File

@@ -0,0 +1,50 @@
{ config, ... }:
let
inherit (import ./lib.nix config) mkContainer blazestar havenisms;
in
{
sops.secrets = {
"oauth2-proxy/cookie-secret" = {
restartUnits = [ "${config.local.container-backend}-oauth2-proxy.service" ];
mode = "0400";
};
"oauth2-proxy/client-secret" = {
restartUnits = [ "${config.local.container-backend}-oauth2-proxy.service" ];
mode = "0400";
};
};
sops.templates."oauth2-proxy.env".content = ''
OAUTH2_PROXY_HTTP_ADDRESS='0.0.0.0:4180'
OAUTH2_PROXY_COOKIE_SECRET='${config.sops.placehoder."oauth2-proxy/cookie-secret"}'
OAUTH2_PROXY_COOKIE_DOMAINS='.${blazestar} .${havenisms}'
OAUTH2_PROXY_WHITELIST_DOMAINS='.${blazestar} .${havenisms}'
OAUTH2_PROXY_PROVIDER='oidc'
OAUTH2_PROXY_CLIENT_ID='oauth2-proxy'
OAUTH2_PROXY_CLIENT_SECRET='${config.sops.placehoder."oauth2-proxy/client-secret"}'
OAUTH2_PROXY_EMAIL_DOMAINS='*'
OAUTH2_PROXY_OIDC_ISSUER_URL='https://auth.${blazestar}/realms/master'
OAUTH2_PROXY_REDIRECT_URL='https://auth.${blazestar}/oauth2/callback'
OAUTH2_PROXY_COOKIE_CSRF_PER_REQUEST=true
OAUTH2_PROXY_COOKIE_CSRF_EXPIRE='5m'
OAUTH2_PROXY_CUSTOM_TEMPLATES_DIR="/templates"
OAUTH2_PROXY_REVERSE_PROXY=true
'';
virtualisation.oci-containers.containers.oauth2-proxy = mkContainer {
image = "quay.io/oauth2-proxy/oauth2-proxy";
hostName = "oauth";
domain = blazestar;
port = "4180";
homepageOpts = {
group = "Infra";
name = "OAuth2-Proxy";
icon = "oauth2-proxy.png";
description = "An OAuth2 Reverse Proxy";
};
volumes = [
];
environment = {
};
};
}

View File

@@ -2,18 +2,19 @@
let let
inherit (import ./lib.nix config) mkContainer havenisms; inherit (import ./lib.nix config) mkContainer havenisms;
hostName = "projects"; hostName = "projects";
in { in
{
sops.secrets = { sops.secrets = {
"openproject/secret-key-base" = { "openproject/secret-key-base" = {
restartUnits = [ "podman-openproject.service" ]; restartUnits = [ "${config.local.container-backend}-openproject.service" ];
mode = "0400"; mode = "0400";
owner = config.users.users.bookstack.name; owner = config.users.users.bookstack.name;
}; };
}; };
sops.templates."openproject.env" = { sops.templates."openproject.env" = {
restartUnits = [ "podman-openproject.service" ]; restartUnits = [ "${config.local.container-backend}-openproject.service" ];
content = '' content = ''
OPENPROJECT_SECRET_KEY_BASE=${config.sops.placeholder."openproject/secret-key-base"} OPENPROJECT_SECRET_KEY_BASE=${config.sops.placeholder."openproject/secret-key-base"}
OPENPROJECT_HOST__NAME=${hostName}.${havenisms} OPENPROJECT_HOST__NAME=${hostName}.${havenisms}

View File

@@ -1,30 +1,64 @@
# Static websites # Static websites
{ lib, config, ... }: { lib, config, ... }:
let let
inherit (import ./lib.nix config) terakoda havenisms blazestar; inherit (import ./lib.nix config)
mkStaticSite = domain: let terakoda
cleanDomain = lib.strings.stringAsChars (c: if c == "." then "-" else c) domain; havenisms
in { blazestar
"${cleanDomain}-static" = { ;
image = "nginx:alpine"; mkStaticSite =
autoStart = true; {
volumes = [ host,
"/tank/web/${domain}/public:/usr/share/nginx/html:ro" dir ? "public",
]; redirectWww ? true,
labels = { }:
"traefik.enable" = "true"; let
"traefik.http.routers.${cleanDomain}.rule" = "Host(`${domain}`) || Host(`www.${domain}`)"; cleanHost = lib.strings.stringAsChars (c: if c == "." then "-" else c) host;
"traefik.http.routers.${cleanDomain}.middlewares" = "${cleanDomain}-add-www@docker"; wwwLabels =
"traefik.http.services.${cleanDomain}.loadbalancer.server.port" = "80"; if redirectWww then
"traefik.http.middlewares.${cleanDomain}-add-www.redirectregex.regex" = "^https://${domain}/(.*)"; {
"traefik.http.middlewares.${cleanDomain}-add-www.redirectregex.replacement" = "https://www.${domain}/\${1}"; "traefik.http.routers.${cleanHost}.middlewares" = "${cleanHost}-add-www@docker";
"traefik.http.middlewares.${cleanDomain}-add-www.redirectregex.permanent" = "true"; "traefik.http.middlewares.${cleanHost}-add-www.redirectregex.regex" = "^https://${host}/(.*)";
"traefik.http.middlewares.${cleanHost}-add-www.redirectregex.replacement" =
"https://www.${host}/\${1}";
"traefik.http.middlewares.${cleanHost}-add-www.redirectregex.permanent" = "true";
}
else
{ };
in
{
"${cleanHost}-static" = {
image = "nginx:alpine";
autoStart = true;
volumes = [
"/tank/web/${host}/${dir}:/usr/share/nginx/html:ro"
];
labels = {
"traefik.enable" = "true";
"traefik.http.routers.${cleanHost}.rule" = "Host(`${host}`) || Host(`www.${host}`)";
"traefik.http.services.${cleanHost}.loadbalancer.server.port" = "80";
} // wwwLabels;
}; };
}; };
}; in
in { {
virtualisation.oci-containers.containers = virtualisation.oci-containers.containers =
mkStaticSite terakoda // mkStaticSite {
mkStaticSite havenisms // host = terakoda;
mkStaticSite blazestar; dir = "deployed";
}
// mkStaticSite {
host = blazestar;
dir = "deployed";
}
// mkStaticSite {
host = "wow.${blazestar}";
dir = "deployed";
redirectWww = false;
}
// mkStaticSite {
host = havenisms;
dir = "public";
};
} }

View File

@@ -1,60 +0,0 @@
{ config, ... }:
let inherit (import ./lib.nix config) hostRule havenisms;
syncRule = "(PathPrefix(`/client/`) || PathPrefix(`/_matrix/client/unstable/org.matrix.msc3575/sync`))";
wellKnownRule = "PathPrefix(`/.well-known`)";
in
{
virtualisation.oci-containers.containers = {
synapse = {
image = "docker.io/matrixdotorg/synapse:latest";
autoStart = true;
dependsOn = [
"db"
];
volumes = [
"/tank/config/synapse/data:/data"
];
ports = [
"8008:8008/tcp"
];
extraOptions = [
"-l=traefik.enable=true"
"-l=traefik.http.routers.synapse.rule=${hostRule "chat" havenisms} && !(${syncRule} || ${wellKnownRule})"
"-l=traefik.http.services.synapse.loadbalancer.server.port=8008"
];
};
matrix_sliding_sync = {
image = "ghcr.io/matrix-org/sliding-sync:latest";
dependsOn = ["db"];
ports = [
"8009:8009"
];
environment = {
SYNCV3_SERVER = "http://synapse:8008";
# TODO: Store password securely
SYNCV3_DB = "postgres://syncv3:TZKr3RNmVx@db:5432/syncv3?sslmode=disable";
# TODO: Store secret securely
SYNCV3_SECRET = "4917590296b90910ec31ba355af6c7731409fd5f284d24912b852c3f928fa162";
SYNCV3_BINDADDR = ":8009";
};
extraOptions = [
"-l=traefik.enable=true"
"-l=traefik.http.routers.syncv3.rule=${hostRule "chat" havenisms} && ${syncRule}"
"-l=traefik.http.services.syncv3.loadbalancer.server.port=8009"
];
};
# This server helps to serve the .well-known files that are required by clients to find the sync server.
matrix_well_known = {
image = "nginx";
ports = [ "80" ];
volumes = [
"/tank/config/synapse/static-files:/usr/share/nginx/html:ro"
];
extraOptions = [
"-l=traefik.enable=true"
"-l=traefik.http.routers.matrix-static.rule=${hostRule "chat" havenisms} && ${wellKnownRule}"
"-l=traefik.http.services.matrix-static.loadbalancer.server.port=80"
];
};
};
}

View File

@@ -0,0 +1,30 @@
{ config, ... }:
let
inherit (import ./lib.nix config) mkContainer;
in
{
virtualisation.oci-containers.containers.timetagger = mkContainer {
image = "ghcr.io/almarklein/timetagger:v24.12.2";
hostName = "time";
port = "80";
oauthProxy = true;
homepageOpts = {
group = "Apps";
name = "TimeTagger";
icon = "timetagger.png";
description = "Time tracker";
};
volumes = [
"/tank/config/timetagger:/data"
];
environment = {
TIMETAGGER_BIND = "0.0.0.0:80";
TIMETAGGER_DATADIR = "/data";
TIMETAGGER_LOG_LEVEL = "debug";
TIMETAGGER_PROXY_AUTH_ENABLED = "True";
TIMETAGGER_PROXY_AUTH_TRUSTED = "10.88.0.0/16";
TIMETAGGER_PROXY_AUTH_HEADER = "X-Remote-User";
};
};
}

View File

@@ -5,28 +5,77 @@ let
name = "traefik-config"; name = "traefik-config";
path = ./traefik; path = ./traefik;
}; };
in { in
virtualisation.oci-containers.containers.traefik = mkContainer { {
image = "traefik";
hostName = "proxy"; sops.secrets = {
port = 8080; "traefik/oauth2-client-secret" = {
domain = blazestar; restartUnits = [ "${config.local.container-backend}-traefik.service" ];
public = false; mode = "0400";
ports = [
"80:80"
"443:443"
];
volumes =
[
"/var/run/podman/podman.sock:/var/run/docker.sock:ro"
"${traefikConfigDir}:/etc/traefik"
"/tank/config/traefik/acme:/etc/traefik/acme"
];
homepageOpts = {
name = "Traefik";
icon = "traefik.svg";
group = "Infra";
description = "Reverse Proxy";
};
}; };
} "traefik/oauth2-plugin-secret" = {
restartUnits = [ "${config.local.container-backend}-traefik.service" ];
mode = "0400";
};
};
sops.templates."traefik/oauth2-config.yaml".content = ''
http:
middlewares:
oidc-auth:
plugin:
traefik-oidc-auth:
LogLevel: DEBUG
Secret: "${config.sops.placeholder."traefik/oauth2-plugin-secret"}"
# Omitting the Callback URL means it will use the current domain for the callback.
# CallbackUri: "https://oidc.blazestar.net/oidc/callback"
Provider:
Url: "https://auth.blazestar.net/"
ClientId: "3e3f7d9a-a684-4412-866c-ea7281954a9f"
ClientSecret: "${config.sops.placeholder."traefik/oauth2-client-secret"}"
TokenValidation: "IdToken"
UsePkce: false
Scopes: ["openid", "profile", "email"]
Headers:
- Name: "X-Oidc-Username"
Value: "{{`{{ .claims.preferred_username }}`}}"
- Name: "X-Oidc-Email"
Value: "{{`{{ .claims.email }}`}}"
- Name: "X-Oidc-Subject"
Value: "sub"
- Name: "Authorization"
Value: "{{`Bearer {{ .accessToken }}`}}"
- Name: "IdToken"
Value: "{{`Bearer {{ .idToken }}`}}"
'';
virtualisation.oci-containers.containers.traefik = mkContainer {
image = "traefik";
hostName = "proxy";
port = 8080;
domain = blazestar;
public = false;
ports = [
"80:80"
"443:443"
"8448:8448"
];
volumes = [
"${config.local.container-socket}:/var/run/docker.sock:ro"
# All the configs from the config directory
"${traefikConfigDir}:/etc/traefik"
# Oauth2 config containing secrets
"${config.sops.templates."traefik/oauth2-config.yaml".path}:/etc/traefik/dynamic/oauth2-config.yaml"
# Persistent storage for acme certificates
# TODO: It may be possible to just use docker storage because persistence
# is not critical when the cert can just be renewed.
"/tank/config/traefik/acme:/etc/traefik/acme"
];
homepageOpts = {
name = "Traefik";
icon = "traefik.svg";
group = "Infra";
description = "Reverse Proxy";
};
};
}

View File

@@ -13,6 +13,13 @@ entryPoints:
certResolver: letsencrypt certResolver: letsencrypt
metrics: metrics:
address: ":8082" address: ":8082"
asDefault: false
matrix-federation:
address: ":8448"
asDefault: false
http:
tls:
certResolver: letsencrypt
api: api:
insecure: true insecure: true
@@ -21,7 +28,7 @@ providers:
docker: docker:
exposedByDefault: false exposedByDefault: false
file: file:
directory: /etc/traefik/static directory: /etc/traefik/dynamic
watch: true watch: true
certificatesResolvers: certificatesResolvers:
@@ -37,3 +44,13 @@ metrics:
addEntryPointsLabels: true addEntryPointsLabels: true
addServicesLabels: true addServicesLabels: true
entryPoint: "metrics" entryPoint: "metrics"
# Plugins must be defined in static config
# Configuration of the plugin is in traefik.nix because it contains secrets.
# TODO: Convert this whole file to a template in Nix
experimental:
plugins:
traefik-oidc-auth:
moduleName: "github.com/sevensolutions/traefik-oidc-auth"
version: "v0.13.0"

View File

@@ -1,4 +1,5 @@
{ pkgs, ... }: let { pkgs, ... }:
let
systemUsers = { systemUsers = {
gitea = { gitea = {
uid = 2001; uid = 2001;
@@ -19,6 +20,7 @@
home = "/tank/web"; home = "/tank/web";
packages = [ pkgs.git ]; packages = [ pkgs.git ];
}; };
immich = 2009;
}; };
mkUser = name: value: { mkUser = name: value: {
@@ -27,25 +29,30 @@
description = "System User for ${name}"; description = "System User for ${name}";
group = "${name}"; group = "${name}";
shell = value.shell or null; shell = value.shell or null;
extraGroups = value.extraGroups or []; extraGroups = value.extraGroups or [ ];
openssh.authorizedKeys.keys = value.authorizedKeys or []; openssh.authorizedKeys.keys = value.authorizedKeys or [ ];
home = value.home or "/var/empty"; home = value.home or "/var/empty";
packages = value.packages or []; packages = value.packages or [ ];
}; };
mkGroup = name: value: let mkGroup =
# 1. Value if int name: value:
# 2. "gid" if present let
# 3. "uid" # 1. Value if int
gid = # 2. "gid" if present
if builtins.isInt value # 3. "uid"
then value gid =
else if builtins.hasAttr "gid" value if builtins.isInt value then
then value.gid value
else value.uid; else if builtins.hasAttr "gid" value then
in { value.gid
inherit gid; else
}; value.uid;
in { in
{
inherit gid;
};
in
{
users.users = builtins.mapAttrs mkUser systemUsers; users.users = builtins.mapAttrs mkUser systemUsers;
users.groups = (builtins.mapAttrs mkGroup systemUsers) // { users.groups = (builtins.mapAttrs mkGroup systemUsers) // {
# Legacy groups. # Legacy groups.

View File

@@ -6,6 +6,7 @@
../../authorized-keys.nix ../../authorized-keys.nix
inputs.sops-nix.nixosModules.sops inputs.sops-nix.nixosModules.sops
../../features/gc.nix ../../features/gc.nix
../../features/printing.nix
]; ];
nixpkgs.config.allowUnfree = true; nixpkgs.config.allowUnfree = true;

View File

@@ -0,0 +1,149 @@
{ config, pkgs, ... }:
let
gitKnownHosts = pkgs.writeText "known_hosts" ''
[git.blazestar.net]:2222 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDSikNAZDAbdQ5TA6Eg95FBM3sdPfAfghG+n56akCal8XXV/vOnXgqfeDASfXVOu+PZqCHnpGTxsym7hf2naFC0enznhS2sqahdQKKcsHvSfyQxpYFYyB2Zp8YDbnbRNGl2SbnqOajzk1SxJrJ0fFXmfrRIMnGNz+uFtIqc+T52CM051nd5Gj3f9a8xCwg7hedvSCynobsW9IOCmCc9rZ99TRd+m0kO74pUbgVqLv/+aSuW40K1uCkKgyh6PQsmkZd5GY0URwoJvLZauZLSPxl6DEU6lYz8S/hPrTP/e6fOPZsavQBYC+3Q/akoFnY+qlKgWLQy/Om6hz0EfYuuzNPRhf1jaGKjHgEri1f3OMgXcRMvjovRgbbu0JRGANmN8FMe20S4AAvbxmsQdQci+QcXZPDPbcmT3XJv8e8p4HNQyLxHyh0u9dLBE2ccTv5gdf/6iZy6WXlYEf1UAKC2lExRuKBV3lrnuyHhOj+iL09gUMYFuIyHuX2Hsw9yKZbO8J2+STNIVQfAJ0Upa2cJ33a6RlOxGiHXi4UbZTPguNgQaQdM0CuklVTynBfWr1Hfd8c8hVtT+HLz+XOU2Nrmgq90/w7g7mo5JxXHkcfBlqlXKONTkDUG3KHbwKtQNVC6l3bhpvPc32Mys6e7JeWnrb1zXojopnPvoct54qDVlwc5xQ==
'';
migratePocketbase =
with pkgs;
writeShellScript "migrate-pocketbase" ''
set -e
echo "Migrating in $(pwd) as $(id)"
${pkgs.pocketbase}/bin/pocketbase migrate up
echo "Migration complete"
'';
deployNpmApp =
with pkgs;
writeShellApplication {
name = "build-npm-app";
runtimeInputs = [
openssh
gitFull
nodejs_22
bashNonInteractive
rsync
];
text = ''
set -e
echo "Deploying in $(pwd) as $(id)"
OUTPUT_DIR="./$(date --utc --iso-8601=seconds)"
echo "Deploying into $OUTPUT_DIR"
export GIT_SSH_COMMAND='ssh -v -o "UserKnownHostsFile ${gitKnownHosts}" -i "${
config.sops.secrets."deploy-key/mcp".path
}"'
# Disable astro telemetry otherwise it will try to write to `~/.config/astro/config.json`
export ASTRO_TELEMETRY_DISABLED=1
# Fetch the repository and make sure we are reset to HEAD
git fetch origin main
git reset --hard
git checkout main
git reset --hard origin/main
# Use a local cache with --cache .npm
npm ci --cache .npm
npm run build -- --outDir "$OUTPUT_DIR"
echo "Activating $OUTPUT_DIR"
# Trailing slash on source to only copy contents, not the directory itself
rsync --archive --delete "$OUTPUT_DIR"/ deployed
echo "Deployment complete"
'';
};
in
{
sops.secrets = {
"deploy-key/mcp" = {
restartUnits = [ "webhook.service" ];
owner = config.users.users.webhook.name;
};
};
services.webhook =
let
trigger-rule = {
or = [
# There were some issues getting the payload signature validation to work.
# Switching to only accepting requests from internal IPs.
# {
# match = {
# type = "payload-hmac-sha1";
# secret = "mysecret";
# parameter = {
# source = "header";
# name = "X-Hub-Signature";
# };
# };
# }
{
match = {
type = "ip-whitelist";
ip-range = "192.168.0.0/16";
};
}
{
match = {
type = "ip-whitelist";
ip-range = "10.88.0.0/16";
};
}
];
};
in
{
enable = true;
verbose = true;
port = 9000;
openFirewall = true;
hooks = {
"deploy-terakoda-com" = {
id = "deploy-terakoda-com";
http-methods = [ "POST" ];
command-working-directory = "/tank/web/terakoda.com";
include-command-output-in-response-on-error = true;
execute-command = "${deployNpmApp}/bin/build-npm-app";
trigger-rule-mismatch-http-response-code = 400;
inherit trigger-rule;
};
"deploy-dm-terakoda-com" = {
id = "deploy-dm-terakoda-com";
http-methods = [ "POST" ];
command-working-directory = "/tank/web/dm.terakoda.com";
include-command-output-in-response-on-error = true;
execute-command = toString (
pkgs.writeShellScript "deploy-dm-terakoda-com" ''
"${deployNpmApp}/bin/build-npm-app";
"${migratePocketbase}";
''
);
trigger-rule-mismatch-http-response-code = 400;
inherit trigger-rule;
};
"deploy-blazestar-net" = {
id = "deploy-blazestar-net";
http-methods = [ "POST" ];
command-working-directory = "/tank/web/blazestar.net";
include-command-output-in-response-on-error = true;
execute-command = "${deployNpmApp}/bin/build-npm-app";
trigger-rule-mismatch-http-response-code = 400;
inherit trigger-rule;
};
"deploy-wow-blazestar-net" = {
id = "deploy-wow-blazestar-net";
http-methods = [ "POST" ];
command-working-directory = "/tank/web/wow.blazestar.net";
include-command-output-in-response-on-error = true;
execute-command = "${deployNpmApp}/bin/build-npm-app";
trigger-rule-mismatch-http-response-code = 400;
inherit trigger-rule;
};
};
};
}

View File

@@ -107,10 +107,16 @@
}; };
# Enable flakes # Enable flakes
nix.settings.experimental-features = [ nix = {
"nix-command" # Binary caches for Reflex FRP
"flakes" binaryCaches = [ "https://nixcache.reflex-frp.org" ];
]; binaryCachePublicKeys = [ "ryantrinkle.com-1:JJiAKaRv9mWgpVAz8dwewnZe0AzzEAzPkagE9SP5NWI=" ];
settings.experimental-features = [
"nix-command"
"flakes"
];
};
services.openssh.enable = true; services.openssh.enable = true;

View File

@@ -2,6 +2,7 @@
{ {
imports = imports =
map (x: ../../../home-manager + x) [ map (x: ../../../home-manager + x) [
"/features/chat.nix"
"/features/development/development.nix" "/features/development/development.nix"
"/features/development/docker.nix" "/features/development/docker.nix"
"/features/development/haskell.nix" "/features/development/haskell.nix"
@@ -9,6 +10,7 @@
"/features/development/typescript.nix" "/features/development/typescript.nix"
"/features/development/vscode.nix" "/features/development/vscode.nix"
"/features/eww" "/features/eww"
"/features/image-editing.nix"
"/features/linux-desktop.nix" "/features/linux-desktop.nix"
"/features/notes.nix" "/features/notes.nix"
] ]

View File

@@ -1,31 +1,45 @@
# Do not modify this file! It was generated by nixos-generate-config # Do not modify this file! It was generated by nixos-generate-config
# and may be overwritten by future invocations. Please make changes # and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead. # to /etc/nixos/configuration.nix instead.
{ config, lib, pkgs, modulesPath, ... }: {
config,
lib,
modulesPath,
...
}:
{ {
imports = imports = [
[ (modulesPath + "/installer/scan/not-detected.nix") (modulesPath + "/installer/scan/not-detected.nix")
]; ../../features/graphics.nix
];
# Bootloader. # Bootloader.
boot.loader.grub.enable = true; boot.loader.grub.enable = true;
boot.loader.grub.device = "/dev/sda"; boot.loader.grub.device = "/dev/sda";
boot.loader.grub.useOSProber = true; boot.loader.grub.useOSProber = true;
boot.initrd.availableKernelModules = [ "xhci_pci" "ehci_pci" "ata_piix" "ahci" "usbhid" "usb_storage" "sd_mod" ]; boot.initrd.availableKernelModules = [
"xhci_pci"
"ehci_pci"
"ata_piix"
"ahci"
"usbhid"
"usb_storage"
"sd_mod"
];
boot.initrd.kernelModules = [ ]; boot.initrd.kernelModules = [ ];
boot.kernelModules = [ "kvm-intel" ]; boot.kernelModules = [ "kvm-intel" ];
boot.extraModulePackages = [ ]; boot.extraModulePackages = [ ];
fileSystems."/" = fileSystems."/" = {
{ device = "/dev/disk/by-uuid/4981ad42-b108-4eb4-accb-b092813bd981"; device = "/dev/disk/by-uuid/4981ad42-b108-4eb4-accb-b092813bd981";
fsType = "ext4"; fsType = "ext4";
}; };
swapDevices = swapDevices = [
[ { device = "/dev/disk/by-uuid/f8868dce-6b32-45b7-bdf3-c9a34df1441d"; } { device = "/dev/disk/by-uuid/f8868dce-6b32-45b7-bdf3-c9a34df1441d"; }
]; ];
# Enables DHCP on each ethernet and wireless interface. In case of scripted networking # Enables DHCP on each ethernet and wireless interface. In case of scripted networking
# (the default) this is the recommended approach. When using systemd-networkd it's # (the default) this is the recommended approach. When using systemd-networkd it's
@@ -39,34 +53,6 @@
hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
# Graphics settings # Graphics settings
hardware.graphics = { # Using the default production drivers.
enable = true; graphics.enable = true;
enable32Bit = true;
};
services.xserver.videoDrivers = [ "nvidia" ];
hardware.nvidia = {
package = config.boot.kernelPackages.nvidiaPackages.beta;
modesetting.enable = true;
# Nvidia power management. Experimental, and can cause sleep/suspend to fail.
# Enable this if you have graphical corruption issues or application crashes after waking
# up from sleep. This fixes it by saving the entire VRAM memory to /tmp/ instead
# of just the bare essentials.
powerManagement.enable = true;
# Fine-grained power management for PRIME. Turns off GPU when not in use.
# Experimental and only works on modern Nvidia GPUs (Turing or newer).
# Requires offload to be enabled.
# powerManagement.finegrained = false;
# Use the open-source driver?
open = false;
# Enable the nvidia-settings menu?
nvidiaSettings = true;
};
} }