Compare commits

..

616 Commits

Author SHA1 Message Date
soraefir d3f549751f media and cfg 2026-06-04 21:33:55 +02:00
sora-ext edb48ace8a Update systems/sandbox/cfg.nix 2026-06-04 17:59:24 +02:00
sora-ext 215b546128 Add modules/server/containers/apps/favicon.nix 2026-06-04 17:58:00 +02:00
sora-ext 35e41fa630 Update modules/server/containers/apps/homepage.nix 2026-06-04 17:29:52 +02:00
sora-ext 210cf2dc9a Add modules/shared/syscfg/media.nix 2026-06-04 17:29:20 +02:00
sora-ext 809dd65eb0 Update modules/shared/syscfg/default.nix 2026-06-04 17:29:06 +02:00
sora-ext a8f730b4af Update modules/server/containers/default.nix 2026-06-04 17:28:40 +02:00
sora-ext 023620de83 Add modules/server/containers/data/authentik/branding.yaml 2026-06-04 17:28:23 +02:00
sora-ext eb2258a850 Update modules/server/containers/apps/nextcloud.nix 2026-06-04 17:28:04 +02:00
sora-ext 720d8bf154 Update modules/server/containers/apps/authentik.nix 2026-06-04 17:27:53 +02:00
sora-ext 6da8823f85 Add systems/avalon/hardware.nix 2026-06-04 10:55:49 +02:00
sora-ext 4d8fc16bbc Delete systems/avalon/server/docker/secrets.txt 2026-06-04 10:55:24 +02:00
sora-ext f2f9e95198 Update systems/asgard/default.nix 2026-06-04 10:55:09 +02:00
sora-ext 07c748071e Update systems/asgard/cfg.nix 2026-06-04 10:54:59 +02:00
sora-ext b489d719da Update shells/default.nix 2026-06-04 10:54:44 +02:00
sora-ext 9e042ebeaf Update modules/nixos/tools/develop/ollama/default.nix 2026-06-04 10:54:10 +02:00
sora-ext d4887fbd64 Update modules/nixos/tools/debug/default.nix 2026-06-04 10:54:00 +02:00
sora-ext 3642b2f41b Update modules/nixos/tools/debug/default.nix 2026-06-04 10:53:24 +02:00
sora-ext d9f7c63b8a Update modules/nixos/system/hw/udev/default.nix 2026-06-04 10:53:04 +02:00
sora-ext 0b5fd3ac41 Update modules/nixos/system/hw/power/default.nix 2026-06-04 10:52:44 +02:00
sora-ext e9f5942202 Update modules/home/gui/theme/default.nix 2026-06-04 10:46:24 +02:00
soraefir c25e75a1b6 fix influx 2026-06-04 02:16:10 +02:00
soraefir d46785a4ef disable for test 2026-06-04 01:41:36 +02:00
soraefir 200dbb9ed1 fix servarr 2026-06-04 01:10:15 +02:00
soraefir 054d097e0d no mail 2026-06-04 01:07:49 +02:00
soraefir 3db1c98a08 no calc 2026-06-04 01:06:22 +02:00
soraefir 7305fdd7c0 trest 2026-06-04 01:02:28 +02:00
soraefir d2a402f724 Fix 2026-06-04 00:52:50 +02:00
soraefir 9a89479f66 Refactor 2026-06-04 00:30:29 +02:00
soraefir b82393272c Refactor 2026-06-03 19:24:29 +02:00
sora-ext 1cb9e9b645 Update modules/server/containers/default.nix 2026-06-03 17:40:10 +02:00
sora-ext b8735803c4 Update flake.nix 2026-06-03 17:35:15 +02:00
sora-ext 14bf297897 Update modules/shared/syscfg/server.nix 2026-06-03 17:20:36 +02:00
sora-ext 1fad610dff Update modules/server/database/default.nix 2026-06-03 17:19:28 +02:00
sora-ext 2c00901b04 Update modules/server/containers/apps/umami.nix 2026-06-03 17:19:11 +02:00
sora-ext 2c0ac0db09 Update modules/server/containers/apps/transmission.nix 2026-06-03 17:19:02 +02:00
sora-ext 6be107374e Update modules/server/containers/apps/traefik.nix 2026-06-03 17:18:52 +02:00
sora-ext 9e4d8274b5 Update modules/server/containers/apps/suwayomi.nix 2026-06-03 17:18:42 +02:00
sora-ext f54dea8a13 Update modules/server/containers/apps/selfmark.nix 2026-06-03 17:17:45 +02:00
sora-ext a6788f13a8 Update modules/server/containers/apps/searxng.nix 2026-06-03 17:17:36 +02:00
sora-ext 5b4af162b9 Update modules/server/containers/apps/openhab.nix 2026-06-03 17:17:14 +02:00
sora-ext 4e5c956f78 Update modules/server/containers/apps/nextcloud.nix 2026-06-03 17:17:02 +02:00
sora-ext 083549e3c6 Update modules/server/containers/apps/jellyfin.nix 2026-06-03 17:16:53 +02:00
sora-ext fada3c79b3 Update modules/server/containers/apps/invidious.nix 2026-06-03 17:16:42 +02:00
sora-ext ce72e4421b Update modules/server/containers/apps/influx.nix 2026-06-03 17:16:30 +02:00
sora-ext ddc5c76a35 Update modules/server/containers/apps/immich.nix 2026-06-03 17:16:18 +02:00
sora-ext d0b6718254 Update modules/server/containers/apps/handbrake.nix 2026-06-03 17:16:06 +02:00
sora-ext c1c76ab3de Update modules/server/containers/apps/gitea.nix 2026-06-03 17:15:56 +02:00
sora-ext 558874731a Update modules/server/containers/apps/frigate.nix 2026-06-03 17:15:46 +02:00
sora-ext b14135274b Update modules/server/containers/apps/freshrss.nix 2026-06-03 17:15:37 +02:00
sora-ext 5df88ac25a Update modules/server/containers/apps/etherpad.nix 2026-06-03 17:15:29 +02:00
sora-ext 2d8e0da386 Update modules/server/containers/apps/ethercalc.nix 2026-06-03 17:15:20 +02:00
sora-ext 8f87c11cb5 Update modules/server/containers/apps/calibre.nix 2026-06-03 17:15:00 +02:00
sora-ext 7b8eeb917f Update modules/server/containers/apps/authentik.nix 2026-06-03 17:14:30 +02:00
sora-ext a2043cafe1 Update modules/server/containers/apps/.template.nix 2026-06-03 17:14:05 +02:00
soraefir 4866426271 fix 2026-06-02 23:57:04 +02:00
soraefir 7f27a61af7 fix db 2026-06-02 23:54:44 +02:00
soraefir 834fd34b54 fix env 2026-06-02 23:52:14 +02:00
soraefir 43df774914 hardcover and secrets 2026-06-02 19:29:15 +02:00
soraefir 6e9c0ff87c fix basepath 2026-06-02 19:23:19 +02:00
soraefir 84ec32c985 fix 2026-06-02 19:20:36 +02:00
soraefir 50a1092fcf fix mark 2026-06-02 19:07:30 +02:00
soraefir 495b210e9a auth selfmark 2026-06-02 18:59:49 +02:00
soraefir 14df99f7af rm perm freshrss 2026-06-02 18:56:40 +02:00
soraefir ab9aabdddf fix 2026-06-02 18:54:28 +02:00
sora-ext 9b15f5fa16 Update modules/server/containers/apps/selfmark.nix 2026-06-02 18:00:50 +02:00
sora-ext f2986239d5 Update modules/server/containers/apps/influx.nix 2026-06-02 18:00:27 +02:00
soraefir d733ef6a2a calibre config cmd 2026-06-02 01:01:25 +02:00
soraefir 01c7278d51 fix env 2026-06-02 00:35:30 +02:00
soraefir 649d15ce0a fix url 2026-06-01 23:06:35 +02:00
soraefir d257687629 fix oidc 2026-06-01 22:57:52 +02:00
soraefir c1e9dfc42a fix perm 2026-06-01 22:53:44 +02:00
soraefir f40e5be25a fix 2026-06-01 22:49:50 +02:00
soraefir b98e9d3f16 fix 2026-06-01 22:41:42 +02:00
soraefir aa5ec8003f fix 2026-06-01 22:36:41 +02:00
soraefir 33500059b7 fix 2026-06-01 22:35:01 +02:00
soraefir a08be8d4e1 fix typo 2026-06-01 22:31:35 +02:00
soraefir b1e6b2151b fix env 2026-06-01 22:28:50 +02:00
soraefir 70eb9f8664 freshrss oidc 2026-06-01 22:27:12 +02:00
soraefir 7805758114 hardcoded swap for sandbox 2026-06-01 22:06:28 +02:00
soraefir 584859d47c fox stock 2026-06-01 22:05:18 +02:00
soraefir a808ba2741 fix 2026-06-01 21:06:02 +02:00
soraefir bdf0352691 fix home ? 2026-06-01 21:04:59 +02:00
soraefir 14862f7de3 wip 2026-06-01 18:51:40 +02:00
sora-ext cd0e1214cf Update modules/server/sops/example.server.yaml 2026-06-01 17:20:54 +02:00
sora-ext dca2fb09b7 Update systems/sandbox/cfg.nix 2026-06-01 17:19:32 +02:00
sora-ext d487ad0c0f Add modules/server/containers/apps/suwayomi.nix 2026-06-01 17:18:06 +02:00
sora-ext bf0e460163 Add modules/server/containers/apps/selfmark.nix 2026-06-01 17:17:41 +02:00
sora-ext ee4e003ccf Update modules/server/containers/apps/homepage.nix 2026-06-01 17:17:18 +02:00
sora-ext bd8c9210c9 Add modules/server/containers/apps/freshrss.nix 2026-06-01 17:16:50 +02:00
sora-ext 036cd1a5de Add modules/server/containers/apps/calibre.nix 2026-06-01 17:16:29 +02:00
sora-ext 3f67ae6bba Update modules/server/containers/default.nix 2026-06-01 17:15:31 +02:00
sora-ext 060cbf3b31 Update modules/home/cli/neofetch/default.nix 2026-06-01 17:14:18 +02:00
sora-ext f2ca3e2f1a Add modules/home/cli/neofetch/config.jsonc 2026-06-01 17:13:56 +02:00
soraefir 3a182533a6 fix 2026-06-01 01:05:27 +02:00
soraefir d457e6869b fix warnings 2026-06-01 00:43:20 +02:00
soraefir 3f4e965154 fix 2026-06-01 00:39:34 +02:00
soraefir 501fbf32a2 fix 2026-06-01 00:37:31 +02:00
soraefir 22879d8897 fix 2026-06-01 00:36:34 +02:00
soraefir d2ccb0b485 bump 26.05 2026-06-01 00:22:01 +02:00
soraefir 7db880b46a bump version 2026-06-01 00:18:26 +02:00
soraefir 1bbbb74553 fix test 2026-06-01 00:07:03 +02:00
soraefir a9d3cbd89a fix 2026-05-31 22:54:10 +02:00
soraefir c2aa7e22d1 better logs 2026-05-31 21:43:51 +02:00
soraefir 4322d315b2 add widget 2026-05-31 21:29:12 +02:00
soraefir 5d979d0720 add services 2026-05-31 20:09:56 +02:00
soraefir ef09e18cd2 Fix blueprint 2026-05-31 19:20:11 +02:00
soraefir 735f1255c1 service account homepage 2026-05-31 19:07:46 +02:00
soraefir 57726f85bd fix 2026-05-31 18:12:36 +02:00
soraefir 0ed4b93fdc disabled for testing 2026-05-31 18:05:42 +02:00
soraefir 8804722960 fix token ldap 2026-05-31 17:40:14 +02:00
soraefir 262a11b6ff fix token 2026-05-31 17:38:32 +02:00
soraefir d551091e40 fix 2026-05-31 17:25:31 +02:00
soraefir 1956285128 home token 2026-05-31 17:10:06 +02:00
soraefir 42f8b17139 fix 2026-05-31 16:46:36 +02:00
soraefir 52aeaf4e51 fix 2026-05-31 16:32:13 +02:00
soraefir 476b79bf34 fix 2026-05-31 16:05:58 +02:00
soraefir eef8730851 fix 2026-05-31 15:45:57 +02:00
soraefir de61030066 fix 2026-05-31 15:32:04 +02:00
soraefir e53bda295b fix 2026-05-31 15:25:48 +02:00
soraefir a762db5310 fox 2026-05-31 15:10:09 +02:00
soraefir 5640687004 fix blueprint 2026-05-31 15:03:13 +02:00
soraefir 97eeb129f5 fix 2026-05-31 14:24:45 +02:00
soraefir d6d4f11d78 test 2026-05-31 14:22:47 +02:00
soraefir fcf923f068 fix 2026-05-31 14:21:50 +02:00
soraefir cdfdb24910 test 2026-05-31 14:16:02 +02:00
soraefir 9b69e153be fix 2026-05-31 13:40:46 +02:00
soraefir 80801837ba fix 2026-05-31 13:33:43 +02:00
soraefir e8ed71264e manually add modules 2026-05-31 13:22:17 +02:00
soraefir 099593e513 fix 2026-05-31 13:21:13 +02:00
soraefir 292e688323 fix attr build 2026-05-31 13:20:06 +02:00
soraefir 0570a0d278 more api 2026-05-31 13:05:20 +02:00
soraefir deafe70bea escape 2026-05-31 12:35:53 +02:00
soraefir 41317e0f33 escape 2026-05-31 12:34:55 +02:00
soraefir 865cbbec7e pkgs 2026-05-31 12:33:56 +02:00
soraefir 6d356b56c5 gitea token 2026-05-31 12:32:37 +02:00
soraefir 50c96de851 Fix sqlite cmd 2026-05-31 12:19:57 +02:00
soraefir f8c696dc57 sqlite fix 2026-05-31 12:05:34 +02:00
soraefir 41d77a5876 fix api key gen 2026-05-31 12:03:44 +02:00
soraefir 7bae85db98 fix env 2026-05-31 11:37:55 +02:00
soraefir 30dd87c42f test 2026-05-31 11:12:39 +02:00
soraefir 39209ca827 env 2026-05-31 10:11:24 +02:00
soraefir c84b97ad8f fix 2026-05-31 01:35:19 +02:00
soraefir 1a13e9855d Fix 2026-05-31 01:32:39 +02:00
soraefir fecaa28ab0 add ports 2026-05-31 01:29:29 +02:00
soraefir 480f7910a6 fix 2026-05-31 01:20:35 +02:00
soraefir 5928479a4e test env 2026-05-31 01:14:46 +02:00
soraefir ca8f1aa222 custom 2026-05-31 01:09:48 +02:00
soraefir d013b4ae88 test 2026-05-31 01:07:52 +02:00
soraefir cebd969445 Fix 2026-05-31 00:06:58 +02:00
soraefir b46d9139df more services 2026-05-31 00:01:45 +02:00
soraefir d6daa605f8 fix 2026-05-30 23:47:45 +02:00
soraefir 5dffd74661 fix 2026-05-30 23:40:09 +02:00
soraefir 94a8aac8a6 fix 2026-05-30 23:37:55 +02:00
soraefir a3c14208c3 fix 2026-05-30 23:33:23 +02:00
soraefir 00990b49e4 fix? 2026-05-30 23:26:50 +02:00
soraefir b13ead5290 fix 2026-05-30 23:24:23 +02:00
soraefir 1a57074033 fix 2026-05-30 23:09:25 +02:00
soraefir 2c6ce74437 WIP 2026-05-30 22:52:19 +02:00
soraefir 45ec8896f8 fix 2026-05-30 22:48:44 +02:00
soraefir 5d6a419a90 typo 2026-05-30 22:47:41 +02:00
soraefir 7cbfecb5f5 homepage cfg 2026-05-30 22:46:26 +02:00
soraefir 7657f711b5 typo 2026-05-30 18:18:17 +02:00
soraefir 1b78eee17e Allowed Hosts Homepage 2026-05-30 18:15:57 +02:00
soraefir 97e1c456d4 Follow main nixpkgs 2026-05-30 18:09:31 +02:00
soraefir 509f55db30 fix build 2026-05-30 18:03:38 +02:00
soraefir 4217227070 Update Cfg and add proxy/wsl 2026-05-30 17:48:18 +02:00
soraefir d9e7775afc fix internal 2026-05-22 01:04:33 +02:00
soraefir 424f12f5f7 test 2026-05-22 00:59:46 +02:00
soraefir 7ca8362d39 auth token 2026-05-22 00:53:29 +02:00
soraefir f54977fe42 perm 2026-05-22 00:48:05 +02:00
soraefir 1b05194939 openport 2026-05-22 00:42:42 +02:00
soraefir 4e31a9f54a stable db 2026-05-22 00:33:52 +02:00
soraefir 7cd78511e7 influx port 2026-05-22 00:33:05 +02:00
soraefir 88d100dd77 update influx 2026-05-22 00:25:59 +02:00
soraefir 775e3e93bb tmpfs 2026-05-22 00:17:12 +02:00
soraefir bfec529d88 lock update 2026-05-22 00:00:59 +02:00
soraefir 2afcbf6d99 fix metrum 2026-05-21 23:55:42 +02:00
soraefir 2cd45ef7de influx secret 2026-05-21 23:43:02 +02:00
soraefir 4d743836ca add influx, disable gitea login proxy 2026-05-21 23:39:36 +02:00
soraefir 9a6dda390b FIX 2026-05-21 22:17:36 +02:00
soraefir dcd998830c more openhab features 2026-05-21 22:15:47 +02:00
soraefir 57bcf4d33c openhab setup 2026-05-21 03:18:36 +02:00
soraefir 7cc516a0be fix auth midleware 2026-05-21 02:28:42 +02:00
soraefir 37143eff2d openhab 2026-05-21 02:16:30 +02:00
soraefir c3edd3c9fa setup 2026-05-21 02:15:48 +02:00
soraefir 775b0b4823 Radarr Naming 2026-05-21 02:14:33 +02:00
soraefir ce0797b73b disable openhab 2026-05-21 02:08:58 +02:00
soraefir a8bbbdc518 fix? 2026-05-21 02:06:43 +02:00
soraefir 742760afa7 fix openhab 2026-05-21 02:00:42 +02:00
soraefir 8b9187b17a Fix stuff 2026-05-21 01:56:13 +02:00
soraefir 8d50d4ecaf Add Radarr setup 2026-05-21 01:51:07 +02:00
soraefir 7ee341ee06 flip size 2026-05-21 01:43:12 +02:00
soraefir 5288f83c2e Fix 2026-05-21 01:39:16 +02:00
soraefir dd70ef6499 Remove linebreak 2026-05-21 01:23:47 +02:00
soraefir beaed878f8 Fix rm 2026-05-21 01:18:32 +02:00
soraefir 09ca162eed Fix 2026-05-21 01:14:46 +02:00
soraefir 4f5e6f210d Fix 2026-05-21 01:10:32 +02:00
soraefir 4a61f43eb9 fix 2026-05-21 01:09:52 +02:00
soraefir a257a3153d Setup Script 2026-05-21 01:08:59 +02:00
soraefir 7da9acfcdc fix 2026-05-21 00:24:57 +02:00
soraefir f838eb9850 fix 2026-05-21 00:23:01 +02:00
soraefir 302f9ae51b fix 2026-05-21 00:20:42 +02:00
soraefir 90b5828663 Add indexers 2026-05-21 00:16:37 +02:00
soraefir 1d9c5cdcd2 fix setup 2026-05-20 22:57:29 +02:00
soraefir b59eecd26a add stuff 2026-05-20 20:43:06 +02:00
soraefir 6f8c8c92f1 fix 2026-05-20 20:05:52 +02:00
soraefir f24102d752 fix url 2026-05-20 19:39:11 +02:00
soraefir 23147ca625 disable for testing 2026-05-20 19:29:18 +02:00
soraefir 40cf001ffa update servarr setup 2026-05-20 19:21:41 +02:00
soraefir 46fae29477 wip 2026-05-20 19:04:51 +02:00
soraefir deea98b2de url envar 2026-05-20 18:45:43 +02:00
soraefir b7aa160baa new stuff 2026-05-20 18:39:11 +02:00
soraefir b91e9cacfd temp 2026-05-20 01:06:30 +02:00
soraefir 46b6b4db4f root 2026-05-20 00:56:23 +02:00
soraefir 5dcb3a7d4a add usr 2026-05-20 00:48:40 +02:00
soraefir 43780f80aa fix 2026-05-20 00:43:56 +02:00
soraefir 23cd521445 test 2026-05-20 00:36:30 +02:00
soraefir 51b6d88c64 test 2026-05-20 00:24:37 +02:00
soraefir bc9b06f3ae fix 2026-05-20 00:06:38 +02:00
soraefir 220aee72ef key length 2026-05-20 00:03:23 +02:00
soraefir 5dac3d02ce root user 2026-05-19 23:58:38 +02:00
soraefir 58825913e0 fixed 2026-05-19 23:56:45 +02:00
soraefir 20103fe83c arr secrets 2026-05-19 23:48:24 +02:00
soraefir 271502f1c9 temp 2026-05-19 23:46:10 +02:00
soraefir 33da5d9f1b enable arr 2026-05-19 23:14:38 +02:00
soraefir 771e6029b6 fix subpath 2026-05-19 22:32:18 +02:00
soraefir 2cf5dcedbd alt path (as we need rpc) 2026-05-19 22:29:33 +02:00
soraefir f27ec01b2f fix 2026-05-19 22:14:40 +02:00
soraefir 9b6359fe86 fix 2026-05-19 22:13:52 +02:00
soraefir 31b23ae4a6 fix 2026-05-19 22:13:05 +02:00
soraefir af36497035 wip 2026-05-19 22:10:22 +02:00
soraefir 3f57b606a0 subpath 2026-05-18 23:17:33 +02:00
soraefir f6498b3177 fixes 2026-05-18 21:50:52 +02:00
soraefir 44d9ae0faf add hass & handbrake 2026-05-18 21:30:32 +02:00
soraefir d5cedb017e fix 2026-05-17 21:52:26 +02:00
soraefir 3e3a108707 Add settings for transmission 2026-05-17 21:38:46 +02:00
soraefir 2b555b1a78 fix 2026-05-17 21:21:51 +02:00
soraefir 04ac376ea9 fix whitelist 2026-05-17 09:43:00 +02:00
soraefir b20763fa86 whitelist fix 2026-05-17 09:40:13 +02:00
soraefir de92ad0f12 fix whitelist 2026-05-17 09:37:48 +02:00
soraefir c3b2468ff3 cleanup and whitelist 2026-05-17 09:30:19 +02:00
soraefir 6ed1514f31 fix invidious companion 2026-05-17 09:07:50 +02:00
soraefir 94a27bb403 fix ssl ? 2026-05-15 16:37:40 +02:00
soraefir 1d656dc4ae add db creation 2026-05-15 16:31:14 +02:00
soraefir b3014bce85 Fix keys 2026-05-15 16:26:27 +02:00
soraefir ef845edd12 typo 2026-05-15 16:22:52 +02:00
soraefir c456e4cad8 Fix env config path 2026-05-15 16:19:18 +02:00
soraefir 39b12966f1 Login rule 2026-05-15 16:06:25 +02:00
soraefir cbef43ae83 missing end 2026-05-15 16:03:34 +02:00
soraefir 765f18d5a0 Add missing func 2026-05-15 16:02:31 +02:00
soraefir 76302840d0 fix typos 2026-05-15 15:59:52 +02:00
soraefir e02aca85bd fix cr code 2026-05-15 15:55:17 +02:00
soraefir 0378ce7dff fix cr code 2026-05-15 15:51:34 +02:00
soraefir 0fd8286331 type fix 2026-05-15 15:47:30 +02:00
soraefir e4aac05b6a fix 2026-05-15 15:44:50 +02:00
soraefir fd7b95e12e Fix 2026-05-15 15:42:49 +02:00
soraefir a94e8beb37 Fix invidious env 2026-05-15 15:41:37 +02:00
soraefir 3e05dfbc07 Override login page 2026-05-15 14:51:32 +02:00
soraefir a94574a53d wip 2026-05-15 13:51:23 +02:00
soraefir 03bec133ba fix 2026-05-15 02:46:04 +02:00
soraefir b6de2d2ccf fix scope 2026-05-15 02:39:27 +02:00
soraefir 334a484ad4 fix scope 2026-05-15 02:35:00 +02:00
soraefir 11f7e95d95 Authentik immich 2026-05-15 02:26:48 +02:00
soraefir 24a0fb6a93 disable IMMICH_IGNORE_MOUNT_CHECK_ERRORS 2026-05-15 02:15:05 +02:00
soraefir 892e271719 Fix typo 2026-05-15 02:12:49 +02:00
soraefir 8769b6da9d Fix immich setup 2026-05-15 02:12:14 +02:00
soraefir 00bdef4307 Fix 2026-05-15 01:31:57 +02:00
soraefir 6ed72c00ff mkdir missings 2026-05-15 01:22:43 +02:00
soraefir 6d5cd82e72 fix 2026-05-15 01:15:59 +02:00
soraefir 3e921ef2ab fix setup 2026-05-15 01:10:34 +02:00
soraefir 09cc16bc40 fix 2026-05-15 00:59:20 +02:00
soraefir 425722e2c6 Disable Setup 2026-05-15 00:55:53 +02:00
soraefir 0fb6aa0047 Fix env 2026-05-15 00:45:59 +02:00
soraefir 07283e1f26 Fix temp 2026-05-15 00:41:21 +02:00
soraefir 362afd3d4e temp override 2026-05-15 00:40:14 +02:00
soraefir 1154cbb3bd Fix dirs 2026-05-15 00:33:48 +02:00
soraefir 25c7823f38 add vchord 2026-05-15 00:26:32 +02:00
soraefir d57fb32f67 Add missing extensions 2026-05-15 00:22:56 +02:00
soraefir 08a7ed2469 fix user 2026-05-15 00:15:40 +02:00
soraefir 45e375168e db setup immich 2026-05-15 00:12:30 +02:00
soraefir 56252474d9 add vector to dbs 2026-05-15 00:04:02 +02:00
soraefir 0ee26c817c add vector pgsql 2026-05-14 23:51:31 +02:00
soraefir 51bd495981 Fix immich 2026-05-14 23:34:32 +02:00
soraefir d9a59e9593 fix image 2026-05-14 23:20:41 +02:00
soraefir 8557df0199 fix image 2026-05-14 23:14:12 +02:00
soraefir 84d9b0ade0 immich db setup 2026-05-14 23:12:56 +02:00
soraefir 7e3d86e37e Simplify 2026-05-14 23:10:11 +02:00
soraefir f5c16775c4 sops immich 2026-05-14 23:07:38 +02:00
soraefir 83dec697d1 cleanup 2026-05-14 23:05:27 +02:00
soraefir dd47977cbd ldap default app 2026-05-14 22:53:12 +02:00
soraefir 601999180b Fix token 2026-05-14 21:30:11 +02:00
soraefir 97004b4b75 Fix ldap 2026-05-14 21:11:19 +02:00
soraefir d15895d8e3 fix ldap role 2026-05-14 20:59:07 +02:00
soraefir 983c19eaa5 Fix 2026-05-14 20:54:43 +02:00
soraefir 4e7a348461 Fix ldap 2026-05-14 20:41:28 +02:00
soraefir 4ccb941766 Fix 2026-05-14 20:31:14 +02:00
soraefir e19fe6a973 remove usuported blueprint 2026-05-14 20:27:24 +02:00
soraefir d9e07543ba fix ldap authentik 2026-05-14 20:19:25 +02:00
soraefir 5d4aaeb49f Fix escaping 2026-05-14 17:51:30 +02:00
soraefir 14540f043d fix script errors 2026-05-14 17:40:57 +02:00
soraefir 2c29f8a41b grep fix 2026-05-14 17:33:45 +02:00
soraefir cd994d6359 check installed plugins 2026-05-14 17:32:48 +02:00
soraefir a1da14f9fb Fix script health check 2026-05-14 17:27:04 +02:00
soraefir 143ea35dc1 Ldap setup jellyfin 2026-05-14 17:18:39 +02:00
soraefir c23ad28f85 fix 2026-05-14 16:34:59 +02:00
soraefir c60123ca3c fix 2026-05-14 16:29:30 +02:00
soraefir c069079a3a fix setup gitea 2026-05-14 16:01:52 +02:00
soraefir e777a56816 fix ldap 2026-05-14 15:57:01 +02:00
soraefir 9933d12183 Fix ldap password 2026-05-14 15:46:51 +02:00
soraefir 2e6c044b89 Ldap WIP 2026-05-14 15:43:52 +02:00
soraefir 252373f956 Fix 2026-05-14 14:46:55 +02:00
soraefir 511837f0a5 Fix JQ 2026-05-14 14:45:11 +02:00
soraefir 2882889eae Add LDAP 2026-05-14 14:43:59 +02:00
soraefir c174fe20ae Fix script 2026-05-14 14:34:39 +02:00
soraefir 31a972d55b Fix 2026-05-14 13:37:41 +02:00
soraefir 9de6cedf33 Jellyfin nss 2026-05-14 13:00:30 +02:00
soraefir f80107efab nss 2026-05-14 12:32:10 +02:00
soraefir ea21907e5d add nss 2026-05-14 11:52:38 +02:00
soraefir d86c3b76b5 perms 2026-05-14 01:35:26 +02:00
soraefir 66e878b902 user 2026-05-14 01:25:44 +02:00
soraefir 6a3327386f home&script 2026-05-14 00:59:26 +02:00
soraefir 860955f555 fix curl 2026-05-14 00:33:01 +02:00
soraefir f5fd711636 fix ssl 2026-05-14 00:26:20 +02:00
soraefir 677c9bc7d4 fixes 2026-05-14 00:21:53 +02:00
soraefir 2cb8d6c24e test 2026-05-14 00:12:18 +02:00
soraefir 3b4e7d07a4 jellyfin setup script 2026-05-14 00:10:38 +02:00
soraefir 8ecef91c92 Authentik apps 2026-05-13 23:27:09 +02:00
soraefir 95c1bb126e typo 2026-05-13 22:48:10 +02:00
soraefir de18ad8127 Try secure 2026-05-13 22:45:44 +02:00
soraefir 8e57822c10 Fix host 2026-05-13 22:45:28 +02:00
soraefir f2883aa33d Fix ldap env 2026-05-13 22:43:17 +02:00
soraefir 7bd5ceacd9 fix name 2026-05-13 22:33:28 +02:00
soraefir e940bd0ec1 Fix ldap 2026-05-13 22:28:55 +02:00
soraefir 44813226c2 ldap insecure 2026-05-13 22:26:34 +02:00
soraefir 7381a17f87 fix ldap 2026-05-13 22:26:18 +02:00
soraefir 19cec13a43 fix authentik token 2026-05-13 22:23:26 +02:00
soraefir 9117530393 Fix authentik 2026-05-13 22:18:16 +02:00
soraefir 868ba53208 move traefik rule 2026-05-13 22:00:34 +02:00
soraefir f7c55f3a5a Fix Authentik LDAP 2026-05-13 21:48:57 +02:00
sora-ext 7cfd1bb245 Update modules/server/sops/example.server.yaml 2026-05-13 17:32:12 +02:00
sora-ext 85a6517609 Add modules/server/containers/data/authentik/ldap.yaml 2026-05-13 17:31:46 +02:00
sora-ext d55756f8f8 Update modules/server/containers/apps/servarr.nix 2026-05-13 17:31:29 +02:00
sora-ext 18beb41cd8 Update modules/server/containers/apps/jellyfin.nix 2026-05-13 17:31:19 +02:00
sora-ext 78f01cf111 Update modules/server/containers/apps/invidious.nix 2026-05-13 17:31:07 +02:00
sora-ext c582d89715 Add modules/server/containers/apps/influx.nix 2026-05-13 17:30:57 +02:00
sora-ext 4270b15f9d Update modules/server/containers/apps/immich.nix 2026-05-13 17:30:43 +02:00
sora-ext a62bc660c5 Update modules/server/containers/apps/homeassistant.nix 2026-05-13 17:30:33 +02:00
sora-ext 973fd78c1b Update modules/server/containers/apps/frigate.nix 2026-05-13 17:30:24 +02:00
sora-ext 7e62883e66 Update modules/server/containers/apps/authentik.nix 2026-05-13 17:30:10 +02:00
sora-ext feccc2c3e0 Update modules/server/containers/apps/.template.nix 2026-05-13 17:30:00 +02:00
sora-ext 0b02de0957 Update modules/server/containers/default.nix 2026-05-13 17:29:49 +02:00
sora-ext 721838df2b Update modules/server/containers/builder.nix 2026-05-13 17:29:33 +02:00
soraefir 8c800ad0d3 Jellyfin LDAP 2026-05-13 01:56:45 +02:00
soraefir f7af832d8e fix jellyfin media path 2026-05-13 01:31:15 +02:00
soraefir 6ddf4a07cc fix umami db 2026-05-13 01:00:14 +02:00
soraefir 0df674647c sops 2026-05-13 00:50:22 +02:00
soraefir 7bead6b3ac fix umami 2026-05-13 00:41:36 +02:00
soraefir f0befa87be test 2026-05-12 22:52:25 +02:00
soraefir 6d665ee59c add sops to servarr 2026-05-12 21:28:24 +02:00
soraefir befe46c085 sops 2026-05-12 21:26:46 +02:00
soraefir ba6d057600 Fix 2026-05-12 21:26:07 +02:00
soraefir db5230bd69 typo 2026-05-12 21:25:01 +02:00
soraefir e1a80bb7ce Add new modules 2026-05-12 21:24:02 +02:00
sora-ext 7d95ba04a9 Add modules/shared/syscfg/user.nix 2026-05-12 18:02:13 +02:00
sora-ext 9169205357 Add modules/shared/syscfg/make.nix 2026-05-12 18:02:01 +02:00
sora-ext 74721f6b09 Add modules/shared/syscfg/net.nix 2026-05-12 18:01:52 +02:00
sora-ext 668c0107f9 Add modules/shared/syscfg/server.nix 2026-05-12 18:01:44 +02:00
sora-ext 331291c54d Update modules/shared/syscfg/default.nix 2026-05-12 18:01:33 +02:00
sora-ext d10f53e485 Add modules/server/containers/apps/immich.nix 2026-05-12 17:47:01 +02:00
sora-ext 82aea8268f Update modules/shared/syscfg/default.nix 2026-05-12 17:46:23 +02:00
sora-ext 050eaedca2 Update modules/server/sops/example.server.yaml 2026-05-12 17:46:11 +02:00
sora-ext be9cb270aa Update modules/server/sops/default.nix 2026-05-12 17:45:59 +02:00
sora-ext a5e0e96b52 Update modules/server/database/default.nix 2026-05-12 17:45:49 +02:00
sora-ext 4366232f18 Update modules/server/containers/apps/umami.nix 2026-05-12 17:45:34 +02:00
sora-ext 4398b1d888 Update modules/server/containers/apps/transmission.nix 2026-05-12 17:45:25 +02:00
sora-ext c4b5c47aa4 Update modules/server/containers/apps/traefik.nix 2026-05-12 17:45:16 +02:00
sora-ext ff64e6c231 Update modules/server/containers/apps/servarr.nix 2026-05-12 17:45:07 +02:00
sora-ext e7d656141a Update modules/server/containers/apps/searxng.nix 2026-05-12 17:44:59 +02:00
sora-ext cf3c2428fb Update modules/server/containers/apps/nextcloud.nix 2026-05-12 17:44:50 +02:00
sora-ext a2dc050b1c Update modules/server/containers/apps/jellyfin.nix 2026-05-12 17:44:41 +02:00
sora-ext 8bf332caf2 Update modules/server/containers/apps/gitea.nix 2026-05-12 17:44:25 +02:00
sora-ext 20d3786547 Update modules/server/containers/apps/etherpad.nix 2026-05-12 17:44:18 +02:00
sora-ext 79422c180a Update modules/server/containers/apps/ethercalc.nix 2026-05-12 17:44:11 +02:00
sora-ext 65fc9c6df2 Update modules/server/containers/apps/collabora.nix 2026-05-12 17:44:03 +02:00
sora-ext a59cbd13a3 Update modules/server/containers/apps/authentik.nix 2026-05-12 17:43:55 +02:00
sora-ext 5f04ef7ae5 Update modules/server/containers/apps/.todo.md 2026-05-12 17:43:48 +02:00
sora-ext 0aff508cda Add modules/server/containers/apps/.template.nix 2026-05-12 17:43:40 +02:00
sora-ext 30df106b94 Update modules/server/containers/default.nix 2026-05-12 17:43:28 +02:00
sora-ext 3abdb6d637 Update modules/server/containers/builder.nix 2026-05-12 17:43:18 +02:00
soraefir cd4c727255 gitea runner 2026-05-12 01:06:14 +02:00
soraefir 26cb095014 Fix 2026-05-12 00:55:55 +02:00
soraefir 2a9113e07d fix calc 2026-05-12 00:50:25 +02:00
soraefir 42a170283d add sops 2026-05-12 00:45:07 +02:00
soraefir 3d4cdaf6e9 new stuff 2026-05-12 00:42:09 +02:00
soraefir 21d959b592 fix script admin 2026-05-11 23:00:07 +02:00
soraefir 0895d67489 fix gitea script 2026-05-11 22:57:43 +02:00
soraefir 86f1fc116c Fix gitea script 2026-05-11 22:55:03 +02:00
soraefir b82033f857 gitea adminify 2026-05-11 22:45:34 +02:00
soraefir fbe8399886 Fix 2026-05-11 22:11:26 +02:00
soraefir 036f1117be fix mkdir 2026-05-11 22:05:38 +02:00
soraefir b3eb1de9e6 fix 2026-05-11 22:02:16 +02:00
soraefir 91c2928a56 Fix 2026-05-11 21:53:49 +02:00
soraefir 89ffc75db2 Fix sops APIKEY 2026-05-11 21:49:20 +02:00
soraefir 63fc0bde6e Fix port 2026-05-11 21:29:16 +02:00
soraefir bc765ea0c6 Fix cloud port 2026-05-11 20:35:14 +02:00
soraefir df236d4ec7 sh 2026-05-11 20:18:14 +02:00
soraefir 2f24725df4 add sh 2026-05-11 20:16:37 +02:00
soraefir 4708753085 Gitea 2026-05-11 19:22:06 +02:00
soraefir 370416edba Fix sops 2026-05-11 19:04:16 +02:00
soraefir ee1dec3d44 rm port 2026-05-11 18:50:36 +02:00
sora-ext b748db9550 Add modules/server/containers/apps/.todo.md 2026-05-11 18:47:46 +02:00
sora-ext 78381d15ff Update modules/server/containers/apps/authentik.nix 2026-05-11 18:47:46 +02:00
sora-ext 037d75af2a Update modules/server/containers/apps/collabora.nix 2026-05-11 18:47:46 +02:00
sora-ext 9e9338d136 Add modules/server/containers/apps/ethercalc.nix 2026-05-11 18:47:46 +02:00
sora-ext cd19d8ac06 Update modules/server/containers/apps/etherpad.nix 2026-05-11 18:47:46 +02:00
sora-ext 6dda5f6bd5 Add modules/server/containers/apps/frigate.nix 2026-05-11 18:47:46 +02:00
sora-ext 836b890fab Add modules/server/containers/apps/gitea.nix 2026-05-11 18:47:46 +02:00
sora-ext 40ed44aa52 Add modules/server/containers/apps/handbrake.nix 2026-05-11 18:47:46 +02:00
sora-ext 2fcbf6adb3 Add modules/server/containers/apps/homeassistant.nix 2026-05-11 18:47:46 +02:00
sora-ext f3fbf159b4 Add modules/server/containers/apps/invidious.nix 2026-05-11 18:47:46 +02:00
sora-ext f3b8feb50d Update modules/server/containers/apps/nextcloud.nix 2026-05-11 18:47:46 +02:00
sora-ext f6f51597cd Add modules/server/containers/apps/jellyfin.nix 2026-05-11 18:47:46 +02:00
sora-ext 5c7b5fcbfe Add modules/server/containers/apps/searxng.nix 2026-05-11 18:47:46 +02:00
sora-ext 07b6868d27 Add modules/server/containers/apps/servarr.nix 2026-05-11 18:47:46 +02:00
sora-ext 870b13ef36 Update modules/server/containers/apps/traefik.nix 2026-05-11 18:47:46 +02:00
sora-ext 4b8c8bdc51 Add modules/server/containers/apps/transmission.nix 2026-05-11 18:47:46 +02:00
sora-ext c24628b574 Add modules/server/containers/apps/trmnl.nix 2026-05-11 18:47:46 +02:00
sora-ext c1fb77a89f Add modules/server/containers/apps/umami.nix 2026-05-11 18:47:46 +02:00
sora-ext 94012aa44c Update modules/shared/sops/default.nix 2026-05-11 18:47:46 +02:00
sora-ext 5ff282e65c Update modules/shared/syscfg/default.nix 2026-05-11 18:47:46 +02:00
Renovate Bot 0bedb71d07 Lock file maintenance 2026-05-11 18:47:46 +02:00
Renovate Bot 47cbbc56cb Lock file maintenance 2026-05-11 18:47:46 +02:00
sora e116efd45c Update modules/server/containers/apps/etherpad.nix 2026-05-11 03:03:52 +02:00
soraefir ff498d15a3 fix 2026-05-11 02:51:27 +02:00
soraefir 90c596270f Fix 2026-05-11 02:49:57 +02:00
soraefir 458a9091d4 fix 2026-05-11 02:32:08 +02:00
soraefir 123d18d1e8 fix 2026-05-11 02:29:11 +02:00
soraefir f05f7b0147 fix 2026-05-11 02:23:54 +02:00
soraefir a41390dcee Fix key 2026-05-11 02:19:31 +02:00
soraefir 29478e2aed Fix api 2026-05-11 02:11:15 +02:00
soraefir 82b422883e Fix api 2026-05-11 01:56:11 +02:00
soraefir 4151e50a42 fix 2026-05-11 01:36:08 +02:00
soraefir 5afaf859b9 fix 2026-05-11 00:55:20 +02:00
soraefir 0cd20319fe fix script 2026-05-11 00:54:02 +02:00
soraefir 468cd34fca fix 2026-05-11 00:46:52 +02:00
soraefir 882d36ff83 typo 2026-05-11 00:45:43 +02:00
soraefir dc2682c829 fix 2026-05-11 00:44:26 +02:00
soraefir f354a99d56 test new setup script 2026-05-11 00:42:34 +02:00
soraefir bf1fbea959 chmod and fix 2026-05-11 00:38:02 +02:00
soraefir 31addeda66 Opt dir 2026-05-11 00:33:38 +02:00
soraefir d0ca9761d7 fix 2026-05-11 00:24:31 +02:00
soraefir bbbb5831a8 etherpad api 2026-05-11 00:15:54 +02:00
soraefir 46f4b5288b Admin 2026-05-10 22:39:33 +02:00
soraefir 8293df4974 Fix 2026-05-10 22:36:43 +02:00
soraefir 08866273cc fix 2026-05-10 22:31:31 +02:00
soraefir e2772e51d9 Fix group 2026-05-10 22:27:29 +02:00
soraefir 6bf856b702 WIP 2026-05-10 22:21:02 +02:00
soraefir 93199b4359 tmp fix 2026-05-10 22:11:53 +02:00
soraefir d3ffacf4ca Fix admin 2026-05-10 22:08:03 +02:00
soraefir ac0e28b5ab fix authentic flow 2026-05-10 22:02:11 +02:00
soraefir e76f53d887 test template 2026-05-10 21:47:49 +02:00
soraefir f67e142f53 fix envfile 2026-05-10 21:43:48 +02:00
soraefir 8165bf6935 Add force exec 2026-05-10 21:40:49 +02:00
soraefir 09539b5866 Add user setup script 2026-05-10 21:39:12 +02:00
soraefir 1b2a724a26 Fix idp & co, add base ak setup 2026-05-10 20:42:19 +02:00
soraefir e6e6e4af49 Fix saml url 2026-05-10 19:56:05 +02:00
soraefir e999a5bf2c Fix 2026-05-10 19:49:32 +02:00
soraefir a57818e37e Fix db 2026-05-10 19:43:19 +02:00
soraefir 0e61b2fad4 saml name 2026-05-10 19:41:58 +02:00
soraefir 9016657699 import cert 2026-05-10 19:34:46 +02:00
soraefir 5462434558 Fix 2026-05-10 19:19:15 +02:00
soraefir aa36fa812c Foix blueprint 2026-05-10 19:14:37 +02:00
soraefir f5f28968c6 test blueprint fix 2026-05-10 18:58:13 +02:00
soraefir 4c2ef6e264 Fix blueprints 2026-05-10 18:51:58 +02:00
soraefir fa808f3eb2 Fix nix mkdata 2026-05-10 18:44:58 +02:00
soraefir 7bc9ae1f2d Fix mkData 2026-05-10 18:42:03 +02:00
soraefir e53be27e96 Fix 2026-05-10 18:36:20 +02:00
soraefir 88ab6e2007 typo 2026-05-10 18:34:23 +02:00
soraefir 864e698272 fix 2026-05-10 18:33:22 +02:00
soraefir 8961706503 fix 2026-05-10 18:31:56 +02:00
soraefir c637fea0d0 Add authentik blueprints 2026-05-10 18:29:53 +02:00
soraefir 9813e7d49a Longer timeout 2026-05-10 12:29:46 +02:00
soraefir ea6db4b9bf fix 2026-05-10 12:26:34 +02:00
soraefir 2eff0969e0 fix 2026-05-10 12:21:46 +02:00
soraefir cf5648122d fix 2026-05-10 12:21:01 +02:00
soraefir b10e7a5a93 fix 2026-05-10 12:04:03 +02:00
soraefir 882a43b705 cfg 2026-05-10 12:03:05 +02:00
soraefir e9868a2513 fix 2026-05-10 12:02:02 +02:00
soraefir 43a0f903b0 Fix 2026-05-10 12:01:28 +02:00
soraefir 1b76ec20b4 fix 2026-05-10 11:59:37 +02:00
soraefir 6a7fcf6152 fix 2026-05-10 11:58:29 +02:00
soraefir b6bc6dd138 dbg 2026-05-10 11:57:59 +02:00
soraefir 90f8387192 tmp ignore 2026-05-10 11:56:13 +02:00
soraefir 25604d6c14 test 2026-05-10 11:55:53 +02:00
soraefir 51d60de5c0 fix 2026-05-10 11:54:37 +02:00
soraefir 5e8cd65785 fix 2026-05-10 11:52:32 +02:00
soraefir fa5845808b fix 2026-05-10 11:50:34 +02:00
soraefir 28c17d9bb6 colors 2026-05-10 11:49:18 +02:00
soraefir 89d2f9a48e typo2 2026-05-10 11:42:53 +02:00
soraefir e58d323ea0 typo 2026-05-10 11:41:59 +02:00
soraefir 7465b6b24c script omprovement 2026-05-10 11:38:19 +02:00
soraefir 59c6b68501 Add cron 2026-05-09 19:40:22 +02:00
soraefir 9273387170 Script improvements 2026-05-09 19:35:05 +02:00
soraefir 55a08673f0 fix caldav 2026-05-09 19:25:06 +02:00
soraefir 5dbb95603d silence script verbosity 2026-05-09 19:20:35 +02:00
soraefir d60f8dd56f improve script 2026-05-09 19:18:27 +02:00
soraefir 7d35cb319f Fix 2026-05-09 19:12:36 +02:00
soraefir 8d4caac83b group for nextcloud 2026-05-09 18:47:38 +02:00
soraefir ad2b492b51 Fix service 2026-05-09 18:42:22 +02:00
soraefir 4b68accf2f fix nextcloud 2026-05-09 17:52:39 +02:00
soraefir 0d9c8a2974 fix 2026-05-09 17:51:06 +02:00
soraefir 63d2dddd1e setup scripts 2026-05-09 17:50:23 +02:00
soraefir 55d678df19 bump nextcloud 2026-05-09 13:33:56 +02:00
soraefir 88a4ab069e registries 2026-05-09 13:28:48 +02:00
soraefir c54ed4a712 Admin user 2026-05-09 12:54:13 +02:00
soraefir 3db4517a3b temps 2026-05-09 12:43:16 +02:00
soraefir f3dfe561ad add plugin 2026-05-09 12:38:04 +02:00
soraefir b58da2b2e1 port fix 2026-05-09 12:12:42 +02:00
soraefir 28fa63919f Fix env 2026-05-09 12:09:27 +02:00
soraefir cb7e29bfe0 container settings 2026-05-09 11:58:38 +02:00
soraefir ea58be6fdc fix typo 2026-05-09 11:57:21 +02:00
soraefir da51e61c05 escape 2026-05-09 11:56:42 +02:00
soraefir 1ca61b70d2 fix env 2026-05-09 11:53:29 +02:00
soraefir eafafe876f postgres 2026-05-09 11:03:58 +02:00
soraefir 21adca1fbc tmp perm 2026-05-09 10:50:25 +02:00
soraefir 57efc58bc2 Fix user etherpad 2026-05-09 10:46:04 +02:00
soraefir cd5deea849 etherpad 2026-05-09 10:45:16 +02:00
soraefir 9f5f8751e5 fix ddos 2026-05-09 10:32:18 +02:00
soraefir f02adc6d93 fix 2026-05-09 10:24:13 +02:00
soraefir b2f6d8cc9e Fix 2026-05-09 10:19:21 +02:00
soraefir c18ac097fa test 2026-05-09 10:17:27 +02:00
soraefir 1fc9017e7e fix 2026-05-09 10:12:01 +02:00
soraefir 8ff90e54b8 fix 2026-05-09 10:11:21 +02:00
soraefir fba3a24f16 custom image 2026-05-09 10:09:51 +02:00
soraefir fcb97828f4 test custom img 2026-05-09 10:04:47 +02:00
soraefir e04382742f cleanup traefik 2026-05-09 10:03:09 +02:00
soraefir 48b40d819b fix typo 2026-05-09 09:56:28 +02:00
soraefir 8b75968f11 fix tls 2026-05-09 09:55:30 +02:00
soraefir dda8409329 cert 2026-05-09 09:46:18 +02:00
soraefir 9a0b5171b1 fix dns 2026-05-09 09:42:33 +02:00
soraefir 9abb5b2f26 logs 2026-05-09 09:35:41 +02:00
soraefir 8362599b54 traefik 2026-05-09 09:34:07 +02:00
soraefir c1b9c12281 fix 2026-05-09 09:26:40 +02:00
soraefir e4dcb0bd39 api port 2026-05-09 00:20:27 +02:00
soraefir a31991c507 typo 2026-05-08 23:58:53 +02:00
soraefir e1651cba2a traefik docker 2026-05-08 23:57:19 +02:00
soraefir bb5ecbba73 acme 2026-05-08 23:54:20 +02:00
soraefir 0c79617647 test acme 2026-05-08 23:48:37 +02:00
soraefir a3bc8b80c5 fix acme 2026-05-08 23:39:48 +02:00
soraefir 55fcf8b71a fix 2026-05-08 23:32:25 +02:00
soraefir 5aabd9acce Fix 2026-05-08 23:31:56 +02:00
soraefir e652c12bf2 fix traefik 2026-05-08 23:30:04 +02:00
soraefir 4c684cf9b1 Fix portfw traefik 2026-05-08 23:18:43 +02:00
soraefir 0c60bbbaa8 rm deprecated 2026-05-08 23:14:51 +02:00
soraefir 097334b483 fix statfs 2026-05-08 23:10:52 +02:00
soraefir bfd099d201 container registry 2026-05-08 23:05:25 +02:00
soraefir 1fe6e43046 sops 2026-05-08 22:56:05 +02:00
soraefir 23b8ad480e fix subdomain 2026-05-08 22:55:21 +02:00
soraefir 3d1fc2a2c9 traefik 2026-05-08 22:53:41 +02:00
soraefir aacca16eb2 fix tmpfs 2026-05-08 21:05:08 +02:00
soraefir 5de459c347 fix nulls 2026-05-08 21:01:46 +02:00
soraefir d898116ff4 fix nulls 2026-05-08 20:59:40 +02:00
soraefir e2b688c836 fix sops 2026-05-08 20:54:54 +02:00
soraefir b5d57bf9c8 test 2026-05-08 20:52:08 +02:00
soraefir 236f9dbdc3 Sops 2026-05-08 20:50:13 +02:00
soraefir 9696ca9a6d ipfw 2026-05-08 20:47:00 +02:00
soraefir df523c48e5 rename and fix 2026-05-08 20:46:23 +02:00
soraefir 4d398d5596 sops 2026-05-08 20:36:26 +02:00
soraefir 5045291097 sops 2026-05-08 20:35:43 +02:00
soraefir 2dc1632a40 sops 2026-05-08 20:32:37 +02:00
soraefir 744a2b8563 Secrets 2026-05-08 20:31:12 +02:00
soraefir b722d349af fix cloud 2026-05-08 20:28:22 +02:00
soraefir 7438905618 WIP 2026-05-08 20:25:51 +02:00
soraefir 908c144c73 add cloud 2026-05-08 20:25:14 +02:00
soraefir 6d353df19f fix collabora 2026-05-08 20:23:40 +02:00
soraefir 7194d91b1c WIP 2026-05-08 20:22:04 +02:00
soraefir d3c301db36 Fix 2026-05-08 02:52:56 +02:00
soraefir 135d48d78c test 2026-05-08 02:47:19 +02:00
soraefir d4292cd46d test 2026-05-08 02:45:33 +02:00
soraefir 4a4d3e3604 typo 2026-05-08 02:37:06 +02:00
soraefir d076538901 test 2026-05-08 02:35:32 +02:00
soraefir 8fedaf18cd firewall? 2026-05-08 02:20:28 +02:00
soraefir 4c1f9f0e78 nft 2026-05-08 02:17:10 +02:00
soraefir 1a8eb085df fix db ? 2026-05-08 02:13:44 +02:00
soraefir 8a619d9fc6 env 2026-05-08 02:00:10 +02:00
soraefir a76f920297 Fix 2026-05-08 01:58:37 +02:00
soraefir fe93cb708e accept podman traffic 2026-05-08 01:49:31 +02:00
soraefir cb29056296 Sops 2026-05-08 01:37:57 +02:00
soraefir 4bc68eeeaf more fix 2026-05-08 01:34:17 +02:00
soraefir 9cf9937cb7 wg nft 2026-05-08 01:26:53 +02:00
soraefir 593514c100 fix ssh 2026-05-08 01:21:56 +02:00
soraefir 6ad9a0b34c Env 2026-05-08 01:19:04 +02:00
soraefir 65e3568072 Db 2026-05-08 01:18:02 +02:00
soraefir c55b06cca9 fix nft 2026-05-08 01:15:56 +02:00
soraefir 40dba4b959 Fix nftable 2026-05-08 01:15:27 +02:00
soraefir bc8a9d42f9 Fix nftable 2026-05-08 01:09:51 +02:00
soraefir cd5a1aeed4 temp fix 2026-05-08 01:08:59 +02:00
soraefir 0f2081486d Wops 2026-05-08 01:08:07 +02:00
soraefir 1c022d7642 Fix secret 2026-05-08 00:53:00 +02:00
soraefir 379f6befb3 fix 2026-05-08 00:44:13 +02:00
soraefir 868d2ce116 fix 2026-05-08 00:29:12 +02:00
soraefir 94fdfa2b33 Test acme 2026-05-08 00:17:46 +02:00
soraefir a73ad174ea Fix 2026-05-08 00:14:41 +02:00
soraefir fba5a79ce6 Fix parenthesis 2026-05-08 00:12:17 +02:00
soraefir e8c9fc52fb Update 2026-05-08 00:06:21 +02:00
soraefir 8092bac6b7 nginx 2026-05-07 00:03:43 +02:00
soraefir 7d80478e83 more fixes authentik 2026-05-06 23:47:09 +02:00
soraefir 2cab462db5 Fix authentik worker 2026-05-06 23:45:21 +02:00
soraefir 0bb796fbe8 Fix cfg 2026-05-06 23:42:29 +02:00
soraefir 1f2cc94a0a Fix builder 2026-05-06 23:39:28 +02:00
soraefir 3caf507905 Fix attempt 2026-05-06 23:35:03 +02:00
soraefir 27a5566ac6 Rename file 2026-05-06 23:31:12 +02:00
soraefir b439888fa8 Fix naming 2026-05-06 23:30:08 +02:00
soraefir 093497367a container builder 2026-05-06 23:28:49 +02:00
soraefir 1c0cfd1afe change podman building 2026-05-06 22:59:11 +02:00
113 changed files with 5094 additions and 766 deletions
+2 -2
View File
@@ -12,13 +12,13 @@ jobs:
build-nixos:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v6
- name: "Install Nix ❄️"
uses: cachix/install-nix-action@v31
# - uses: DeterminateSystems/nix-installer-action@v4
- uses: DeterminateSystems/magic-nix-cache-action@v14
- uses: DeterminateSystems/magic-nix-cache-action@v13
- uses: DeterminateSystems/flake-checker-action@v12
- name: "Install Cachix ❄️"
+1 -1
View File
@@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v7
uses: actions/checkout@v6
- name: Install nix
uses: DeterminateSystems/nix-installer-action@v22
with:
+1
View File
@@ -2,3 +2,4 @@ result
age-key.txt
.decrypted~common.yaml
.decrypted*
.tmp
Generated
+115 -96
View File
@@ -1,27 +1,5 @@
{
"nodes": {
"arion": {
"inputs": {
"flake-parts": "flake-parts",
"haskell-flake": "haskell-flake",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1770259557,
"narHash": "sha256-EvZ09k9+mzXAngPzU2K7oLLUDlKoT1numb4bDb3Gtl4=",
"owner": "hercules-ci",
"repo": "arion",
"rev": "9b24cf65c72cb0e9616e437d55e1ac8e5c6bc715",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "arion",
"type": "github"
}
},
"base16-schemes": {
"flake": false,
"locked": {
@@ -45,11 +23,11 @@
]
},
"locked": {
"lastModified": 1783395956,
"narHash": "sha256-AAbexQvDoK+6GFJdhY6kqjA+6ECKRkidgWxkn4gMwAA=",
"lastModified": 1779036909,
"narHash": "sha256-zXcwYQGCT6pzinK+1dBB2ekTVtfxGZAapb3Evdcu4fY=",
"owner": "lnl7",
"repo": "nix-darwin",
"rev": "d5bd9cd77aea4c0a8f49e7fd85545671a208ed15",
"rev": "56c666e108467d87d13508936aade6d567f2a501",
"type": "github"
},
"original": {
@@ -59,28 +37,23 @@
"type": "github"
}
},
"flake-parts": {
"inputs": {
"nixpkgs-lib": [
"arion",
"nixpkgs"
]
},
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1769996383,
"narHash": "sha256-AnYjnFWgS49RlqX7LrC4uA+sCCDBj0Ry/WOJ5XWAsa0=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "57928607ea566b5db3ad13af0e57e921e6b12381",
"lastModified": 1767039857,
"narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"flake-parts_2": {
"flake-parts": {
"inputs": {
"nixpkgs-lib": [
"nur",
@@ -101,16 +74,34 @@
"type": "github"
}
},
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1681202837,
"narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "cfacdce06f30d2b68473a46042957675eebb3401",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"hardware": {
"inputs": {
"nixpkgs": "nixpkgs"
},
"locked": {
"lastModified": 1783792734,
"narHash": "sha256-50rvY9GdFvpYDcMLcD/4cWSi0hVxArT5wsGlVsHy8eY=",
"lastModified": 1780065812,
"narHash": "sha256-SCSLUKBmwlSLGQ8Xbr8PjRFtiHNk0l9ktqkcmqdBkfE=",
"owner": "nixos",
"repo": "nixos-hardware",
"rev": "8efb4337e857949f4cfac86d12ef1066f417f31f",
"rev": "b76b5639c0593e0aeb0b5879ad62d4b30596c144",
"type": "github"
},
"original": {
@@ -119,22 +110,6 @@
"type": "github"
}
},
"haskell-flake": {
"locked": {
"lastModified": 1675296942,
"narHash": "sha256-u1X1sblozi5qYEcLp1hxcyo8FfDHnRUVX3dJ/tW19jY=",
"owner": "srid",
"repo": "haskell-flake",
"rev": "c2cafce9d57bfca41794dc3b99c593155006c71e",
"type": "github"
},
"original": {
"owner": "srid",
"ref": "0.1.0",
"repo": "haskell-flake",
"type": "github"
}
},
"home-manager": {
"inputs": {
"nixpkgs": [
@@ -142,16 +117,16 @@
]
},
"locked": {
"lastModified": 1779506708,
"narHash": "sha256-QOD/CNm196nCJRheux/URi4/HE66fthdOMqCJoPP1Y0=",
"lastModified": 1779726825,
"narHash": "sha256-RUkMrREjKDQrA+dA9+xZviGAxM5W1aVdyOr/bSYpHrE=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "3ee51fbdac8c8bdfe1e7e1fcaba6520a563f394f",
"rev": "b179bde238977f7d4454fc770b1a727eaf55111c",
"type": "github"
},
"original": {
"owner": "nix-community",
"ref": "release-25.11",
"ref": "release-26.05",
"repo": "home-manager",
"type": "github"
}
@@ -177,11 +152,11 @@
},
"nixUnstable": {
"locked": {
"lastModified": 1783791668,
"narHash": "sha256-zbcZ1dmBTPfJ7Mlqh/yLEPGpgJnwuv4Xr1xucy2WqMA=",
"lastModified": 1780030872,
"narHash": "sha256-u6WU/yd/o8iYQrHX3RAwO1hYa3LkoSL+WNQD0rJfJZQ=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "716c7a2664ca8325617b8a7fbb609273f2c4cae7",
"rev": "e9a7635a57597d9754eccebdfc7045e6c8600e6b",
"type": "github"
},
"original": {
@@ -191,6 +166,27 @@
"type": "github"
}
},
"nixos-wsl": {
"inputs": {
"flake-compat": "flake-compat",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1780169171,
"narHash": "sha256-3HBYDfBgZ+ph52HS6Ks/bMMwuh2uONIT72sZ1CtLE/s=",
"owner": "nix-community",
"repo": "nixos-wsl",
"rev": "998b2821c30b2938637230916904ceb8757c79e8",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nixos-wsl",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1767892417,
@@ -221,47 +217,33 @@
},
"nixpkgs_2": {
"locked": {
"lastModified": 1782847189,
"narHash": "sha256-twXPFqFsrrY5r28Zh7Homgcp2gUMBgQ6WDS98Q/3xFI=",
"lastModified": 1780203844,
"narHash": "sha256-K5sT4jTpGs15ADhviMKNBH38REpPf5Q6mM1+N6cArVE=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "b6018f87da91d19d0ab4cf979885689b469cdd41",
"rev": "b51242d7d43689db2f3be91bd05d5b24fbb469c4",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-25.11",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_3": {
"locked": {
"lastModified": 1783522502,
"narHash": "sha256-iffAls3iaNTyJC2faYcUXSI+Gp02cDjYl+MygxKl2GI=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "0bb7ec54c8483066ec9d7720e780a5caa71f8612",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-unstable",
"ref": "nixos-26.05",
"repo": "nixpkgs",
"type": "github"
}
},
"nur": {
"inputs": {
"flake-parts": "flake-parts_2",
"nixpkgs": "nixpkgs_3"
"flake-parts": "flake-parts",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1783812717,
"narHash": "sha256-/ShoG1aG4RN+002Y69CQk0HJ8U/DRw5PHGJcgqB/rLQ=",
"lastModified": 1780265777,
"narHash": "sha256-t/KORFHEv8Jn2vFmVfv4Zffekv+MUogI2KgtxuCcEmQ=",
"owner": "nix-community",
"repo": "nur",
"rev": "f677cb4b829c9df339277c6e80200b1c98320463",
"rev": "39917b7f68263188707925ffe26c9df6ef4e7d64",
"type": "github"
},
"original": {
@@ -272,15 +254,16 @@
},
"root": {
"inputs": {
"arion": "arion",
"darwin": "darwin",
"hardware": "hardware",
"home-manager": "home-manager",
"nix-colors": "nix-colors",
"nixUnstable": "nixUnstable",
"nixos-wsl": "nixos-wsl",
"nixpkgs": "nixpkgs_2",
"nur": "nur",
"sops-nix": "sops-nix"
"sops-nix": "sops-nix",
"vscode-server": "vscode-server"
}
},
"sops-nix": {
@@ -290,11 +273,11 @@
]
},
"locked": {
"lastModified": 1783174389,
"narHash": "sha256-aCWC8ngycU7OdJrU2+Je3qf+1a2ykuBvpPhZT/9tXMc=",
"lastModified": 1777944972,
"narHash": "sha256-VfGRo1qTBKOe3s2gOv8LSoA6Fk19PvBlwQ1ECN0Evn8=",
"owner": "Mic92",
"repo": "sops-nix",
"rev": "f1406619a3884cd5c47992a70b8b35c9c0fcb4c9",
"rev": "c591bf665727040c6cc5cb409079acb22dcce33c",
"type": "github"
},
"original": {
@@ -302,6 +285,42 @@
"repo": "sops-nix",
"type": "github"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"vscode-server": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1770124655,
"narHash": "sha256-yHmd2B13EtBUPLJ+x0EaBwNkQr9LTne1arLVxT6hSnY=",
"owner": "nix-community",
"repo": "nixos-vscode-server",
"rev": "92ce71c3ba5a94f854e02d57b14af4997ab54ef0",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nixos-vscode-server",
"type": "github"
}
}
},
"root": "root",
+32 -24
View File
@@ -3,12 +3,15 @@
inputs = {
# Trick renovate into working: "github:NixOS/nixpkgs/nixpkgs-unstable"
nixUnstable.url = "github:nixos/nixpkgs/nixpkgs-unstable";
nixpkgs.url = "github:nixos/nixpkgs/nixos-25.11";
nixpkgs.url = "github:nixos/nixpkgs/nixos-26.05";
hardware.url = "github:nixos/nixos-hardware";
nur.url = "github:nix-community/nur";
nur = {
url = "github:nix-community/nur";
inputs.nixpkgs.follows = "nixpkgs";
};
home-manager = {
url = "github:nix-community/home-manager/release-25.11";
url = "github:nix-community/home-manager/release-26.05";
inputs.nixpkgs.follows = "nixpkgs";
};
@@ -17,40 +20,45 @@
inputs.nixpkgs.follows = "nixpkgs";
};
# hyprland = {
# url = "github:hyprwm/Hyprland";
# inputs.nixpkgs.follows = "nixpkgs";
# };
sops-nix = {
url = "github:Mic92/sops-nix";
inputs.nixpkgs.follows = "nixpkgs";
};
nix-colors.url = "github:misterio77/nix-colors";
nixos-wsl = {
url = "github:nix-community/nixos-wsl";
inputs.nixpkgs.follows = "nixpkgs";
};
arion.url = "github:hercules-ci/arion";
arion.inputs.nixpkgs.follows = "nixpkgs";
vscode-server = {
url = "github:nix-community/nixos-vscode-server";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = inputs:
let gen = import ./generator.nix { inherit inputs; };
let
lib = inputs.nixpkgs.lib;
gen = import ./generator.nix { inherit inputs; };
systemsDir = ./systems;
systemNames = lib.attrNames (lib.filterAttrs
(name: type: type == "directory" && builtins.pathExists (systemsDir + "/${name}/cfg.nix"))
(builtins.readDir systemsDir));
hostsByType = systemType:
lib.filter
(host: (import (systemsDir + "/${host}/cfg.nix")).syscfg.type == systemType)
systemNames;
generateHosts = systemType:
builtins.listToAttrs (map
(host: lib.nameValuePair host (gen.generate { inherit host; }))
(hostsByType systemType));
in {
devShells = import ./shells { inherit inputs; };
nixosConfigurations = {
valinor = gen.generate { host = "valinor"; };
iriy = gen.generate { host = "iriy"; };
efir = gen.generate { host = "efir"; };
avalon = gen.generate { host = "avalon"; };
ci = gen.generate { host = "ci"; };
sandbox = gen.generate { host = "sandbox"; };
gateway = gen.generate { host = "gateway"; };
};
darwinConfigurations = { asgard = gen.generate { host = "asgard"; }; };
homeConfigurations = {
yomi = gen.generate { host = "example"; };
example = gen.generate { host = "example"; };
};
nixosConfigurations = generateHosts "nixos";
darwinConfigurations = generateHosts "macos";
homeConfigurations = generateHosts "home";
};
# ===== Unsupported/NotImplemented ======
+6 -4
View File
@@ -5,7 +5,7 @@
nameValuePair = name: value: { inherit name value; };
in ({
"nixos" = inputs.nixpkgs.lib.nixosSystem {
system = syscfg.syscfg.system;
system = "x86_64-linux";
specialArgs = { inherit inputs; };
modules = [
./modules/shared/syscfg
@@ -13,9 +13,12 @@
./modules/nixos
syscfg
./systems/${host}
inputs.arion.nixosModules.arion
inputs.sops-nix.nixosModules.sops
inputs.home-manager.nixosModules.home-manager
inputs.nixos-wsl.nixosModules.wsl
inputs.vscode-server.nixosModules.default
{
home-manager.useGlobalPkgs = true;
home-manager.useUserPackages = true;
@@ -29,7 +32,6 @@
syscfg
{ usercfg = userConfig; }
inputs.nix-colors.homeManagerModule
# inputs.hyprland.homeManagerModules.default
inputs.sops-nix.homeManagerModules.sops
];
}) syscfg.syscfg.users);
@@ -38,7 +40,7 @@
};
"macos" = inputs.darwin.lib.darwinSystem {
system = syscfg.system;
system = "x86_64-darwin";
modules = [
./modules/shared/syscfg
./modules/shared/sops
+147
View File
@@ -0,0 +1,147 @@
{
"$schema": "https://github.com/fastfetch-cli/fastfetch/raw/dev/doc/json_schema.json",
"logo": {
"type": "builtin", // Logo type: auto, builtin, small, file, etc.
// "source": "arch",
"width": 10,
"height": 10,
"padding": {
"top": 3,
"left": 2,
"right": 2
},
"color": {
"1": "blue",
"2": "white",
"3": "cyan"
}
},
"display": { /* Display settings */},
"modules": [
"break",
{
"type": "custom",
"format": "\u001b[90m┌──────────────────────Hardware──────────────────────┐"
},
{
"type": "host",
"key": "󰌢 PC",
"keyColor": "green",
"format": "{2}"
},
{
"type": "cpu",
"key": "│ ├󱛠 ",
"keyColor": "green",
"format": "{1} | {4} @{6}"
},
{
"type": "gpu",
"key": "│ ├󰍹 ",
"keyColor": "green",
"format": "{2} | {7}"
},
{
"type": "memory",
"key": "│ ├󰑭 ",
"keyColor": "green",
"format": "{2}"
},
// {
// "type": "disk",
// "key": "└ └󰋊 ",
// "keyColor": "green"
// },
{
"type": "custom",
"format": "\u001b[90m└────────────────────────────────────────────────────┘"
},
"break",
{
"type": "custom",
"format": "\u001b[90m┌──────────────────────Software──────────────────────┐"
},
{
"type": "os",
"key": " OS",
"keyColor": "yellow",
"format": " {2} {8}"
},
{
"type": "kernel",
"key": "│ ├󰌽 ",
"keyColor": "yellow",
"format": "{1} {2}"
},
{
"type": "bios",
"key": "│ ├󰖡 ",
"keyColor": "yellow"
},
{
"type": "packages",
"key": "│ ├󰏗 ",
"keyColor": "yellow"
},
{
"type": "de",
"key": "󰧨 DE",
"keyColor": "blue",
"format": "{2} | {3}"
},
{
"type": "lm",
"key": "│ ├󰍁 ",
"keyColor": "blue",
"format": "{1} {2} {3}"
},
{
"type": "wm",
"key": "│ ├󱂬 ",
"keyColor": "blue",
"format": "{2} {5}"
},
{
"type": "custom",
"format": "\u001b[90m└────────────────────────────────────────────────────┘"
},
"break",
{
"type": "custom",
"format": "\u001b[90m┌──────────────────────Age───────────────────────────┐"
},
{
"type": "command",
"key": " OS Age ",
"keyColor": "magenta",
"text": "birth_install=$(stat -c %W /); current=$(date +%s); time_progression=$((current - birth_install)); days_difference=$((time_progression / 86400)); echo $days_difference days"
},
{
"type": "command",
"key": " Update ",
"keyColor": "magenta",
"text": "nixos-rebuild list-generations | awk '$NF == \"True\" {print $2, $3}' | xargs -I {} date -d \"{}\" +\"%s\" | awk '{diff=systime()-$1; printf \"%d days, %d hours, %d mins\\n\", diff/86400, (diff%86400)/3600, (diff%3600)/60}'"
},
{
"type": "uptime",
"key": " Uptime ",
"keyColor": "magenta"
},
{
"type": "custom",
"format": "\u001b[90m└────────────────────────────────────────────────────┘"
},
{
"type": "colors",
"paddingLeft": 2,
"block": {
"width": 3,
"range": [
0,
15
]
} //,
//"symbol": "circle"
},
]
}
+2 -1
View File
@@ -1,4 +1,5 @@
{ pkgs, config, ... }: {
home.packages = with pkgs; [ neofetch ];
home.packages = with pkgs; [ fastfetch ];
xdg.configFile."neofetch/config.conf".source = ./config.conf;
xdg.configFile."fastfetch/config.jsonc".source = ./config.jsonc;
}
-1
View File
@@ -1,6 +1,5 @@
{ lib, config, pkgs, ... }:
let
colorVariant = " black";
gtkThemeFromScheme = import ./gtk-theme-gen.nix { inherit pkgs config; };
wallpaperGen = import ./wallpaper-gen.nix { inherit pkgs config; };
in {
+2 -2
View File
@@ -7,7 +7,7 @@
xdg.userDirs.documents = "${config.home.homeDirectory}/desktop";
xdg.userDirs.download = "${config.home.homeDirectory}/downloads";
xdg.userDirs.extraConfig = {
XDG_MISC_DIR = "${config.home.homeDirectory}/misc";
MISC = "${config.home.homeDirectory}/misc";
};
xdg.userDirs.music = "${config.home.homeDirectory}/media/music";
xdg.userDirs.pictures = "${config.home.homeDirectory}/media/photo";
@@ -15,5 +15,5 @@
xdg.userDirs.templates = "${config.home.homeDirectory}/media/template";
xdg.userDirs.videos = "${config.home.homeDirectory}/media/video";
xdg.userDirs.createDirectories = true;
xdg.userDirs.setSessionVariables = true;
}
+8
View File
@@ -1,3 +1,11 @@
{ ... }: {
imports = [ ./dbus ./fonts ./hw ./locale ./network ./nix ./security ./xdg ];
services.journald.extraConfig = ''
SystemMaxUse=512M
SystemMaxFileSize=64M
MaxRetentionSec=1month
RateLimitIntervalSec=30s
RateLimitBurst=10000
'';
}
+1 -1
View File
@@ -1 +1 @@
{ ... }: { imports = [ ./base ./boot ./fs ./graphics ./power ./udev ./virt ]; }
{ ... }: { imports = [ ./base ./boot ./fs ./graphics ./power ./udev ./virt ./wsl ]; }
+4 -4
View File
@@ -15,10 +15,10 @@
# suspend to RAM (deep) rather than `s2idle`
boot.kernelParams = [ "mem_sleep_default=deep" ];
# suspend-then-hibernate
systemd.sleep.extraConfig = ''
HibernateDelaySec=30m
SuspendState=mem
'';
systemd.sleep.settings.Sleep = {
HibernateDelaySec = "30m";
SuspendState = "mem";
};
services.logind.settings.Login.HandleLidSwitch = "suspend-then-hibernate";
# Hibernate on power button pressed
+2 -2
View File
@@ -1,8 +1,8 @@
{ ... }: {
{ pkgs, ... }: {
systemd.services.systemd-udevd.restartIfChanged = false;
services.udev = {
packages = [ ];
packages = with pkgs; [ android-udev-rules ];
extraRules = ''
SUBSYSTEM=="usb", ATTRS{idVendor}=="2104", ATTRS{idProduct}=="0127", GROUP="plugdev", TAG+="uaccess"
SUBSYSTEM=="usb", ATTRS{idVendor}=="2104", ATTRS{idProduct}=="0118", GROUP="plugdev", TAG+="uaccess"
+1
View File
@@ -18,5 +18,6 @@
};
};
};
virtualisation.containers.registries.search = [ "quay.io" "docker.io" "ghcr.io" ];
};
}
+13
View File
@@ -0,0 +1,13 @@
{ lib, config, pkgs, ... }: {
config = lib.mkIf (config.syscfg.extra.wsl) {
wsl.enable = true;
wsl.defaultUser = config.syscfg.defaultUser;
wsl.extraBin = with pkgs; [
{ src = "${coreutils}/bin/uname"; }
{ src = "${coreutils}/bin/dirname"; }
{ src = "${coreutils}/bin/readlink"; }
];
wsl.wslConf.network.generateHosts = false;
};
}
+15 -3
View File
@@ -1,17 +1,29 @@
{ config, ... }: {
{ lib, config, ... }: {
networking = {
hostName = config.syscfg.hostname;
useDHCP = true;
nameservers = [ "1.1.1.1" "9.9.9.9" ];
extraHosts = ''
${lib.concatStringsSep "\n" config.syscfg.extra.hosts}
'';
proxy = lib.mkIf (config.syscfg.extra.proxy.domain != "") {
default = "http://${config.syscfg.extra.proxy.domain}:${config.syscfg.extra.proxy.port or "8080"}";
noProxy = "${config.syscfg.extra.proxy.noProxy}";
};
firewall = {
enable = true;
allowedUDPPorts =
(if config.syscfg.server ? wireguard then [ 1515 ] else [ ]) ++
(if (config.syscfg.server != false && config.syscfg.server.wireguard) then [ 1515 ] else [ ]) ++
(if (config.syscfg.server != false && config.syscfg.server.web) then [ 80 443 22 ] else [ ]) ++
[ ];
allowedTCPPorts =
(if config.syscfg.server ? web then [ 80 443 22 ] else [ ]) ++
(if (config.syscfg.server != false && config.syscfg.server.web) then [ 80 443 22 ] else [ ]) ++
(if (config.syscfg.server != false) then [ 5432 6379 8181 ] else [ ]) ++
[ ];
};
};
+7
View File
@@ -37,5 +37,12 @@
];
};
};
programs.nix-ld = {
enable = true;
libraries = with pkgs; [
libx11 libxcb libxi libxext libxkbfile xcbutilcursor
libpng libdrm libpulseaudio nss nspr expat libbsd
];
};
system.stateVersion = "24.11";
}
+2 -5
View File
@@ -1,12 +1,9 @@
{ pkgs, config, lib, ... }: {
config = lib.mkIf (config.syscfg.make.develop) {
programs.adb.enable = true;
# services.udev.packages = [
# pkgs.android-udev-rules
# ];
programs.wireshark.enable = true;
environment.systemPackages = with pkgs; [ wget dconf wireshark ];
environment.systemPackages = with pkgs; [ wget dconf wireshark mtr android-tools ];
};
}
+6
View File
@@ -9,6 +9,12 @@ in {
imports = [ ./ollama ];
config = lib.mkIf (config.syscfg.make.develop) {
services.vscode-server = lib.mkIf (config.syscfg.extra.wsl) {
enable = true;
enableFHS = true;
};
environment.systemPackages = with pkgs;
[
# android-tools
@@ -1,14 +1,13 @@
{ lib, config, pkgs, ... }:
let
ollamaPkg = pkgs.ollama-rocm;
ollamaPkg = pkgs.ollama-vulkan;
in{
config = lib.mkIf (config.syscfg.make.develop) {
services.ollama = {
enable = true;
package = ollamaPkg;
acceleration = "rocm";
loadModels = [ "deepseek-v2:lite" "qwen2.5-coder:7b" "qwen2.5-coder:1.5b" ];
loadModels = [ ];
syncModels = true;
};
environment.systemPackages = with pkgs; [ ollamaPkg ];
@@ -0,0 +1,54 @@
{ config, containerCfg, pkgs, lib, builder, name,... }:
let
version = "latest";
serverCfg = config.syscfg.server;
image = pkgs.dockerTools.streamLayeredImage {
name = "EXAMPLE";
tag = "0.0.0";
contents = [ pkgs.bashInteractive ];
config = {
Entrypoint = [ "echo 1" ];
ExposedPorts = { };
};
};
settings = pkgs.writeText "settings.yaml" ...;
templateData = builder.mkData { name = "template"; dir = "template"; vars = {
_ARGUMENT = "template";
};
};
in {
requires = {
secrets = [ ];
databases = [ ];
};
runtime = {
paths = [{
path="${serverCfg.path.config}/example/";
mode = "0444";
}];
containers = {
server = builder.mkContainer {
subdomain = containerCfg.subdomain;
# imageStream = image;
image = "....:${version}";
port = 8080;
secret = name;
extraEnv = { };
overrides = {
cmd = [ ];
volumes = [ ];
};
};
};
setup = {
trigger = "server";
envFile = config.sops.secrets."EXAMPLE".path;
script = pkgs.writeShellScript "setup" ''
...
'';
};
};
}
+8
View File
@@ -0,0 +1,8 @@
# Missing
RSS: TTRSS / FreshRSS
Monitoring: Telegraf + InfluxDB
https://github.com/tarampampam/error-pages ?
kavita + mylar ? kapowarr ?
- Transmission Cfg and API/Token handling
@@ -0,0 +1,139 @@
{ config, containerCfg, pkgs, lib, builder, name, ... }:
let
version = "2026.2.2";
serverCfg = config.syscfg.server;
mediaCfg = config.syscfg.media;
authentikBackground = if mediaCfg.banner.png != null then mediaCfg.banner.png else mediaCfg.bg;
logoSvgFileName = builtins.baseNameOf (toString mediaCfg.logo.svg);
logoIcoFileName = builtins.baseNameOf (toString mediaCfg.logo.ico);
backgroundFileName = builtins.baseNameOf (toString authentikBackground);
logoSvgMount = "/media/custom/${logoSvgFileName}";
logoIcoMount = "/media/custom/${logoIcoFileName}";
backgroundMount = "/media/custom/${backgroundFileName}";
authentikData = builder.mkData {
name = "authentik"; dir = "authentik"; vars = {
AUTHENTIK_DOMAIN = "${containerCfg.subdomain}.${serverCfg.domain}";
COOKIE_DOMAIN = "${serverCfg.domain}";
AUTHENTIK_LDAP_DC_DOMAIN = "dc=ldap," + (lib.concatMapStringsSep "," (x: "dc=${x}") (lib.splitString "." serverCfg.domain));
AUTHENTIK_BRANDING_TITLE = if containerCfg.extra ? name then containerCfg.extra.name else "authentik";
AUTHENTIK_BRANDING_LOGO = "custom/${logoSvgFileName}";
AUTHENTIK_BRANDING_FAVICON = "custom/${logoIcoFileName}";
AUTHENTIK_BRANDING_BACKGROUND = "custom/${backgroundFileName}";
}
// (if serverCfg.containers?jellyfin then { JELLYFIN_DOMAIN = "${serverCfg.containers.jellyfin.subdomain}.${serverCfg.domain}";} else {})
// (if serverCfg.containers?gitea then { GITEA_DOMAIN = "${serverCfg.containers.gitea.subdomain}.${serverCfg.domain}";} else {})
// (if serverCfg.containers?immich then { IMMICH_DOMAIN = "${serverCfg.containers.immich.subdomain}.${serverCfg.domain}";} else {})
// (if serverCfg.containers?freshrss then { FRESHRSS_DOMAIN = "${serverCfg.containers.freshrss.subdomain}.${serverCfg.domain}";} else {})
// (if serverCfg.containers?homepage then { HOMEPAGE_DOMAIN = "${serverCfg.containers.homepage.subdomain}.${serverCfg.domain}";} else {})
// (if serverCfg.containers?nextcloud then { NEXTCLOUD_DOMAIN = "${serverCfg.containers.nextcloud.subdomain}.${serverCfg.domain}";} else {});
};
in {
requires = {
secrets = [ name ];
databases = [ name ];
};
runtime = {
paths = [{
path="${serverCfg.path.config}/authentik";
owner = "1000:1000";
dirs = ["media" "templates"];
mode = "0755";
}];
containers = {
server = builder.mkContainer {
subdomain = containerCfg.subdomain;
image = "ghcr.io/goauthentik/server:${version}";
port = 9000;
secret = name;
extraEnv = {
AUTHENTIK_REDIS__HOST = builder.host;
AUTHENTIK_POSTGRESQL__HOST = builder.host;
AUTHENTIK_POSTGRESQL__USER = "authentik_user";
AUTHENTIK_POSTGRESQL__NAME = "authentik_db";
AUTHENTIK_POSAUTHENTIK_POSTGRESQL__SSLMODE = "false";
AUTHENTIK_DISABLE_UPDATE_CHECK = "true";
AUTHENTIK_POSTGRESQL__SSLMODE = "disable";
} // lib.optionalAttrs (serverCfg.mail.server != null) {
AUTHENTIK_EMAIL__HOST = serverCfg.mail.server;
AUTHENTIK_EMAIL__PORT = "587";
AUTHENTIK_EMAIL__USERNAME = "noreply@${serverCfg.domain}";
AUTHENTIK_EMAIL__USE_TLS = "true";
AUTHENTIK_EMAIL__USE_SSL = "false";
AUTHENTIK_EMAIL__TIMEOUT = "10";
AUTHENTIK_EMAIL__FROM = "sso@noreply.${serverCfg.domain}";
};
overrides = {
environmentFiles = [ config.sops.secrets."AUTHENTIK".path config.sops.secrets."CUSTOM".path ] ;
cmd = [ "server" ];
volumes = [
"${serverCfg.path.config}/authentik/media:/media"
"${serverCfg.path.config}/authentik/templates:/templates"
"${authentikData}:/blueprints/custom:ro"
"${mediaCfg.logo.svg}:${logoSvgMount}:ro"
"${mediaCfg.logo.ico}:${logoIcoMount}:ro"
"${authentikBackground}:${backgroundMount}:ro"
];
};
};
worker = builder.mkContainer {
image = "ghcr.io/goauthentik/server:${version}";
secret = name;
extraEnv = {
AUTHENTIK_REDIS__HOST = builder.host;
AUTHENTIK_POSTGRESQL__HOST = builder.host;
AUTHENTIK_POSTGRESQL__USER = "authentik_user";
AUTHENTIK_POSTGRESQL__NAME = "authentik_db";
AUTHENTIK_POSAUTHENTIK_POSTGRESQL__SSLMODE = "false";
AUTHENTIK_DISABLE_UPDATE_CHECK = "true";
AUTHENTIK_POSTGRESQL__SSLMODE = "disable";
};
overrides = {
cmd = [ "worker" ];
volumes = [
"${serverCfg.path.config}/authentik/media:/media"
"${serverCfg.path.config}/authentik/templates:/templates"
"${authentikData}:/blueprints/custom:ro"
"${mediaCfg.logo.svg}:${logoSvgMount}:ro"
"${mediaCfg.logo.ico}:${logoIcoMount}:ro"
"${authentikBackground}:${backgroundMount}:ro"
];
};
};
ldap = builder.mkContainer {
image = "ghcr.io/goauthentik/ldap:${version}";
secret = name;
extraEnv = {
AUTHENTIK_HOST = "https://${containerCfg.subdomain}.${serverCfg.domain}";
AUTHENTIK_INSECURE = "false";
};
};
};
setup = {
trigger = "worker";
script = pkgs.writeShellScript "setup" ''
# Define the command wrapper
AK="${pkgs.podman}/bin/podman --events-backend=none exec --env-file ${config.sops.secrets."CUSTOM".path} -e DOMAIN=${serverCfg.domain} -u root authentik-worker ak"
$AK apply_blueprint /blueprints/custom/authentik.yaml
$AK apply_blueprint /blueprints/custom/branding.yaml
$AK apply_blueprint /blueprints/custom/traefik.yaml
$AK apply_blueprint /blueprints/custom/ldap.yaml
${lib.optionalString (serverCfg.containers ? gitea) ''$AK apply_blueprint /blueprints/custom/gitea.yaml''}
${lib.optionalString (serverCfg.containers ? jellyfin) ''$AK apply_blueprint /blueprints/custom/jellyfin.yaml''}
${lib.optionalString (serverCfg.containers ? nextcloud) ''$AK apply_blueprint /blueprints/custom/nextcloud.yaml''}
${lib.optionalString (serverCfg.containers ? immich) ''$AK apply_blueprint /blueprints/custom/immich.yaml''}
${lib.optionalString (serverCfg.containers ? freshrss) ''$AK apply_blueprint /blueprints/custom/freshrss.yaml''}
${lib.optionalString (serverCfg.containers ? homepage) ''$AK apply_blueprint /blueprints/custom/homepage.yaml''}
echo "Completed Authentik Setup"
'';
};
};
}
@@ -0,0 +1,42 @@
{ config, containerCfg, pkgs, lib, builder, name,... }:
let
version = "latest";
serverCfg = config.syscfg.server;
in {
runtime = {
containers = {
server = builder.mkContainer {
subdomain = containerCfg.subdomain;
image = "crocodilestick/calibre-web-automated:${version}";
port = 8083;
# secret = name;
extraEnv = {
CWA_PORT_OVERRIDE = "8083";
PUID = "1000";
PGID = "1000";
#HARDCOVER_TOKEN= ....
TRUSTED_PROXY_COUNT= "1";
};
extraLabels = {
"traefik.http.routers.${containerCfg.subdomain}-login.rule" = "Host(`${containerCfg.subdomain}.${serverCfg.domain}`)";
"traefik.http.routers.${containerCfg.subdomain}-login.middlewares" = if (serverCfg.containers?authentik) then "authentik" else "";
"traefik.http.routers.${containerCfg.subdomain}-login.priority" = "100";
"traefik.http.routers.${containerCfg.subdomain}-login.entrypoints" = "web-secure";
"traefik.http.routers.${containerCfg.subdomain}-login.tls" = "true";
};
overrides = {
volumes = [
"${serverCfg.path.book}:/calibre-library"
"${serverCfg.path.download}/book:/cwa-book-ingest"
];
};
};
};
};
# curl 'https://books.test.helcel.net/admin/ajaxconfig' \
# -X POST
# -H 'Content-Type: application/x-www-form-urlencoded; charset=UTF-8'
# --data-raw 'csrf_token=${CSRF_TOKEN}&config_certfile=&config_keyfile=&config_updatechannel=0&config_trustedhosts=&config_log_level=20&config_logfile=%2Fdev%2Fstdout&config_access_logfile=%2Fconfig%2Faccess.log&config_embed_metadata=on&config_uploading=on&config_upload_formats=m4b%2Cacsm%2Cdoc%2Cpdf%2Cmp3%2Codt%2Ccbr%2Crtf%2Clit%2Cprc%2Cm4a%2Cdjv%2Cfb2%2Copus%2Cdocx%2Cazw3%2Cepub%2Cdjvu%2Cwav%2Ccb7%2Ccbz%2Cmp4%2Ckfx-zip%2Cmobi%2Ccbt%2Cogg%2Ckfx%2Ckepub%2Ctxt%2Cazw%2Chtml%2Cflac&config_external_port=8083&config_goodreads_api_key=&config_hardcover_token=&config_use_https=on&config_reverse_proxy_login_header_name=&config_login_type=1&config_ldap_provider_url=sso.test.helcel.net&config_ldap_port=389&config_ldap_encryption=0&config_ldap_cacert_path=&config_ldap_cert_path=&config_ldap_key_path=&config_ldap_authentication=2&config_ldap_serv_username=cn%3Dldap-service%2Cou%3Dusers%2C%24%7BLDAP_DC_DOMAIN%7D&config_ldap_serv_password_e=%24DEFAULT_LDAP_PASSWORD&config_ldap_dn=%24%7BLDAP_DC_DOMAIN%7D&config_ldap_user_object=(memberOf%3Dcn%3Dcloud%2Cou%3Dgroups%2C%24%7BLDAP_DC_DOMAIN%7D)&config_ldap_openldap=on&config_ldap_auto_create_users=on&config_ldap_group_object_filter=(memberOf%3Dcn%3Dcloud%2Cou%3Dgroups%2C%24%7BLDAP_DC_DOMAIN%7D)&config_ldap_group_name=cloud&config_ldap_group_members_field=memberUid&ldap_import_user_filter=0&config_ldap_member_user_object=&config_generic_oauth_metadata_url=&config_generic_oauth_server_url=&config_generic_oauth_auth_url=&config_generic_oauth_token_url=&config_generic_oauth_userinfo_url=&config_generic_oauth_scope=email+openid+profile&config_oauth_redirect_host=&config_generic_oauth_client_id=&config_generic_oauth_client_secret=&config_generic_oauth_username_mapper=preferred_username&config_generic_oauth_email_mapper=email&config_generic_oauth_admin_group=admin&config_generic_oauth_login_button=OpenID+Connect&config_1_oauth_client_id=&config_1_oauth_client_secret=&config_2_oauth_client_id=&config_2_oauth_client_secret=&config_binariesdir=%2Fusr%2Fbin&config_calibre=&config_kepubifypath=%2Fusr%2Fbin%2Fkepubify&config_rarfile_location=%2Fusr%2Fbin%2Funrar&config_enable_oauth_group_admin_management=on&config_ratelimiter=on&config_limiter_uri=&config_limiter_options=&config_check_extensions=on&config_session=1&config_password_policy=on&config_password_min_length=8&config_password_number=on&config_password_lower=on&config_password_upper=on&config_password_character=on&config_password_special=on'
}
@@ -0,0 +1,37 @@
{ config, containerCfg, pkgs, lib, builder, name, ... }:
let
version = "latest";
serverCfg = config.syscfg.server;
in {
requires.secrets = [ name ];
runtime = {
containers = {
server = builder.mkContainer {
subdomain = containerCfg.subdomain;
image = "collabora/code:${version}";
port = 9980;
secret = name;
extraEnv = {
"aliasgroup1" = "https://${serverCfg.containers.nextcloud.subdomain}.${serverCfg.domain}";
"server_name" = "${containerCfg.subdomain}.${serverCfg.domain}";
"username" = "collabora_user";
"VIRTUAL_HOST" = "${containerCfg.subdomain}.${serverCfg.domain}";
"VIRTUAL_PORT" = "9980";
"VIRTUAL_PROTO" = "http";
"DONT_GEN_SSL_CERT" = "true";
"RESOLVE_TO_PROXY_IP" = "true";
"extra_params" = "--o:ssl.enable=false --o:ssl.termination=true";
"dictionaries" = "en fr de jp no";
};
overrides = {
volumes = [
"${pkgs.noto-fonts}/share/fonts/noto:/opt/collaboraoffice/share/fonts/truetype/noto:ro"
"${pkgs.ibm-plex}/share/fonts/opentype:/opt/collaboraoffice/share/fonts/opentype/plex:ro"
];
};
};
};
};
}
@@ -0,0 +1,42 @@
{ config, containerCfg, pkgs, lib, builder, name,... }:
let
serverCfg = config.syscfg.server;
ethercalc_exe = pkgs.ethercalc;
image = pkgs.dockerTools.streamLayeredImage {
name = "ethercalc";
tag = ethercalc_exe.version;
contents = [ pkgs.bashInteractive ];
config = {
Entrypoint = [ "${ethercalc_exe}/bin/ethercalc" ];
ExposedPorts = { "8080/tcp" = {}; };
};
};
in {
requires.secrets = [ name ];
runtime = {
paths = [{
path="${serverCfg.path.data}/ethercalc/";
mode = "0666";
}];
containers = {
server = builder.mkContainer {
subdomain = containerCfg.subdomain;
imageStream = image;
port = 8080;
secret = name;
extraEnv = {
ETHERCALC_PORT = "8080";
#CONNECT TO REDIS
};
overrides = {
volumes = [
"${serverCfg.path.data}/ethercalc:/data"
];
};
};
};
};
}
+129
View File
@@ -0,0 +1,129 @@
{ config, containerCfg, pkgs, lib, builder, name,... }:
let
serverCfg = config.syscfg.server;
etherpad_exe = pkgs.etherpad-lite;
settings = pkgs.writeText"settings.json" (builtins.toJSON {
title= "\${TITLE:Etherpad}";
showRecentPads = "\${SHOW_RECENT_PADS:true}";
favicon = "\${FAVICON:null}";
publicURL = "\${PUBLIC_URL:null}";
skinName = "\${SKIN_NAME:colibris}";
skinVariants = "\${SKIN_VARIANTS:super-light-toolbar super-light-editor light-background}";
ip = "\${IP:0.0.0.0}";
port = "\${PORT:9001}";
showSettingsInAdminPage = "\${SHOW_SETTINGS_IN_ADMIN_PAGE:true}";
enableMetrics = "\${ENABLE_METRICS:true}";
updates.tier = "off";
cleanup.enabled = false;
gdprAuthorErasure.enabled = "\${GDPR_AUTHOR_ERASURE_ENABLED:false}";
authenticationMethod = "\${AUTHENTICATION_METHOD:apikey}";
enableDarkMode = "\${ENABLE_DARK_MODE:true}";
enablePadWideSettings = "\${ENABLE_PAD_WIDE_SETTINGS:true}";
dbType = "\${DB_TYPE:dirty}";
dbSettings = {
host = "\${DB_HOST:undefined}";
port = "\${DB_PORT:undefined}";
database = "\${DB_NAME:undefined}";
user = "\${DB_USER:undefined}";
password = "\${DB_PASS:undefined}";
charset = "\${DB_CHARSET:undefined}";
filename = "\${DB_FILENAME:var/dirty.db}";
collection = "\${DB_COLLECTION:undefined}";
url = "\${DB_URL:undefined}";
};
defaultPadText = "\${DEFAULT_PAD_TEXT:P A D}";
padOptions = {
noColors = "\${PAD_OPTIONS_NO_COLORS:false}";
showControls = "\${PAD_OPTIONS_SHOW_CONTROLS:true}";
showChat = "\${PAD_OPTIONS_SHOW_CHAT:true}";
showLineNumbers = "\${PAD_OPTIONS_SHOW_LINE_NUMBERS:true}";
useMonospaceFont = "\${PAD_OPTIONS_USE_MONOSPACE_FONT:false}";
userName = "\${PAD_OPTIONS_USER_NAME:null}";
userColor = "\${PAD_OPTIONS_USER_COLOR:null}";
rtl = "\${PAD_OPTIONS_RTL:false}";
alwaysShowChat = "\${PAD_OPTIONS_ALWAYS_SHOW_CHAT:false}";
chatAndUsers = "\${PAD_OPTIONS_CHAT_AND_USERS:false}";
lang = "\${PAD_OPTIONS_LANG:null}";
fadeInactiveAuthorColors = "\${PAD_OPTIONS_FADE_INACTIVE_AUTHOR_COLORS:true}";
enforceReadableAuthorColors = "\${PAD_OPTIONS_ENFORCE_READABLE_AUTHOR_COLORS:true}";
};
requireSession = "\${REQUIRE_SESSION:false}";
editOnly = "\${EDIT_ONLY:false}";
minify = "\${MINIFY:true}";
requireAuthentication = "\${REQUIRE_AUTHENTICATION:false}";
requireAuthorization = "\${REQUIRE_AUTHORIZATION:false}";
trustProxy = "\${TRUST_PROXY:true}";
ep_headerauth.username_header = "X-authentik-username";
users.admin = {
password = "\${ADMIN_PASSWORD:null}";
is_admin = true;
};
socketTransportProtocols = ["websocket" "polling"];
socketIo.maxHttpBufferSize = "\${SOCKETIO_MAX_HTTP_BUFFER_SIZE:1000000}";
indentationOnNewLine = true;
loglevel = "\${LOGLEVEL:INFO}";
lowerCasePadIds = "\${LOWER_CASE_PAD_IDS:true}";
});
image = pkgs.dockerTools.streamLayeredImage {
name = "etherpad";
tag = etherpad_exe.version;
contents = [ pkgs.bashInteractive ];
config = {
Entrypoint = [ "${etherpad_exe}/bin/etherpad-lite" ];
ExposedPorts = { "8080/tcp" = {}; };
};
};
in {
requires = {
secrets = [ name ];
databases = [ name ];
};
runtime = {
paths = [{
path="${serverCfg.path.config}/etherpad/";
mode = "0444";
}];
containers = {
server = builder.mkContainer {
subdomain = containerCfg.subdomain;
imageStream = image;
port = 8080;
secret = name;
extraEnv = {
TITLE = "Pad";
PORT ="8080";
DB_TYPE = "postgres";
DB_HOST = builder.host;
DB_NAME = "etherpad_db";
DB_USER = "etherpad_user";
TRUST_PROXY = "true";
DB_CHARSET = "utf8mb4";
DEFAULT_PAD_TEXT = "";
PAD_OPTIONS_SHOW_LINE_NUMBERS = "true";
PAD_OPTIONS_USE_MONOSPACE_FONT = "true";
SKIN_VARIANTS = "super-dark-toolbar light-editor dark-background";
};
overrides = {
cmd = [ "--settings" "/etc/etherpad/settings.json" "--apikey" "/etc/etherpad/APIKEY.txt" ];
volumes = [
"${settings}:/etc/etherpad/settings.json"
"${serverCfg.path.config}/etherpad/APIKEY.txt:/etc/etherpad/APIKEY.txt:ro"
];
};
};
};
setup = {
trigger = "server";
envFile = config.sops.secrets."ETHERPAD".path;
script = pkgs.writeShellScript "setup" ''
echo "$APIKEY" > ${serverCfg.path.config}/etherpad/APIKEY.txt
chmod 444 ${serverCfg.path.config}/etherpad/APIKEY.txt
'';
};
};
}
+301
View File
@@ -0,0 +1,301 @@
{ config, containerCfg, pkgs, lib, builder, name, ... }:
let
serverCfg = config.syscfg.server;
mediaCfg = config.syscfg.media;
port = 8080;
cacheControl = containerCfg.extra.cacheControl or "public, max-age=86400";
priority = toString (containerCfg.extra.priority or 1000);
logoSvgFileName = builtins.baseNameOf (toString mediaCfg.logo.svg);
logoSvgMount = "/assets/${logoSvgFileName}";
configFile = pkgs.writeText "favicon-config.json" (builtins.toJSON {
inherit cacheControl;
mappings = containerCfg.extra.mappings or {};
default = containerCfg.extra.default or null;
logoScale = containerCfg.extra.logoScale or 0.72;
});
pythonEnv = pkgs.python3.withPackages (ps: with ps; [
cairosvg
pillow
]);
serverScript = pkgs.writeText "favicon-server.py" ''
import base64
import hashlib
from io import BytesIO
import json
import os
import re
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from pathlib import Path
from urllib.parse import urlsplit
import cairosvg
from PIL import Image
CONFIG_PATH = os.environ.get("FAVICON_CONFIG", "/config/config.json")
LOGO_PATH = os.environ.get("FAVICON_LOGO", "/assets/logo.svg")
CACHE_DIR = Path(os.environ.get("FAVICON_CACHE_DIR", "/cache"))
LISTEN_HOST = os.environ.get("FAVICON_LISTEN_HOST", "0.0.0.0")
LISTEN_PORT = int(os.environ.get("FAVICON_PORT", "8080"))
DEFAULT_CACHE_CONTROL = "public, max-age=86400"
PATH_PATTERN = re.compile(
r"(?i)(?:^|/)(?:"
r"favicon(?:-[0-9]+x[0-9]+)?\.(?:ico|png|svg)"
r"|apple-touch-icon(?:-precomposed)?\.png"
r"|android-chrome-(?:192x192|512x512)\.png"
r"|mstile-150x150\.png"
r")$"
)
with open(CONFIG_PATH, "r", encoding="utf-8") as fh:
APP_CONFIG = json.load(fh)
with open(LOGO_PATH, "rb") as fh:
LOGO_BYTES = fh.read()
LOGO_DATA_URI = "data:image/svg+xml;base64," + base64.b64encode(LOGO_BYTES).decode("ascii")
def _normalize_host(host):
return (host or "").split(":", 1)[0].strip().lower()
def _pick_profile(host):
mappings = APP_CONFIG.get("mappings", {})
exact = mappings.get(host)
if exact is not None:
return exact
wildcard_matches = []
for pattern, profile in mappings.items():
if pattern.startswith("*.") and host.endswith(pattern[1:].lower()):
wildcard_matches.append((pattern.count("."), profile))
if wildcard_matches:
wildcard_matches.sort(key=lambda item: item[0], reverse=True)
return wildcard_matches[0][1]
return APP_CONFIG.get("default")
def _color(value, fallback):
return value if isinstance(value, str) and value else fallback
def _badge_text(profile):
badge = profile.get("icon") or profile.get("label") or profile.get("text") or ""
badge = str(badge).strip()
if not badge:
return ""
return badge[:3].upper()
def _logo_scale():
scale = APP_CONFIG.get("logoScale", 0.72)
try:
scale = float(scale)
except (TypeError, ValueError):
return 0.72
return max(0.2, min(0.9, scale))
def _render_svg(profile):
bg = _color(profile.get("background"), "#111827")
fg = _color(profile.get("foreground"), "#f8fafc")
accent = _color(profile.get("accent"), "#38bdf8")
badge = _badge_text(profile)
canvas = 256
panel = 188
panel_offset = (canvas - panel) // 2
logo_size = int(canvas * _logo_scale())
logo_offset = (canvas - logo_size) // 2
badge_svg = ""
if badge:
badge_svg = f"""
<g>
<circle cx="196" cy="60" r="34" fill="{accent}" />
<text x="196" y="72"
font-family="DejaVu Sans, sans-serif"
font-size="34"
font-weight="700"
text-anchor="middle"
fill="{fg}">{badge}</text>
</g>
"""
return f"""<svg xmlns="http://www.w3.org/2000/svg" width="{canvas}" height="{canvas}" viewBox="0 0 {canvas} {canvas}">
<defs>
<radialGradient id="glow" cx="84%" cy="16%" r="75%">
<stop offset="0%" stop-color="{accent}" stop-opacity="0.34" />
<stop offset="100%" stop-color="{accent}" stop-opacity="0" />
</radialGradient>
</defs>
<rect width="{canvas}" height="{canvas}" rx="56" fill="{bg}" />
<rect width="{canvas}" height="{canvas}" rx="56" fill="url(#glow)" />
<rect x="{panel_offset}" y="{panel_offset}" width="{panel}" height="{panel}" rx="44" fill="#ffffff" fill-opacity="0.08" stroke="{accent}" stroke-opacity="0.6" stroke-width="6" />
<rect x="42" y="214" width="172" height="10" rx="5" fill="{accent}" fill-opacity="0.9" />
<image href="{LOGO_DATA_URI}" x="{logo_offset}" y="{logo_offset}" width="{logo_size}" height="{logo_size}" preserveAspectRatio="xMidYMid meet" />
{badge_svg}
</svg>"""
def _target_spec(path):
name = Path(path).name.lower()
if name.endswith(".svg"):
return ("svg", None)
if name.endswith(".ico"):
return ("ico", 64)
known_sizes = {
"apple-touch-icon.png": 180,
"apple-touch-icon-precomposed.png": 180,
"android-chrome-192x192.png": 192,
"android-chrome-512x512.png": 512,
"mstile-150x150.png": 150,
"favicon.png": 256,
}
if name in known_sizes:
return ("png", known_sizes[name])
match = re.search(r"([0-9]{2,4})x([0-9]{2,4})", name)
if match:
return ("png", int(match.group(1)))
return ("png", 256)
def _cache_key(host, spec, profile):
payload = json.dumps(
{"host": host, "spec": spec, "profile": profile, "logo": hashlib.sha256(LOGO_BYTES).hexdigest()},
sort_keys=True,
).encode("utf-8")
return hashlib.sha256(payload).hexdigest()
def _mime(ext):
return {
"svg": "image/svg+xml",
"png": "image/png",
"ico": "image/x-icon",
}[ext]
def _generate_asset(host, profile, path):
spec = _target_spec(path)
cache_name = f"{_cache_key(host, spec, profile)}.{spec[0]}"
target = CACHE_DIR / cache_name
if target.exists():
return target, _mime(spec[0])
CACHE_DIR.mkdir(parents=True, exist_ok=True)
svg = _render_svg(profile).encode("utf-8")
if spec[0] == "svg":
target.write_bytes(svg)
return target, _mime("svg")
png_bytes = cairosvg.svg2png(bytestring=svg, output_width=spec[1], output_height=spec[1])
if spec[0] == "png":
target.write_bytes(png_bytes)
return target, _mime("png")
image = Image.open(BytesIO(png_bytes))
image.save(target, format="ICO", sizes=[(64, 64), (48, 48), (32, 32), (16, 16)])
image.close()
return target, _mime("ico")
class Handler(BaseHTTPRequestHandler):
server_version = "favicon-router/1.0"
def _serve(self, include_body):
path = urlsplit(self.path).path
if not PATH_PATTERN.search(path):
self.send_error(404)
return
host = _normalize_host(self.headers.get("Host", ""))
profile = _pick_profile(host)
if not profile:
self.send_error(404, "No favicon mapping for host")
return
asset_path, content_type = _generate_asset(host, profile, path)
payload = asset_path.read_bytes()
self.send_response(200)
self.send_header("Content-Type", content_type)
self.send_header("Content-Length", str(len(payload)))
self.send_header("Cache-Control", APP_CONFIG.get("cacheControl", DEFAULT_CACHE_CONTROL))
self.end_headers()
if include_body:
self.wfile.write(payload)
def do_GET(self):
self._serve(include_body=True)
def do_HEAD(self):
self._serve(include_body=False)
def log_message(self, fmt, *args):
print("%s - - [%s] %s" % (self.address_string(), self.log_date_time_string(), fmt % args))
if __name__ == "__main__":
httpd = ThreadingHTTPServer((LISTEN_HOST, LISTEN_PORT), Handler)
httpd.serve_forever()
'';
image = pkgs.dockerTools.streamLayeredImage {
name = "favicon";
tag = "1";
contents = [
pythonEnv
pkgs.fontconfig
pkgs.dejavu_fonts
pkgs.cacert
pkgs.tzdata
];
config = {
Entrypoint = [ "${pythonEnv}/bin/python3" serverScript ];
ExposedPorts = { "${toString port}/tcp" = { }; };
WorkingDir = "/";
};
};
in {
runtime = {
paths = [
{
path = "${serverCfg.path.config}/favicon";
mode = "0755";
dirs = [ "cache" ];
}
];
containers = {
server = builder.mkContainer {
imageStream = image;
port = port;
extraEnv = {
FAVICON_CONFIG = "/config/config.json";
FAVICON_LOGO = logoSvgMount;
FAVICON_CACHE_DIR = "/cache";
FAVICON_PORT = toString port;
FONTCONFIG_FILE = "${pkgs.fontconfig.out}/etc/fonts/fonts.conf";
FONTCONFIG_PATH = "${pkgs.fontconfig.out}/etc/fonts";
};
extraLabels = {
"traefik.enable" = "true";
"traefik.http.routers.${name}.entrypoints" = "web-secure";
"traefik.http.routers.${name}.rule" = "HostRegexp(`{host:.+}`) && PathRegexp(`^/(?:.*\\/)?(?:favicon(?:-[0-9]+x[0-9]+)?\\.(?:ico|png|svg)|apple-touch-icon(?:-precomposed)?\\.png|android-chrome-(?:192x192|512x512)\\.png|mstile-150x150\\.png)$`)";
"traefik.http.routers.${name}.priority" = priority;
"traefik.http.routers.${name}.tls" = "true";
"traefik.http.services.${name}.loadbalancer.server.port" = toString port;
};
overrides = {
volumes = [
"${configFile}:/config/config.json:ro"
"${serverCfg.path.config}/favicon/cache:/cache"
"${mediaCfg.logo.svg}:${logoSvgMount}:ro"
];
};
};
};
};
}
@@ -0,0 +1,61 @@
{ config, containerCfg, pkgs, lib, builder, name, ... }:
let
version = "latest";
serverCfg = config.syscfg.server;
in {
requires = {
secrets = [ name ];
databases = [ name ];
};
runtime = {
paths = [
{
path = "${serverCfg.path.config}/freshrss";
owner = "1000:1000";
mode = "0755";
}
];
containers = {
server = builder.mkContainer {
subdomain = containerCfg.subdomain;
image = "ghcr.io/freshrss/freshrss:${version}";
port = 80;
extraEnv = {
CRON_MIN = "5,35";
TRUSTED_PROXY = "10.0.0.0/8 192.168.0.1/16";
LISTEN = "80";
OIDC_ENABLED = "1";
OIDC_PROVIDER_METADATA_URL = "https://${serverCfg.containers.authentik.subdomain}.${serverCfg.domain}/application/o/freshrss/.well-known/openid-configuration";
OIDC_REMOTE_USER_CLAIM = "preferred_username";
OIDC_CLIENT_ID = "freshrss";
OIDC_SCOPES = "openid profile";
OIDC_X_FORWARDED_HEADERS = "X-Forwarded-Host X-Forwarded-Port X-Forwarded-Proto";
};
overrides = {
environmentFiles = [ config.sops.secrets."FRESHRSS".path config.sops.secrets."CUSTOM".path ];
volumes = ["${serverCfg.path.config}/freshrss:/var/www/FreshRSS/data"];
};
};
};
setup = {
trigger = "server"; # Triggers atomic environment verification on main controller
envFile = [ config.sops.secrets."FRESHRSS".path config.sops.secrets."CUSTOM".path];
script = pkgs.writeShellScript "setup-freshrss" ''
RSS="${pkgs.podman}/bin/podman --events-backend=none exec -u www-data freshrss-server"
$RSS ./cli/prepare.php
$RSS ./cli/do-install.php --default-user $DEFAULT_ADMIN_USERNAME --auth-type http_auth --base-url https://${containerCfg.subdomain}.${serverCfg.domain} --language en \
--title RSS --api-enabled --db-type pgsql --db-host ${builder.host} --db-user freshrss_user --db-password $DB_PASSWORD --db-base freshrss_db
$RSS ./cli/create-user.php --user $DEFAULT_ADMIN_USERNAME --password $DEFAULT_ADMIN_PASSWORD --email $DEFAULT_ADMIN_EMAIL
$RSS ./cli/reconfigure.php
# $RSS ./cli/access-permissions.sh
'';
};
};
}
@@ -0,0 +1,96 @@
{ config, containerCfg, pkgs, lib, builder, name, ... }:
let
serverCfg = config.syscfg.server;
# Ensure the package is available (Nixpkgs includes frigate)
frigatePkg = pkgs.frigate;
image = pkgs.dockerTools.streamLayeredImage {
name = "frigate";
tag = frigatePkg.version;
contents = [
pkgs.bashInteractive
frigatePkg
pkgs.ffmpeg # Explicitly included for video stream processing
];
config = {
Entrypoint = [ "${frigatePkg}/bin/frigate" ];
Cmd = [ "start" ];
ExposedPorts = {
"5000/tcp" = {}; # Web UI / API
"8554/tcp" = {}; # RTSP Feeds
"8555/tcp" = {}; # WebRTC
};
Env = [
"FRIGATE_RTSP_PASSWORD=secret" # Base fallback, overridden by envFile/sops
];
};
};
in {
requires.secrets = [ name ];
runtime = {
paths = [
{
path = "${serverCfg.path.config}/frigate/";
mode = "0755";
}
{
path = "/var/lib/frigate/storage/";
mode = "0755"; # Dedicated path for heavy video recordings and media
}
];
containers = {
server = builder.mkContainer {
subdomain = containerCfg.subdomain;
imageStream = image;
port = 5000;
secret = name;
extraEnv = {
PLUS_API_KEY = ""; # Optional: For Frigate Plus users
};
overrides = {
cmd = [ ];
volumes = [
"${serverCfg.path.config}/frigate:/config"
"/var/lib/frigate/storage:/media/frigate"
"/dev/bus/usb:/dev/bus/usb" # Passes Google Coral USB TPU to the container
"/dev/dri:/dev/dri" # Passes Intel/AMD GPU for hardware video decoding
];
};
};
};
setup = {
trigger = "server";
envFile = config.sops.secrets."FRIGATE_ENV".path;
script = pkgs.writeShellScript "setup-frigate" ''
mkdir -p "${serverCfg.path.config}/frigate"
mkdir -p "/var/lib/frigate/storage"
# Bootstrap a standard configuration layout if missing
if [ ! -f "${serverCfg.path.config}/frigate/config.yml" ]; then
cat <<EOF > "${serverCfg.path.config}/frigate/config.yml"
mqtt:
enabled: False # Set to True and define host if connecting to Home Assistant
database:
path: /config/frigate.db
cameras:
dummy_camera: # Replace with your actual RTSP stream details
enabled: false
ffmpeg:
inputs:
- path: rtsp://127.0.0.1:554/live
roles:
- detect
detect:
enabled: false
EOF
fi
'';
};
};
}
+146
View File
@@ -0,0 +1,146 @@
{ config, containerCfg, pkgs, lib, builder, name, ... }:
let
version = "latest";
serverCfg = config.syscfg.server;
LDAP_DC_DOMAIN = "dc=ldap," + (lib.concatMapStringsSep "," (x: "dc=${x}") (lib.splitString "." serverCfg.domain));
in {
requires = {
secrets = [ name ];
databases = [ name ];
};
runtime = {
paths = [{
path="${serverCfg.path.data}/gitea";
owner = "1000:1000";
dirs = ["data" "runner"];
mode = "0755";
}];
containers = {
server = builder.mkContainer {
subdomain = containerCfg.subdomain;
image = "gitea/gitea:${version}";
port = 8080;
secret = name;
extraEnv = { # app.ini -> GITEA__<section>__<KEY> = "<VALUE>";
GITEA__DEFAULT__APP_NAME = if(containerCfg.extra ? name) then containerCfg.extra.name else "Gitea";
GITEA__repository__DISABLED_REPO_UNITS = "repo.ext_issues,repo.ext_wiki";
GITEA__repository__DISABLE_STARS = "true";
GITEA__repository__DEFAULT_MERGE_STYLE = "squash";
# GITEA__ui__THEMES = "";
# GITEA__ui__DEFAULT_THEME = "";
# GITEA__security__SECRET_KEY = "SECRET_ENV";
# GITEA__security__INTERNAL_TOKEN = "SECRET_ENV";
# GITEA__database__PASSWD = "SECRET_ENV";
# GITEA__mailer__PASSWD="SECRET_ENV";
GITEA__database__DB_TYPE = "postgres";
GITEA__database__HOST = builder.host;
GITEA__database__NAME = "gitea_db";
GITEA__database__USER = "gitea_user";
GITEA__mailer__ENABLED = "true";
GITEA__mailer__FROM = "";
GITEA__mailer__PROTOCOL = "smtps";
GITEA__mailer__SMTP_ADDR = "";
GITEA__mailer__SMTP_PORT = "";
GITEA__mailer__USER= "";
GITEA__server__DOMAIN = "${containerCfg.subdomain}.${serverCfg.domain}";
GITEA__server__ROOT_URL = "https://${containerCfg.subdomain}.${serverCfg.domain}/";
GITEA__server__PROTOCOL = "http";
GITEA__server__HTTP_PORT = "8080";
GITEA__server__LFS_START_SERVER = "true";
GITEA__security__INSTALL_LOCK = "true";
} // ( if serverCfg.containers?authentik then {
GITEA__service__ENABLE_BASIC_AUTHENTICATION = "false";
GITEA__service__ENABLE_REVERSE_PROXY_AUTHENTICATION = "true";
GITEA__service__ENABLE_REVERSE_PROXY_AUTHENTICATION_API = "true";
GITEA__service__ENABLE_REVERSE_PROXY_AUTO_REGISTRATION = "true";
GITEA__service__ENABLE_REVERSE_PROXY_EMAIL = "true";
GITEA__service__ENABLE_REVERSE_PROXY_FULL_NAME = "true";
GITEA__service__ALLOW_ONLY_EXTERNAL_REGISTRATION = "true";
GITEA__security__REVERSE_PROXY_LOGOUT_REDIRECT = "https://${serverCfg.containers.authentik.subdomain}.${serverCfg.domain}/outpost.goauthentik.io/sign_out";
GITEA__security__REVERSE_PROXY_AUTHENTICATION_USER = "X-authentik-username";
GITEA__security__REVERSE_PROXY_AUTHENTICATION_EMAIL = "X-authentik-email";
GITEA__security__REVERSE_PROXY_AUTHENTICATION_FULL_NAME = "X-authentik-name";
GITEA__security__RREVERSE_PROXY_LIMIT = "1";
GITEA__security__REVERSE_PROXY_TRUSTED_PROXIES = "127.0.0.0/8,::1/128,10.0.0.0/8";
} else {});
extraLabels = {
"traefik.http.routers.${containerCfg.subdomain}-login.rule" = "Host(`${containerCfg.subdomain}.${serverCfg.domain}`) && Path(`/user/login`) ";
"traefik.http.routers.${containerCfg.subdomain}-login.middlewares" = if (serverCfg.containers?authentik && containerCfg.extra?proxyauth) then "authentik" else "";
"traefik.http.routers.${containerCfg.subdomain}-login.priority" = "100";
"traefik.http.routers.${containerCfg.subdomain}-login.entrypoints" = "web-secure";
"traefik.http.routers.${containerCfg.subdomain}-login.tls" = "true";
};
overrides = {
volumes = [
"${serverCfg.path.data}/gitea/data:/data"
];
ports = [ "2222:22" ];
};
};
runner = builder.mkContainer {
image = "gitea/act_runner:${version}";
secret = name;
extraEnv = {
CONFIG_FILE="/data/config.yml";
GITEA_INSTANCE_URL="https://${containerCfg.subdomain}.${serverCfg.domain}";
GITHUB_INSTANCE_URL="https://${containerCfg.subdomain}.${serverCfg.domain}";
};
overrides = {
volumes = [
"${serverCfg.path.data}/gitea/runner:/data"
"/var/run/podman/podman.sock:/var/run/docker.sock"
];
# ports = [ "8088:8088" ];
};
};
};
setup = {
trigger = "server";
envFile = config.sops.secrets."CUSTOM".path;
script = pkgs.writeShellScript "setup" ''
# Define the command wrapper
GT="${pkgs.podman}/bin/podman --events-backend=none exec -u git gitea-server gitea"
GTR="${pkgs.podman}/bin/podman --events-backend=none exec -u git gitea-runner ./act_runner"
$GT admin user create --username "$DEFAULT_ADMIN_USERNAME" --password "$DEFAULT_ADMIN_PASSWORD" --email "$DEFAULT_ADMIN_EMAIL" --admin || true
touch ${serverCfg.path.data}/gitea/data-runner/config.yml
RUNNER_TOKEN=$($GT actions generate-runner-token)
$GTR register \
--instance "https://${containerCfg.subdomain}.${serverCfg.domain}" \
--token "$RUNNER_TOKEN" \
--name "Runner" \
--labels "ubuntu-latest:docker://catthehacker/ubuntu:act-latest" \
--no-interactive
${lib.optionalString (serverCfg.containers ? authentik) ''
$GT admin auth add-ldap --name Authentik --host authentik-ldap --port 6636 --security-protocol ldaps --skip-tls-verify \
--bind-dn "cn=ldap-service,ou=users,${LDAP_DC_DOMAIN}" --bind-password $DEFAULT_LDAP_PASSWORD \
--user-search-base "ou=users,${LDAP_DC_DOMAIN}" \
--user-filter "(&(objectClass=user)(|(uid=%[1]s)(mail=%[1]s)))" \
--admin-filter "(memberOf=cn=admin,ou=groups,${LDAP_DC_DOMAIN})" \
--username-attribute "username" --firstname-attribute "givenName" --surname-attribute "sn" --email-attribute "mail" \
--synchronize-users
''}
echo "Completed Gitea Setup"
'';
};
};
}
@@ -0,0 +1,48 @@
{ config, containerCfg, pkgs, lib, builder, name,... }:
let
serverCfg = config.syscfg.server;
version = "latest";
in {
runtime = {
paths = [{
path = "${serverCfg.path.config}/handbrake";
mode = "0755";
}];
containers = {
server = builder.mkContainer {
authentik = true;
tmpfs = true;
subdomain = containerCfg.subdomain;
subpath = containerCfg.subpath;
image = "ghcr.io/jlesage/handbrake:${version}";
port = 5800;
extraEnv = {
USER_ID = "1000";
GROUP_ID = "1000";
AUTOMATED_CONVERSION_PRESET = "Custom/AV1 MKV 1080p30";
AUTOMATED_CONVERSION_FORMAT = "mkv";
AUTOMATED_CONVERSION_OUTPUT_SUBDIR = "SAME_AS_SRC";
};
overrides = {
volumes = [
"${serverCfg.path.config}/handbrake:/config:rw"
"${serverCfg.path.dlComplete}:/watch:rw"
"${serverCfg.path.dlConverted}:/output:rw"
];
};
};
};
setup = {
trigger = "server";
script = pkgs.writeShellScript "setup" ''
mkdir -p ${serverCfg.path.data}/handbrake/{watch,output}
'';
};
};
}
@@ -0,0 +1,102 @@
{ config, containerCfg, pkgs, lib, builder, name, ... }:
let
version = "latest";
serverCfg = config.syscfg.server;
in {
runtime = {
vm = {
portForward = [ 8123 ];
cfg = {cfg,...}: {
services.home-assistant = {
enable = true;
openFirewall = true;
extraComponents = [
"matter" "thread" "cast" "zha"
"default_config" "met" "esphome" "radio_browser"
"telegram_bot" "swiss_public_transport" "nextcloud" "jellyfin"
] ++ (if containerCfg.extra ? components then containerCfg.extra.components else []);
extraPackages = pp: with pp; [
python-telegram gtts
];
lovelaceConfig = {};
config = {
homeassistant = {
name = "Home";
latitude = "${if containerCfg.extra ? latitude then toString containerCfg.extra.latitude else toString 0}";
longitude = "${if containerCfg.extra ? longitude then toString containerCfg.extra.longitude else toString 0}";
elevation = "${if containerCfg.extra ? elevation then toString containerCfg.extra.elevation else toString 0}";
unit_system = "metric";
time_zone = config.time.timeZone;
};
lovelace = { mode = "yaml"; };
customLovelaceModules = [];
# default_config = {};
http = {
use_x_forwarded_for = true;
trusted_proxies = [ "10.0.0.0/8" "127.0.0.1" ];
};
};
};
};
};
containers = {
dummy = builder.mkContainer {
subdomain = containerCfg.subdomain;
image = "alpine:latest";
extraLabels = {
"traefik.http.services.${containerCfg.subdomain}.loadbalancer.server.url" = "http://${builder.hostIp}:8123";
};
overrides = {cmd = [ "sleep" "infinity" ];};
};
};
setup = {
trigger = "dummy";
envFile = config.sops.secrets."CUSTOM".path;
script = pkgs.writeShellScript "setup" ''
HASS_URL="https://${containerCfg.subdomain}.${serverCfg.domain}"
until [[ "$(${pkgs.curl}/bin/curl -s -o /dev/null -w "%{http_code}" "$HASS_URL/manifest.json")" =~ (200|301|302) ]]; do
sleep 5
done
sleep 5
ONBOARDING_STATUS=$(${pkgs.curl}/bin/curl -s -o /dev/null -w "%{http_code}" "$HASS_URL/api/onboarding" 2>/dev/null || echo "000")
if [ "$ONBOARDING_STATUS" = "200" ]; then
AUTH_CODE=$( ${pkgs.curl}/bin/curl -s -X POST "$HASS_URL/api/onboarding/users" \
-H "Content-Type: application/json" \
-d '{"client_id":"'"$HASS_URL"'","name":"'"$DEFAULT_ADMIN_USERNAME"'","username":"'"$DEFAULT_ADMIN_USERNAME"'","password":"'"$DEFAULT_ADMIN_PASSWORD"'","language":"en"}' \
| ${pkgs.jq}/bin/jq -r '.auth_code' )
ACCESS_TOKEN=$(${pkgs.curl}/bin/curl -s -X POST "$HASS_URL/auth/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=authorization_code&code=$AUTH_CODE&client_id=$HASS_URL" \
| ${pkgs.jq}/bin/jq -r '.access_token' )
${pkgs.curl} -s -X POST "$HASS_URL/api/onboarding/core_config" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"time_zone":"${config.time.timeZone}"}' > /dev/null 2>&1 || true
# We can configure many more things above !
${pkgs.curl} -s -X POST "$HASS_URL/api/onboarding/analytics" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" -d '{}' > /dev/null 2>&1 || true
${pkgs.curl} -s -X POST "$HA_URL/api/onboarding/integration" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"client_id":"'"$HASS_URL"'","redirect_uri":"'"$HASS_URL"'/?auth_callback=1"}' > /dev/null 2>&1 || true
fi
'';
};
};
}
+291
View File
@@ -0,0 +1,291 @@
{ config, containerCfg, pkgs, lib, builder, name,... }:
let
version = "latest";
serverCfg = config.syscfg.server;
mediaCfg = config.syscfg.media;
backgroundImage = if mediaCfg.banner.png != null then mediaCfg.banner.png else mediaCfg.bg;
backgroundFileName = builtins.baseNameOf (toString backgroundImage);
backgroundMount = "/app/public/media/${backgroundFileName}";
settings = pkgs.writers.writeYAML "settings.yaml" {
title = "My Self-Hosted Dashboard";
description = "";
startUrl = "https://${containerCfg.subdomain}.${serverCfg.domain}";
background = {
image = "/media/${backgroundFileName}";
blur = "sm"; #sm, md, xl,...
# saturate = "";
# brightness = "";
# opacity = "";
};
cardBlur = "xs"; #xs, md, ...
# favicon = "";
theme = "dark";
color = "slate";
pwa = {
};
layout = {
Admin = {
style = "grid";
columns = 1;
};
};
providers = {
finnhub = "{{HOMEPAGE_VAR_FINNHUB}}";
};
headerStyle = "clean";
hideVersion = true;
disableUpdateCheck = true;
showStats = false;
statusStyle = "dot";
hideErrors = true;
};
widgets = pkgs.writers.writeYAML "widgets.yaml" [
{openmeteo = {
latitude = "47.3769";
longitude = "8.5417";
timezone = "Europe/Zurich";
units = "metric";
cache = "15";
};}
{search = {
provider = "custom";
focus = true;
showSearchSuggestions = true;
target = "_blank";
} // (lib.optionalAttrs (serverCfg.containers?searxng) {
url = "https://${serverCfg.containers.searxng.subdomain}.${serverCfg.domain}/search?q=";
suggestionUrl = "https://${serverCfg.containers.searxng.subdomain}.${serverCfg.domain}/autocompleter?q=";
});
}
{stocks = {
provider = "finnhub";
color = true;
cache = 15;
watchlist = containerCfg.extra.stocks or [];
};}
];
bookmarks = pkgs.writers.writeYAML "bookmarks.yaml" [
];
services = pkgs.writers.writeYAML "services.yaml" [
{Media = lib.flatten [
(lib.optional (serverCfg.containers?jellyfin) {
Jellyfin={
icon = "jellyfin.png";
href = "https://${serverCfg.containers.jellyfin.subdomain}.${serverCfg.domain}";
widget = {
type="jellyfin";
url = "http://jellyfin-server:8096";
key = "{{HOMEPAGE_VAR_JELLYFIN_API}}";
};
};
})
(lib.optional (serverCfg.containers?invidious) {
Invidious={
icon = "invidious.png";
href = "https://${serverCfg.containers.invidious.subdomain}.${serverCfg.domain}";
};
})
(lib.optional (serverCfg.containers?miniflux) {
Miniflux={
icon = "miniflux.png";
href = "https://${serverCfg.containers.miniflux.subdomain}.${serverCfg.domain}";
widget = {
type="miniflux";
url = "http://miniflux-server";
key = "{{HOMEPAGE_VAR_MINIFLUX_API}}";
};
};
})
];}
{Cloud = lib.flatten [
(lib.optional (serverCfg.containers?nextcloud) {
Nextcloud={
icon = "nextcloud.png";
href = "https://${serverCfg.containers.nextcloud.subdomain}.${serverCfg.domain}";
widget = {
type="nextcloud";
url = "http://nextcloud-server:80";
key = "{{HOMEPAGE_VAR_NEXTCLOUD_API}}";
};
};
})
(lib.optional (serverCfg.containers?ethercalc) {
Ethercalc={
icon = "ethercalc.png";
href = "https://${serverCfg.containers.ethercalc.subdomain}.${serverCfg.domain}";
};
})
(lib.optional (serverCfg.containers?etherpad) {
Etherpad={
icon = "etherpad.png";
href = "https://${serverCfg.containers.etherpad.subdomain}.${serverCfg.domain}";
};
})
(lib.optional (serverCfg.containers?immich) {
immich={
icon = "immich.png";
href = "https://${serverCfg.containers.immich.subdomain}.${serverCfg.domain}";
widget = {
type="immich";
url = "http://immich-server:80";
key = "{{HOMEPAGE_VAR_IMMICH_API}}";
version = "2";
};
};
})
];}
{Dev = lib.flatten [
(lib.optional (serverCfg.containers?gitea) {
Gitea={
icon = "gitea.png";
href = "https://${serverCfg.containers.gitea.subdomain}.${serverCfg.domain}";
# widget = {
# type="gitea";
# url = "http://gitea-server:8080";
# key = "{{HOMEPAGE_VAR_GITEA_API}}";
# };
};
})
];}
{Admin = #lib.flatten [
#({permissions.groups = ["admin"];})
#({services =
lib.flatten [
(lib.optional (serverCfg.containers?traefik) {
Traefik={
icon = "traefik.png";
href = "https://${serverCfg.containers.traefik.subdomain}.${serverCfg.domain}";
widget = {
type = "traefik";
url = "http://traefik-server:8080";
};
};
})
(lib.optional (serverCfg.containers?authentik) {
Authentik={
icon = "authentik.png";
href = "https://${serverCfg.containers.authentik.subdomain}.${serverCfg.domain}";
widget = {
type = "authentik";
url = "http://authentik-server:9000";
key = "{{HOMEPAGE_VAR_AUTHENTIK_API}}";
version = "2";
};
};
})
(lib.optional (serverCfg.containers?umami) {
Umami={
icon = "umami.png";
href = "https://${serverCfg.containers.umami.subdomain}.${serverCfg.domain}";
};
})
(lib.optional (serverCfg.containers?influx) {
Influx={
icon = "influx.png";
href = "https://${serverCfg.containers.influx.subdomain}.${serverCfg.domain}";
};
})
(lib.optional (serverCfg.containers?handbrake) {
Handbrake={
icon = "handbrake.png";
href = "https://${serverCfg.containers.handbrake.subdomain}.${serverCfg.domain}";
};
})
(lib.optional (serverCfg.containers?transmission) {
Transmission={
icon = "transmission.png";
href = "https://${serverCfg.containers.transmission.subdomain}.${serverCfg.domain}/transmission";
widget = {
type = "transmission";
url = "http://transmission-server:9091";
rpcUrl = "/transmission/";
};
};
})
(lib.optional (serverCfg.containers?servarr) (
let
modules = serverCfg.containers.servarr.extra.modules or ["prowlarr" "sonarr" "radarr" "flaresolverr" ];
in
(lib.optional (builtins.elem "sonarr" modules) {
Sonarr={
icon = "sonarr.png";
href = "https://${serverCfg.containers.servarr.subdomain}.${serverCfg.domain}/sonarr";
widget = {
type = "sonarr";
url = "http://servarr-sonarr:8989";
key = "{{HOMEPAGE_VAR_SONARR_API}}";
};
};
}) ++ (lib.optional (builtins.elem "radarr" modules) {
Radarr={
icon = "radarr.png";
href = "https://${serverCfg.containers.servarr.subdomain}.${serverCfg.domain}/radarr";
widget = {
type = "radarr";
url = "http://servarr-radarr:8989";
key = "{{HOMEPAGE_VAR_RADARR_API}}";
};
};
}) ++ (lib.optional (builtins.elem "lidarr" modules) {
Lidarr={
icon = "lidarr.png";
href = "https://${serverCfg.containers.servarr.subdomain}.${serverCfg.domain}/lidarr";
widget = {
type = "lidarr";
url = "http://servarr-lidarr:8989";
key = "{{HOMEPAGE_VAR_LIDARR_API}}";
};
};
}) ++ (lib.optional (builtins.elem "prowlarr" modules) {
Prowlarr={
icon = "prowlarr.png";
href = "https://${serverCfg.containers.servarr.subdomain}.${serverCfg.domain}/prowlarr";
widget = {
type = "prowlarr";
url = "http://servarr-prowlarr:8989";
key = "{{HOMEPAGE_VAR_PROWLARR_API}}";
};
};
})
# Bazarr
))
];}#)];}
];
in {
runtime = {
containers = {
server = builder.mkContainer {
subdomain = containerCfg.subdomain;
image = "ghcr.io/gethomepage/homepage:${version}";
port = 3000;
extraEnv = {
HOMEPAGE_VAR_TITLE="${serverCfg.domain}";
HOMEPAGE_ALLOWED_HOSTS = "${containerCfg.subdomain}.${serverCfg.domain},${builder.host}";
};
extraLabels = {
"traefik.http.routers.${containerCfg.subdomain}.service" = "${containerCfg.subdomain}";
};
overrides = {
environmentFiles = [ config.sops.secrets."CUSTOM".path ];
volumes = [
"${settings}:/app/config/settings.yaml:ro"
"${services}:/app/config/services.yaml:ro"
"${widgets}:/app/config/widgets.yaml:ro"
"${bookmarks}:/app/config/bookmarks.yaml:ro"
"${backgroundImage}:${backgroundMount}:ro"
];
};
};
};
};
}
+104
View File
@@ -0,0 +1,104 @@
{ config, containerCfg, pkgs, lib, builder, name,... }:
let
version = "v2";
serverCfg = config.syscfg.server;
in {
requires = {
secrets = [ name ];
databases = [ name ];
};
runtime = {
paths = [{
path = "${serverCfg.path.config}/immich";
dirs = ["cache"];
mode = "0750";
}{
path = "${serverCfg.path.data}/immich/";
dirs = ["upload" "thumbs" "encoded-video" "backups"];
mode = "0755";
}];
containers = {
server = builder.mkContainer {
subdomain = containerCfg.subdomain;
image = "ghcr.io/immich-app/immich-server:${version}";
port = 2283;
secret = name;
extraEnv = {
DB_HOSTNAME = builder.host;
REDIS_HOSTNAME = builder.host;
DB_USERNAME = "immich_user";
DB_DATABASE_NAME = "immich_db";
IMMICH_TRUSTED_PROXIES = "10.0.0.0/8";
IMMICH_MACHINE_LEARNING_URL = "http://immich-ml:3003";
# IMMICH_ALLOW_SETUP = "false";
# IMMICH_IGNORE_MOUNT_CHECK_ERRORS = "true";
};
overrides = {
volumes = [
"${serverCfg.path.photo}:/data/upload"
"${serverCfg.path.data}/immich/backups:/data/backups"
"${serverCfg.path.config}/immich/thumbs:/data/thumbs"
"${serverCfg.path.config}/immich/encoded-video:/data/encoded-video"
];
};
};
ml = builder.mkContainer {
image = "ghcr.io/immich-app/immich-machine-learning:${version}";
port = 3003;
overrides = {
volumes = [
"${serverCfg.path.config}/immich/cache:/cache"
];
};
};
};
setup = {
trigger = "server";
envFile = config.sops.secrets."CUSTOM".path;
script = pkgs.writeShellScript "setup" ''
PSQL="${pkgs.postgresql}/bin/psql -U postgres"
$PSQL -d "immich_db" -tAc "CREATE EXTENSION IF NOT EXISTS vchord CASCADE;"
$PSQL -d "immich_db" -tAc "CREATE EXTENSION IF NOT EXISTS earthdistance CASCADE;"
IMMICH_URL="https://${containerCfg.subdomain}.${serverCfg.domain}"
until [[ "$(${pkgs.curl}/bin/curl -s -o /dev/null -w "%{http_code}" "$IMMICH_URL")" =~ (200|301|302) ]]; do
sleep 5
done
${pkgs.curl}/bin/curl -X POST "$IMMICH_URL/api/auth/admin-sign-up" \
-H "Content-Type: application/json" -H "Accept: application/json" \
-d '{ "email": "'"$DEFAULT_ADMIN_EMAIL"'", "password": "'"$DEFAULT_ADMIN_PASSWORD"'", "name": "'"$DEFAULT_ADMIN_USERNAME"'" }'
IMMICH_TOKEN=$(${pkgs.curl}/bin/curl -sSf -X POST "$IMMICH_URL/api/auth/login" \
-H "Content-Type: application/json" \
-d '{ "email": "'"$DEFAULT_ADMIN_EMAIL"'", "password": "'"$DEFAULT_ADMIN_PASSWORD"'"}' \
| ${pkgs.jq}/bin/jq -r '.accessToken')
${lib.optionalString (serverCfg.containers ? authentik) ''
${pkgs.curl}/bin/curl -s -X GET "$IMMICH_URL/api/system-config" -H "Cookie: immich_access_token=$IMMICH_TOKEN; immich_auth_type=password; immich_is_authenticated=true" | \
${pkgs.jq}/bin/jq '.oauth.enabled = true |
.oauth.autoRegister = true |
.oauth.autoLaunch = true |
.oauth.signingAlgorithm = "RS256" |
.oauth.profileSigningAlgorithm = "RS256" |
.oauth.clientId = "immich" |
.oauth.clientSecret = "'"$IMMICH_OAUTH_SECRET"'" |
.oauth.issuerUrl = "https://${serverCfg.containers.authentik.subdomain}.${serverCfg.domain}/application/o/immich/" |
.oauth.scope = "openid profile email" |
.oauth.buttonText = "Login with SSO"' | \
${pkgs.curl}/bin/curl -s -X PUT "$IMMICH_URL/api/system-config" -H "Cookie: immich_access_token=$IMMICH_TOKEN; immich_auth_type=password; immich_is_authenticated=true" -H "Content-Type: application/json" -d @-
''}
${pkgs.curl}/bin/curl -s -X GET "$IMMICH_URL/api/system-config" -H "Cookie: immich_access_token=$IMMICH_TOKEN; immich_auth_type=password; immich_is_authenticated=true" | \
${pkgs.jq}/bin/jq '.storageTemplate.enable = true |
.storageTemplate.template = "{{y}}/{{#if album}}{{album}}{{else}}{{MM}}{{/if}}/{{filename}}"' | \
${pkgs.curl}/bin/curl -s -X PUT "$IMMICH_URL/api/system-config" -H "Cookie: immich_access_token=$IMMICH_TOKEN; immich_auth_type=password; immich_is_authenticated=true" -H "Content-Type: application/json" -d @-
'';
};
};
}
+78
View File
@@ -0,0 +1,78 @@
{ config, containerCfg, pkgs, lib, builder, name, ... }:
let
serverCfg = config.syscfg.server;
version = "latest";
in {
requires = {
secrets = [ name ];
databases = [ name ];
};
runtime = {
paths = [{
path = "${serverCfg.path.config}/influxdb/";
owner = "1500:1500";
mode = "0700";
}{
path = "${serverCfg.path.data}/influxdb/";
owner = "1500:1500";
mode = "0700";
}];
containers = {
# db = builder.mkContainer {
# subdomain = containerCfg.subdomain;
# image = "influxdata/influxdb:3.0";
# port = 8181;
# secret = name;
# extraEnv = {
# INFLUXD_DB_PATH = "/db";
# INFLUXD_CONFIG_PATH = "/config";
# };
# overrides = {
# volumes = [
# "${serverCfg.path.data}/influxdb:/db:rw"
# "${serverCfg.path.config}/influxdb:/config:ro"
# ];
# };
# };
server = builder.mkContainer {
tmpfs = true;
subdomain = containerCfg.subdomain;
image = "influxdata/influxdb3-ui:${version}";
port = 8080;
secret = name;
extraEnv = {
SESSION_SECRET_KEY = "7b0024c13ae770000f797c201e2f210b9932a689c04d34de04379faa44e88e97";
DATABASE_URL = "/db/sqlite.db";
};
overrides = {
ports = [ "8080:8080" ];
cmd = [ "--mode=admin" ];
volumes = [
"${serverCfg.path.data}/influxdb:/db:rw"
"${serverCfg.path.config}/influxdb/:/app-root/config:ro"
];
};
};
};
setup = {
trigger = "server";
script = pkgs.writeShellScript "setup" ''
cat > ${serverCfg.path.config}/influxdb/config.json << 'EOF'
{
"DEFAULT_INFLUX_SERVER": "http://${builder.host}:8181",
"DEFAULT_INFLUX_DATABASE": "main",
"DEFAULT_API_TOKEN": "$INFLUXDB3_TOKEN",
"DEFAULT_SERVER_NAME": "${serverCfg.domain}"
}
EOF
chmod -R 755 ${serverCfg.path.config}/influxdb
'';
};
};
}
@@ -0,0 +1,83 @@
{ config, containerCfg, pkgs, lib, builder, name, ... }:
let
serverCfg = config.syscfg.server;
patchedInvidious = pkgs.invidious.overrideAttrs (oldAttrs: {
postPatch = (oldAttrs.postPatch or "") + ''
cp ${../data/invidious/login.cr} src/invidious/routes/login.cr
'';
});
image = pkgs.dockerTools.streamLayeredImage {
name = pkgs.invidious.name;
tag = pkgs.invidious.version;
contents = [ pkgs.cacert patchedInvidious ];
config = {
Entrypoint = [ "${patchedInvidious}/bin/invidious" ];
ExposedPorts = { "3000/tcp" = {}; };
Env = [
"SSL_CERT_FILE=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt"
"NIX_SSL_CERT_FILE=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt"
];
};
};
in {
requires = {
secrets = [ name ];
databases = [ name ];
};
runtime = {
paths = [{
path="${serverCfg.path.config}/invidious";
mode = "0755";
}];
containers = {
server = builder.mkContainer {
subdomain = containerCfg.subdomain;
imageStream = image;
port = 3000;
secret = name;
extraLabels = {
"traefik.http.routers.${containerCfg.subdomain}-login.rule" = "Host(`${containerCfg.subdomain}.${serverCfg.domain}`) && Path(`/login`) ";
"traefik.http.routers.${containerCfg.subdomain}-login.middlewares" = if serverCfg.containers?authentik then "authentik" else "";
"traefik.http.routers.${containerCfg.subdomain}-login.priority" = "100";
"traefik.http.routers.${containerCfg.subdomain}-login.entrypoints" = "web-secure";
"traefik.http.routers.${containerCfg.subdomain}-login.tls" = "true";
};
extraEnv = {
INVIDIOUS_CONFIG_FILE = "/data/config.yml";
};
overrides = {
volumes = [
"${serverCfg.path.config}/invidious:/data:ro"
];
};
};
companion = builder.mkContainer {
image = "quay.io/invidious/invidious-companion:latest";
port = 8282;
secret = name; #SERVER_SECRET_KEY = INVIDIOUS_COMPANION_KEY
extraOptions = [
"--cap-drop=all"
"--security-opt=no-new-privileges"
];
};
};
setup = {
trigger = "server";
envFile = [ config.sops.secrets."INVIDIOUS".path config.sops.secrets."CUSTOM".path ];
script = pkgs.writeShellScript "setup" ''
export DB_HOST=${builder.host}
export INVIDIOUS_DOMAIN=${containerCfg.subdomain}.${serverCfg.domain}
${pkgs.gettext}/bin/envsubst < "${../data/invidious/config.yml}" > "${serverCfg.path.config}/invidious/config.yml"
'';
};
};
}
+175
View File
@@ -0,0 +1,175 @@
{ config, containerCfg, pkgs, lib, builder, name, ... }:
let
serverCfg = config.syscfg.server;
LDAP_DC_DOMAIN = "dc=ldap," + (lib.concatMapStringsSep "," (x: "dc=${x}") (lib.splitString "." serverCfg.domain));
nss = pkgs.dockerTools.fakeNss.override {
extraPasswdLines = [
"jellyfin:x:1000:1000:Jellyfin Daemon:/config/data:/bin/false"
];
extraGroupLines = [
"jellyfin:x:1000:"
];
};
image = pkgs.dockerTools.streamLayeredImage { # pkgs.dockerTools.buildImage{#
name = pkgs.jellyfin.name;
tag = pkgs.jellyfin.version;
contents = [ pkgs.cacert nss pkgs.jellyfin pkgs.bashInteractive ];
config = {
User = "jellyfin:jellyfin";
Entrypoint = [ "${pkgs.jellyfin}/bin/jellyfin" ];
ExposedPorts = { "8096/tcp" = { }; };
Env = [
"SSL_CERT_FILE=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt"
"NIX_SSL_CERT_FILE=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt"
];
};
};
in {
runtime = {
paths = [
{
path = "${serverCfg.path.config}/jellyfin/";
owner = "1000:1000";
mode = "0755";
}
];
containers = {
server = builder.mkContainer {
tmpfs = true;
subdomain = containerCfg.subdomain;
imageStream = image;
port = 8096;
extraEnv = {
HOME = "/config/data";
DOTNET_SYSTEM_GLOBALIZATION_INVARIANT = "1";
JELLYFIN_HttpListenerHost__BindAddress= "0.0.0.0"; #we can use settings.xml override
JELLYFIN_ServerName = if containerCfg.extra?name then containerCfg.extra.name else "Flix";
};
overrides = {
cmd = [
"--datadir" "/config/data"
"--cachedir" "/config/cache"
"--configdir" "/config/config"
"--logdir" "/config/log"
];
volumes = [
"${serverCfg.path.film}:/media:ro"
"${serverCfg.path.config}/jellyfin:/config"
];
# If you have an Intel/AMD GPU for transcoding, add the device:
devices = lib.optionals (builtins.pathExists "/dev/dri") [ "/dev/dri:/dev/dri" ];
};
};
};
setup = {
trigger = "server";
envFile = config.sops.secrets."CUSTOM".path;
script = pkgs.writeShellScript "setup" ''
JELLYFIN_URL="https://${containerCfg.subdomain}.${serverCfg.domain}"
until [ "$(${pkgs.curl}/bin/curl -sf "$JELLYFIN_URL/health")" = "Healthy" ]; do
sleep 5
done
echo "Jellyfin is up. Sleeping for 20 seconds..."
sleep 20
WIZARD_COMPLETE=$(${pkgs.curl}/bin/curl -sSf "$JELLYFIN_URL/System/Info/Public" 2>/dev/null | \
${pkgs.jq}/bin/jq -r '.StartupWizardCompleted // false')
if [ "$WIZARD_COMPLETE" = "false" ]; then
if ! ${pkgs.curl}/bin/curl -sSf -X POST "$JELLYFIN_URL/Startup/Configuration" \
-H "Content-Type: application/json" \
-d '{"ServerName":"Flix","UICulture":"en-US","MetadataCountryCode":"US","PreferredMetadataLanguage":"en"}'; then
echo "ERROR: Failed to set startup configuration."
exit 1
fi
if ! ${pkgs.curl}/bin/curl -sSf -X GET "$JELLYFIN_URL/Startup/User"; then
echo "ERROR: Failed to get base user."
exit 1
fi
if ! ${pkgs.curl}/bin/curl -sSf -X POST "$JELLYFIN_URL/Startup/User" \
-H 'accept: */*' -H "Content-Type: application/json" \
-d '{"Name": "'"$DEFAULT_ADMIN_USERNAME"'", "Password": "'"$DEFAULT_ADMIN_PASSWORD"'"}'; then
echo "ERROR: Failed to set admin user."
exit 1
fi
if ! ${pkgs.curl}/bin/curl -sSf -X POST "$JELLYFIN_URL/Startup/RemoteAccess" \
-H "Content-Type: application/json" \
-d '{"EnableRemoteAccess":true,"EnableAutomaticPortMapping":false}'; then
echo "ERROR: Failed to configure remote access."
exit 1
fi
if ! ${pkgs.curl}/bin/curl -sSf -X POST "''$JELLYFIN_URL/Startup/Complete"; then
echo "ERROR: Failed to complete wizard."
exit 1
fi
echo "Jellyfin initialization successfully completed!"
fi
${lib.optionalString (serverCfg.containers ? authentik) ''
JELLYFIN_TOKEN=$(${pkgs.curl}/bin/curl -sSf -X POST "$JELLYFIN_URL/Users/AuthenticateByName" \
-H "Content-Type: application/json" \
-H "Authorization: MediaBrowser Client=\"Bash Script\", Device=\"Server Terminal\", DeviceId=\"script-12345\", Version=\"1.0.0\"" \
-d "{\"Username\": \"$DEFAULT_ADMIN_USERNAME\", \"Pw\": \"$DEFAULT_ADMIN_PASSWORD\"}" \
| ${pkgs.jq}/bin/jq -r '.AccessToken')
# Verify we got a token
if [ "$JELLYFIN_TOKEN" = "null" ] || [ -z "$JELLYFIN_TOKEN" ]; then
echo "ERROR: Authentication failed."
exit 1
fi
if ${pkgs.curl}/bin/curl -sSf -H "Authorization: MediaBrowser Token=\"$JELLYFIN_TOKEN\"" \
"$JELLYFIN_URL/Plugins" | ${pkgs.gnugrep}/bin/grep -q "958aad6637844d2ab89aa7b6fab6e25c"; then
echo "LDAP Plugin is already installed. Skipping setup."
else
if ! ${pkgs.curl}/bin/curl -sSf -X POST "$JELLYFIN_URL/Packages/Installed/LDAP%20Authentication?assemblyGuid=958aad6637844d2ab89aa7b6fab6e25c" \
-H "Authorization: MediaBrowser Token=\"$JELLYFIN_TOKEN\"" \
-H "Content-Length: 0"; then
echo "ERROR: LDAP Plugin Setup Failed."
exit 1
fi
if ! ${pkgs.curl}/bin/curl -sSf -X POST "$JELLYFIN_URL/System/Restart" \
-H "Authorization: MediaBrowser Token=\"$JELLYFIN_TOKEN\"" \
-H "Content-Length: 0"; then
echo "ERROR: Server failed to accept restart command."
exit 1
fi
sleep 1-
until [ "$(${pkgs.curl}/bin/curl -sf "$JELLYFIN_URL/health")" = "Healthy" ]; do
sleep 5
done
echo "Jellyfin is up. Sleeping for 20 seconds..."
sleep 20
fi
if ! ${pkgs.curl}/bin/curl -sSf -X POST "$JELLYFIN_URL/Plugins/958aad66-3784-4d2a-b89a-a7b6fab6e25c/Configuration" \
-H "Authorization: MediaBrowser Token=\"$JELLYFIN_TOKEN\"" \
-H "Content-Type: application/json" -H 'accept: */*' \
-d '{"LdapUsers":[],"LdapServer":"authentik-ldap","LdapPort":6636,"UseSsl":true,"UseStartTls":false,"SkipSslVerify":true,
"LdapBindUser":"cn=ldap-service,ou=users,${LDAP_DC_DOMAIN}","LdapBindPassword": "'"$DEFAULT_LDAP_PASSWORD"'",
"LdapBaseDn":"${LDAP_DC_DOMAIN}","LdapSearchFilter":"(memberOf=cn=flix,ou=groups,${LDAP_DC_DOMAIN})",
"LdapSearchAttributes":"uid, cn, mail, displayName",
"LdapAdminBaseDn":"","LdapAdminFilter":"(memberOf=cn=admin,ou=groups,${LDAP_DC_DOMAIN})",
"EnableLdapAdminFilterMemberUid":false,"LdapUidAttribute":"uid","LdapUsernameAttribute":"cn","LdapPasswordAttribute":"userPassword",
"EnableLdapProfileImageSync":false,"RemoveImagesNotInLdap":false,"LdapProfileImageAttribute":"jpegphoto","LdapProfileImageFormat":"Default",
"LdapClientCertPath":"","LdapClientKeyPath":"","LdapRootCaPath":"","CreateUsersFromLdap":true,"AllowPassChange":false,
"EnableAllFolders":true,"EnabledFolders":[],"PasswordResetUrl":""}'; then
echo "ERROR: LDAP Plugin Setup Failed."
exit 1
fi
''}
${pkgs.sqlite}/bin/sqlite3 ${serverCfg.path.config}/jellyfin/data/data/jellyfin.db <<EOF
INSERT OR IGNORE INTO ApiKeys (Id, AccessToken, Name, DateCreated, DateLastActivity)
VALUES ( 1, "$HOMEPAGE_VAR_JELLYFIN_API", 'Home', strftime('%Y-%m-%d %H:%M:%S', 'now'), strftime('%Y-%m-%d %H:%M:%S', 'now'));
EOF
echo "Completed Setup"
'';
};
};
}
@@ -0,0 +1,213 @@
{ config, containerCfg, pkgs, lib, builder, name,... }:
let
version = "31";
serverCfg = config.syscfg.server;
mediaCfg = config.syscfg.media;
backgroundImage = if mediaCfg.banner.png != null then mediaCfg.banner.png else mediaCfg.bg;
backgroundFileName = builtins.baseNameOf (toString backgroundImage);
logoPngFileName = builtins.baseNameOf (toString mediaCfg.logo.png);
logoSvgFileName = builtins.baseNameOf (toString mediaCfg.logo.svg);
logoIcoFileName = builtins.baseNameOf (toString mediaCfg.logo.ico);
logoPngMount = "/var/www/html/theming/${logoPngFileName}";
logoSvgMount = "/var/www/html/theming/${logoSvgFileName}";
logoIcoMount = "/var/www/html/theming/${logoIcoFileName}";
backgroundMount = "/var/www/html/theming/${backgroundFileName}";
in {
requires = {
secrets = [ name ];
databases = [ name ];
};
runtime = {
paths = [{
path="${serverCfg.path.config}/nextcloud";
owner = "33:33";
mode = "0755";
}];
containers = {
server = builder.mkContainer {
tmpfs = true;
subdomain = containerCfg.subdomain;
image = "nextcloud:${version}";
port = 80;
secret = name;
extraEnv = {
REDIS_HOST = builder.host;
POSTGRES_HOST = builder.host;
POSTGRES_USER = "nextcloud_user";
POSTGRES_DB = "nextcloud_db";
AUTHENTIK_POSTGRESQL__SSLMODE = "disable";
NEXTCLOUD_TRUSTED_DOMAINS = "${containerCfg.subdomain}.${serverCfg.domain} nextcloud-server";
# SMTP_HOST = serverCfg.mail.server;
# SMTP_NAME = "mail_user";
# SMTP_PASSWORD = "mail_password";
# MAIL_FROM_ADDRESS = "${containerCfg.subdomain}@${serverCfg.domain}";
# MAIL_DOMAIN = serverCfg.mail.domain;
TRUSTED_PROXIES = "10.10.0.0/16 192.168.0.0/16";
NEXTCLOUD_DATA_DIR = "/var/www/html/data";
};
extraLabels = {
"traefik.http.routers.${containerCfg.subdomain}.middlewares" = "sts_headers,${containerCfg.subdomain}-caldav";
"traefik.http.middlewares.${containerCfg.subdomain}-caldav.redirectregex.permanent" = "true";
"traefik.http.middlewares.${containerCfg.subdomain}-caldav.redirectregex.regex" = "https://(.*)/.well-known/(?:card|cal)dav";
"traefik.http.middlewares.${containerCfg.subdomain}-caldav.redirectregex.replacement" = "https://$1/remote.php/dav";
"traefik.http.middlewares.sts_headers.headers.stsSeconds" = "15552000";
"traefik.http.middlewares.sts_headers.headers.stsIncludeSubdomains" = "true";
};
overrides = {
ports = if containerCfg.port!=null then [ "${toString containerCfg.port}:80" ] else [];
volumes = [
"${serverCfg.path.config}/nextcloud:/var/www/html"
"${serverCfg.path.cloud}:/var/www/html/data"
"${mediaCfg.logo.png}:${logoPngMount}:ro"
"${mediaCfg.logo.svg}:${logoSvgMount}:ro"
"${mediaCfg.logo.ico}:${logoIcoMount}:ro"
"${backgroundImage}:${backgroundMount}:ro"
];
};
};
};
setup = {
trigger = "server";
script = pkgs.writeShellScript "setup" ''
# Define the command wrapper
OCC="${pkgs.podman}/bin/podman --events-backend=none exec --env-file ${config.sops.secrets."CUSTOM".path} -e DOMAIN=${serverCfg.domain} -u www-data nextcloud-server php occ"
echo "Waiting for Nextcloud container to start..."
until $OCC status > /dev/null 2>&1; do
sleep 2
done
INSTALLED=$($OCC status --output=json | grep -o '"installed":true')
if [ -z "$INSTALLED" ]; then
echo "Running first-time setup..."
$OCC maintenance:install \
--admin-user "$DEFAULT_ADMIN_USERNAME" \
--admin-pass "$DEFAULT_ADMIN_PASSWORD"
fi
if [ -z "$INSTALLED" ] || [ -f "/tmp/force-nextcloud-setup" ]; then
rm -f "/tmp/force-nextcloud-setup"
echo "Applying Settings..."
$OCC config:system:set default_phone_region --value="CH"
$OCC config:system:set overwriteprotocol --value="https"
$OCC config:app:set core backgroundjobs_mode --value="cron"
$OCC config:system:set maintenance_window_start --type=integer --value=1
$OCC config:system:set default_language --value="en"
$OCC config:system:set default_locale --value="en_CH"
echo "Applying Apps..."
$OCC app:disable activity || true
$OCC app:disable app_api || true
$OCC app:disable comments || true
$OCC app:disable firstrunwizard || true
$OCC config:system:set show_first_run_wizard --type=bool --value=false
$OCC app:disable nextcloud_announcements || true
$OCC app:disable oauth2 || true
$OCC app:disable recommendations || true
$OCC app:disable sharebymail || true
$OCC app:disable support || true
$OCC app:disable survey_client || true
$OCC app:disable updatenotification || true
$OCC app:disable user_status || true
$OCC app:install calendar || true
$OCC app:install contacts || true
$OCC app:install camerarawpreviews || true
$OCC app:install cospend || true
$OCC app:install deck || true
$OCC app:install files_markdown || true
$OCC app:install forms || true
$OCC app:install groupfolders || true
$OCC app:install ownpad || true
$OCC app:install previewgenerator || true
$OCC app:install richdocuments || true
${lib.optionalString (serverCfg.containers ? collabora == false) ''$OCC app:install richdocumentscode || true''}
# $OCC app:install side_menu || true
$OCC app:install spreed || true
$OCC app:install teamfolders || true
${lib.optionalString (serverCfg.containers ? authentik) ''$OCC app:install user_saml || true''}
echo "Applying Apps Settings..."
$OCC config:system:set enabledPreviewProviders --value='["OC\\Preview\\Movie", "OC\\Preview\\PNG", "OC\\Preview\\JPEG", "OC\\Preview\\GIF", "OC\\Preview\\HEIC", "OC\\Preview\\RAW"]' --type=json
$OCC config:app:set cospend allow_federation --value="yes"
${lib.optionalString (serverCfg.containers ? ethercalc) ''
$OCC config:app:set ownpad ownpad_ethercalc_enable --value="yes"
$OCC config:app:set ownpad ownpad_ethercalc_host --value="https://${serverCfg.containers.ethercalc.subdomain}.${serverCfg.domain}"
''}
${lib.optionalString (serverCfg.containers ? etherpad) ''
$OCC config:app:set ownpad ownpad_etherpad_enable --value="yes"
$OCC config:app:set ownpad ownpad_etherpad_host --value="https://${serverCfg.containers.etherpad.subdomain}.${serverCfg.domain}"
''}
${lib.optionalString (serverCfg.containers ? collabora) ''
$OCC config:app:set richdocuments wopi_url --value="https://${serverCfg.containers.collabora.subdomain}.${serverCfg.domain}/"
$OCC config:app:set richdocuments public_wopi_url --value="https://${serverCfg.containers.collabora.subdomain}.${serverCfg.domain}"
$OCC config:app:set richdocuments wopi_allowlist --value="10.0.0.0/8"
''}
${lib.optionalString (serverCfg.containers ? authentik) ''
$OCC saml:config:set 1 --general-idp0_display_name="authentik"
$OCC saml:config:set 1 --general-uid_mapping="http://schemas.goauthentik.io/2021/02/saml/username"
$OCC saml:config:set 1 --idp-entityId="https://${serverCfg.containers.authentik.subdomain}.${serverCfg.domain}"
$OCC saml:config:set 1 --idp-singleSignOnService.url="https://${serverCfg.containers.authentik.subdomain}.${serverCfg.domain}/application/saml/nextcloud/sso/binding/redirect/"
$OCC saml:config:set 1 --idp-singleLogoutService.url="https://${serverCfg.containers.authentik.subdomain}.${serverCfg.domain}/application/saml/nextcloud/slo/binding/redirect/"
AUTHENTIK_CERT=$(${pkgs.postgresql}/bin/psql -h localhost -U authentik_user -d authentik_db -At -c "SELECT certificate_data FROM authentik_crypto_certificatekeypair WHERE name = 'authentik Self-signed Certificate';")
$OCC saml:config:set 1 --idp-x509cert="$AUTHENTIK_CERT"
$OCC saml:config:set 1 --saml-attribute-mapping-displayName_mapping="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"
$OCC saml:config:set 1 --saml-attribute-mapping-email_mapping="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"
$OCC saml:config:set 1 --saml-attribute-mapping-group_mapping="http://schemas.xmlsoap.org/claims/Group"
$OCC config:app:set user_saml general-allowed_groups --value="admin,cloud"
$OCC group:add cloud || true
$OCC group:adduser admin $DEFAULT_ADMIN_USERNAME
$OCC config:app:set user_saml general-group_provisioning --value="0"
$OCC config:app:set user_saml general-require_provisioning_groups --value="1"
''}
# configure side_menu ...
FOLDERS=$($OCC teamfolders:list --format=json)
${builtins.concatStringsSep "\n" (map (name: ''
if ! echo "$FOLDERS" | grep -q '"name":"${name}"'; then
$OCC teamfolders:create "${name}"
fi
'') containerCfg.extra.teamFolders or [])}
SERVERS=$($OCC federation:list-servers --format=json)
${builtins.concatStringsSep "\n" (map (domain: ''
if ! echo "$SERVERS" | grep -q "${domain}"; then
$OCC federation:add-server "https://${domain}"
fi
'') containerCfg.extra.federatedServers or [])}
$OCC config:app:set systemtags allow_user_creating --value="no"
else
echo "Nextcloud is already installed. Skipping setup."
fi
echo "Applying Theme..."
$OCC config:app:set theming url --value="https://${containerCfg.subdomain}.${serverCfg.domain}"
${lib.optionalString (containerCfg.extra ? name) ''$OCC config:app:set theming name --value="${containerCfg.extra.name}"''}
${lib.optionalString (containerCfg.extra ? slogan) ''$OCC config:app:set theming slogan --value="${containerCfg.extra.slogan}"''}
$OCC config:app:set theming background_color --value="${serverCfg.colorScheme.palette.base02}"
$OCC config:app:set theming primary_color --value="${serverCfg.colorScheme.palette.base0C}"
$OCC theming:config logo "${logoPngMount}"
$OCC theming:config logoheader "${logoSvgMount}"
$OCC theming:config favicon "${logoIcoMount}"
$OCC theming:config background "${backgroundMount}"
$OCC config:app:set serverinfo token --value="$HOMEPAGE_VAR_NEXTCLOUD_API"
echo "Maintenance..."
$OCC app:update --all
$OCC maintenance:repair --include-expensive --no-interaction
$OCC db:add-missing-indices --no-interaction
echo "Completed Setup"
'';
};
cron = [ "*/5 * * * * root ${pkgs.podman}/bin/podman --events-backend=none exec -u www-data nextcloud-server php -f /var/www/html/cron.php" ];
};
}
@@ -0,0 +1,77 @@
{ config, containerCfg, pkgs, lib, builder, name,... }:
let
serverCfg = config.syscfg.server;
version = "5.1.4";
in {
runtime = {
paths = [
{ path="${serverCfg.path.config}/openhab/conf"; owner="1000:1000"; mode = "0755"; }
{ path="${serverCfg.path.config}/openhab/userdata"; owner="1000:1000"; mode = "0755"; }
{ path="${serverCfg.path.config}/openhab/addons"; owner="1000:1000"; mode = "0755"; }
];
containers = {
server = builder.mkContainer {
subdomain = containerCfg.subdomain;
image = "openhab/openhab:${version}";
port = 8080;
extraEnv = {
USER_ID = "1000";
GROUP_ID = "1000";
CRYPTO_POLICY = "unlimited";
OPENHAB_HTTP_PORT = "8080";
};
extraOptions = [
"--network=host"
"--cap-add=NET_ADMIN"
"--cap-add=NET_RAW"
"--no-healthcheck"
];
overrides = {
volumes = [
"${serverCfg.path.config}/openhab/conf:/openhab/conf"
"${serverCfg.path.config}/openhab/userdata:/openhab/userdata"
"${serverCfg.path.config}/openhab/addons:/opt/openhab/addons"
"/var/run/dbus/system_bus_socket:/var/run/dbus/system_bus_socket:ro"
];
};
};
};
setup = {
trigger = "server";
envFile = [ config.sops.secrets."CUSTOM".path ];
script = pkgs.writeShellScript "setup" ''
# Pre-generate openHAB directories on the host
OHAB="${pkgs.podman}/bin/podman --events-backend=none exec openhab-server /openhab/runtime/bin/client -u openhab -p habopen"
sleep 20
exit 0
$OHAB openhab:users add $DEFAULT_ADMIN_USERNAME $DEFAULT_ADMIN_PASSWORD administrator
$OHAB feature:list
$OHAB openhab:addons install persistance-mapdb
$OHAB openhab:addons install persistance-influxdb
$OHAB openhab:addons install ui-basic
$OHAB openhab:addons install automation-jsscripting
$OHAB openhab:addons install binding-telegram
$OHAB openhab:addons install binding-matter
$OHAB openhab:addons install binding-mqtt
$OHAB openhab:addons install binding-bluetooth
$OHAB openhab:addons install binding-zigbee
$OHAB openhab:addons install binding-chromecast
$OHAB openhab:addons install binding-astro
$OHAB openhab:addons install binding-meteoblue
$OHAB openhab:addons install binding-publictransportswitzerland
#IF APPLE DEVICE: HomeKit (siri/apple bridge)
#IF UBIQUITY NET: Unifi + UnifiProtect (net/cam bridge)
#IF YAMAHA+EPSON: EpsonProjector + Yamaha (projector and sound)
#IF BAMBULAB DEVICE: BambuLab (notify print state)
#IF GARDENA DEVICE: Gardena (smart watering)
#Extra: AndroidTV/Jellyfin (Bind with lights + more)
'';
};
};
}
@@ -0,0 +1,87 @@
{ config, containerCfg, pkgs, lib, builder, name,... }:
let
version= "latest";
serverCfg = config.syscfg.server;
settings = pkgs.writeText"settings.yml" (pkgs.lib.generators.toYAML {}{
use_default_settings = true;
brand = {
issue_url = "";
docs_url = "";
public_instances = "";
wiki_url = "";
custom = {
links = {
"Home" = "https://${serverCfg.domain}";
# "Status" = "https://status.${serverCfg.domain}";
};
};
pwa_colors = {
theme_color_light = "${serverCfg.colorScheme.palette.base0C}";
background_color_light = "${serverCfg.colorScheme.palette.base07}";
theme_color_dark = "${serverCfg.colorScheme.palette.base0C}";
background_color_dark = "${serverCfg.colorScheme.palette.base02}";
theme_color_black = "${serverCfg.colorScheme.palette.base0C}";
background_color_black = "${serverCfg.colorScheme.palette.base01}";
};
};
general = {
debug = false;
instance_name = if containerCfg.extra ? instanceName then containerCfg.extra.instanceName else "SearXNG";
privacypolicy_url = false;
donation_url = false;
contact_url = false;
enable_metrics = false;
};
search = {
safe_search = 0;
autocomplete = if containerCfg.extra ? autocomplete then containerCfg.extra.autocomplete else "";
languages = [ "all" "en" "en-US" "ja" "de-CH" "fr-CH" "nb" ];
};
server = {
# secret_key = ""; SET BY ENV VAR
};
ui = {
default_locale = if containerCfg.extra ? defaultLocale then containerCfg.extra.defaultLocale else "en";
# query_in_title = "true";
#default_theme = "custom";
custom_css = "footer { display: none !important; }";
};
# categories_as_tabs = {
# general = {};
# images ={};
# videos = {};
# news = {};
# files = {};
# };
plugins = {
"searx.plugins.infinite_scroll.SXNGPlugin".active = true;
"searx.plugins.tracker_url_remover.SXNGPlugin".active = true;
};
});
in {
requires.secrets = [ name ];
runtime = {
containers = {
server = builder.mkContainer {
subdomain = containerCfg.subdomain;
image = "searxng/searxng:${version}";
port = 8080;
secret = name;
extraEnv = {
SEARXNG_BASE_URL = "https://${containerCfg.subdomain}.${serverCfg.domain}";
SEARXNG_PORT = "8080";
SEARXNG_BIND_ADDRESS = "[::]";
SEARXNG_PUBLIC_INSTANCE = "false";
SEARXNG_SETTINGS_PATH = "/etc/searxng/settings.yml";
#SEARXNG_VALKEY_URL = "valkey://user:password@${builder.host}:6379/0}";
};
overrides = {
volumes = [
"${settings}:/etc/searxng/settings.yml"
];
};
};
};
};
}
@@ -0,0 +1,90 @@
{ config, containerCfg, pkgs, lib, builder, name,... }:
let
version = "latest";
serverCfg = config.syscfg.server;
in {
runtime = {
paths = [{
path = "${serverCfg.path.config}/selfmark/";
mode = "0444";
}];
containers = {
server = builder.mkContainer {
authentik = true;
subdomain = containerCfg.subdomain;
subpath = containerCfg.subpath;
image = "ghcr.io/calibrain/shelfmark:${version}";
port = 8080;
extraEnv = {
# HARDCOVER_API_KEY = ""; #FROM SOPS
# AA_DONATOR_KEY = ""; #FROM SOPS
# PROWLARR_API_KEY = ""; #FROM SOPS
FLASK_PORT = "8080";
PUID = "1000";
PGID = "1000";
USING_TOR = "false";
ONBOARDING = "false";
SUPPORTED_FORMATS = "epub,mobi,azw3,fb2,djvu,cbz,cbr,pdf";
SUPPORTED_AUDIOBOOK_FORMATS = "mp3, m4b";
BOOK_LANGUAGE = "en,fr"; # ,de,jp";
SEARCH_MODE = "universal";
AA_DEFAULT_SORT = "relevance";
METADATA_PROVIDER = "openlibrary";
INGEST_DIR = "/books";
BOOKS_OUTPUT_MODE = "/output";
FILE_ORGANIZATION = "organize";
TEMPLATE_RENAME = "{Author} - {Title} ({Year})";
TEMPLATE_ORGANIZE = "{Author}/{Title} ({Year})";
HARDLINK_TORRENTS = "false";
FILE_ORGANIZATION_AUDIOBOOK = "organize";
TEMPLATE_RENAME_AUDIOBOOK = "{Author} - {Title}";
TEMPLATE_ORGANIZE_AUDIOBOOK = "{Author}/{Title} ({Year})";
HARDCOVER_ENABLED = "true";
HARDCOVER_DEFAULT_SORT = "relevance";
OPENLIBRARY_ENABLED = "true";
OPENLIBRARY_DEFAULT_SORT = "relevance";
DIRECT_DOWNLOAD_ENABLED = "true";
USE_CF_BYPASS = "true";
AA_BASE_URL = "auto";
AA_MIRROR_URLS = "https://annas-archive.gl,https://annas-archive.pk,https://annas-archive.gd,";
LIBGEN_MIRROR_URLS = "https://libgen.li,https://libgen.vg,https://libgen.la,https://libgen.bz,https://libgen.gl";
ZLIB_MIRROR_URLS = "https://z-lib.sk,https://z-library.gs,https://z-lib.fm,https://z-lib.gd,https://z-lib.gl";
# WELIB_MIRROR_URLS = "https://welib.org"; #avoid
} // lib.optionalAttrs(containerCfg.subpath != null) {
BASE_PATH = "/${containerCfg.subpath}";
URL_BASE = "/${containerCfg.subpath}";
} // lib.optionalAttrs(serverCfg.containers?calibre) {
CALIBRE_WEB_URL = "https://${serverCfg.containers.calibre.subdomain}.${serverCfg.domain}";
} // lib.optionalAttrs(serverCfg.containers?authentik) {
AUTH_METHOD = "proxy";
PROXY_AUTH_USER_HEADER = "X-authentik-username";
PROXY_AUTH_ADMIN_GROUP_HEADER = "X-authentik-groups";
PROXY_AUTH_ADMIN_GROUP_NAME = "admin";
} // lib.optionalAttrs(serverCfg.containers?servarr && builtins.elem "prowlarr" serverCfg.containers.servarr.extra.modules) ({
PROWLARR_ENABLED = "true";
PROWLARR_URL = "http://servarr-prowlarr:8989";
} // lib.optionalAttrs(serverCfg.containers?transmission) {
PROWLARR_TORRENT_CLIENT = "transmission";
TRANSMISSION_URL = "http://transmission-server:9091";
}) // lib.optionalAttrs(serverCfg.containers?servarr && builtins.elem "flaresolverr" serverCfg.containers.servarr.extra.modules) {
USING_EXTERNAL_BYPASSER = "true";
EXT_BYPASSER_URL = "http://servarr-flaresolverr:8191";
EXT_BYPASSER_PATH = "/v1";
EXT_BYPASSER_TIMEOUT = "60000";
};
overrides = {
volumes = [
"${serverCfg.path.dlComplete}:/books:rw"
"${serverCfg.path.book}:/output:rw"
"${serverCfg.path.config}/selfmark:/config:rw"
];
};
};
};
};
}
File diff suppressed because one or more lines are too long
@@ -0,0 +1,53 @@
{ config, containerCfg, pkgs, lib, builder, name,... }:
let
version = "stable";
serverCfg = config.syscfg.server;
in {
requires = {
secrets = [ name ];
databases = [ name ];
};
runtime = {
containers = {
server = builder.mkContainer {
subdomain = containerCfg.subdomain;
image = "ghcr.io/suwayomi/suwayomi-server:${version}";
port = 4567;
secret = name;
extraEnv = {
BIND_PORT = "4567";
AUTH_MODE = "ui_login";
WEB_UI_ENABLED = "true";
WEB_UI_FLAVOR = "WebUI";
# AUTO_DOWNLOAD_CHAPTERS = true;
# AUTO_DOWNLOAD_EXCLUDE_UNREAD = true;
# AUTO_DOWNLOAD_NEW_CHAPTERS_LIMIT = 0;
# AUTO_DOWNLOAD_IGNORE_REUPLOADS = false;
# DOWNLOAD_CONVERSIONS = {};
# SERVE_CONVERSIONS = {};
# MAX_SOURCES_IN_PARALLEL = 6;
# UPDATE_EXCLUDE_UNREAD = true;
# UPDATE_EXCLUDE_STARTED = true;
# UPDATE_EXCLUDE_COMPLETED = true;
# UPDATE_INTERVAL = 12; #Hours
# UPDATE_MANGA_INFO = false;
DATABASE_TYPE = "POSTGRESQL";
DATABASE_URL = "postgresql://${builder.host}/suwayomi_db";
DATABASE_USERNAME = "suwayomi_user";
FLARESOLVERR_ENABLED = lib.boolToString (builtins.elem "flaresolverr" (((config.syscfg.server.containers.servarr or {}).extra or {}).modules or []));
FLARESOLVERR_URL = "http://servarr-flaresolverr:8191";
EXTENSION_REPOS = "[\"https://raw.githubusercontent.com/keiyoushi/extensions/repo/index.min.json\"]"; #https://raw.githubusercontent.com/keiyoushi/extensions/repo/index.min.json
};
overrides = {
volumes = [
"${serverCfg.path.manga}:/home/suwayomi/.local/share/Tachidesk/downloads"
# "${serverCfg.path.config}/suwayomi:/home/suwayomi/.local/share/Tachidesk"
];
};
};
};
};
}
@@ -0,0 +1,89 @@
{ config, containerCfg, pkgs, lib, builder, name,... }:
let
serverCfg = config.syscfg.server;
image = pkgs.dockerTools.streamLayeredImage {
name = "traefik";
tag = pkgs.traefik.version;
contents = with pkgs;[ cacert tzdata ];
config = {
Entrypoint = [ "${pkgs.traefik}/bin/traefik" ];
WorkingDir = "/";
};
};
in {
requires.secrets = [ name ];
runtime = {
paths = [{
path="${serverCfg.path.config}/traefik";
owner = "1000:1000";
mode = "0755";
}];
containers = {
server = builder.mkContainer {
imageStream = image;
subdomain = containerCfg.subdomain;
port = 8080;
secret = name;
extraLabels = {
"traefik.http.routers.${containerCfg.subdomain}.priority" = "10";
"traefik.http.routers.${containerCfg.subdomain}.service" = "api@internal";
"traefik.http.routers.${containerCfg.subdomain}.middlewares" = if serverCfg.containers?authentik then "authentik" else "";
} // (if serverCfg.containers?authentik then {
"traefik.http.middlewares.authentik.forwardauth.maxResponseBodySize" = "10485760";
"traefik.http.middlewares.authentik.forwardauth.address" = "http://authentik-server:9000/outpost.goauthentik.io/auth/traefik";
"traefik.http.middlewares.authentik.forwardauth.trustForwardHeader" = "true";
"traefik.http.middlewares.authentik.forwardauth.authResponseHeaders" = "X-authentik-username,X-authentik-groups,X-authentik-email,X-authentik-name,X-authentik-uid,X-authentik-jwt,X-authentik-meta-jwks,X-authentik-meta-outpost,X-authentik-meta-provider,X-authentik-meta-app,X-authentik-meta-version";
} else {}) // (if serverCfg.containers?umami then {
"traefik.http.middlewares.umami-global.plugin.umami-feeder.umamiHost" = "http://umami-server:3000";
"traefik.http.middlewares.umami-global.plugin.umami-feeder.umamiUsername" = "admin";
"traefik.http.middlewares.umami-global.plugin.umami-feeder.umamiPassword" = "umami";
"traefik.http.middlewares.umami-global.plugin.umami-feeder.createNewWebsites" = "true";
} else {}) // (if containerCfg.extra ? provider || serverCfg.domain != "localhost" then {
"traefik.http.routers.${containerCfg.subdomain}.tls.certresolver" = "default";
"traefik.http.routers.${containerCfg.subdomain}.tls.domains[0].main" = "${serverCfg.domain}";
"traefik.http.routers.${containerCfg.subdomain}.tls.domains[0].sans" = "*.${serverCfg.domain}";
} else {});
extraEnv = { };
overrides = {
cmd = [
"--api"
"--log.level=INFO"
"--providers.docker=true"
"--global.checknewversion=false"
"--global.sendanonymoususage=false"
"--api.insecure=true"
"--api.dashboard=true"
"--providers.docker.exposedByDefault=false"
"--entrypoints.web.address=:80"
"--entrypoints.web-secure.address=:443"
"--entrypoints.web.http.redirections.entrypoint.to=web-secure"
"--entrypoints.web.http.redirections.entrypoint.scheme=https"
"--entrypoints.web-secure.transport.respondingtimeouts.readtimeout=0s"
"--entrypoints.web-secure.proxyprotocol.trustedips=127.0.0.1/32,192.168.1.1/16,10.10.0.0/16"
] ++ (if serverCfg.containers ? umami then [
"--experimental.plugins.umami-feeder.moduleName=github.com/astappiev/traefik-umami-feeder"
"--experimental.plugins.umami-feeder.version=v1.4.1"
"--entrypoints.web-secure.http.middlewares=umami-global@docker"
] else []) ++ (if containerCfg.extra ? provider then [
"--certificatesresolvers.default.acme.email=acme@${serverCfg.domain}"
"--certificatesresolvers.default.acme.dnschallenge=true"
"--certificatesresolvers.default.acme.dnschallenge.provider=${containerCfg.extra.provider}"
"--certificatesresolvers.default.acme.storage=/custom/acme.json"
] else []) ++ (if serverCfg.domain != "localhost" then [
"--certificatesresolvers.default.acme.httpchallenge=false"
"--certificatesresolvers.default.acme.tlschallenge=true"
] else []);
ports = [ "443:443" "80:80" ] ++ (if containerCfg.port!=null then [ "${toString containerCfg.port}:8080" ] else []);
volumes = [
"/var/run/podman/podman.sock:/var/run/docker.sock"
# "${serverCfg.path.config}/traefik/access.log:/etc/traefik/access.log"
"${serverCfg.path.config}/traefik:/custom"
];
};
};
};
};
}
@@ -0,0 +1,59 @@
{ config, containerCfg, pkgs, lib, builder, name, ... }:
let
serverCfg = config.syscfg.server;
image = pkgs.dockerTools.streamLayeredImage {
name = pkgs.transmission_4.name;
tag = pkgs.transmission_4.version;
contents = [ pkgs.cacert ];
config = {
Cmd = [ "${pkgs.transmission_4}/bin/transmission-daemon" "--foreground" "--config-dir" "/config" ];
ExposedPorts = {
"9091/tcp" = {};
"51413/tcp" = {}; "51413/udp" = {};
};
};
};
in {
runtime = {
paths = [{
path = "${serverCfg.path.config}/transmission";
owner = "1000:1000";
mode = "0755";
}];
containers = {
server = builder.mkContainer {
authentik = true;
subdomain = containerCfg.subdomain;
subpath = containerCfg.subpath;
imageStream = image;
port = 9091;
extraEnv = {
PUID = "1000";
PGID = "1000";
WHITELIST = "";# 127.0.0.1,::1,10.*";
# HOST_WHITELIST = "traefik-server,authentik-server,authentik-worker";
};
overrides = {
volumes = [
"${serverCfg.path.dlComplete}:/downloads/complete"
"${serverCfg.path.dlIncomplete}:/downloads/incomplete"
"${serverCfg.path.config}/transmission:/config"
];
};
};
};
setup = {
trigger = "server";
envFile = [ config.sops.secrets."CUSTOM".path ];
script = pkgs.writeShellScript "setup" ''
${pkgs.gettext}/bin/envsubst < "${../data/transmission/settings.json}" > "${serverCfg.path.config}/transmission/config/settings.json"
'';
};
};
}
+3
View File
@@ -0,0 +1,3 @@
{...}:{
}
+54
View File
@@ -0,0 +1,54 @@
{ config, containerCfg, pkgs, lib, builder, name,... }:
let
serverCfg = config.syscfg.server;
# Umami image built from nixpkgs
image = pkgs.dockerTools.streamLayeredImage {
name = pkgs.umami.name;
tag = pkgs.umami.version;
contents = with pkgs; [ cacert openssl ];
config = {
# Umami in nixpkgs typically provides a binary or script to start the server
Entrypoint = [ "${pkgs.umami}/bin/umami-server" ];
ExposedPorts = { "3000/tcp" = {}; };
Env = [ "NODE_ENV=production" ];
};
};
in {
requires = {
secrets = [ name ];
databases = [ name ];
};
runtime = {
paths = [{
path = "${serverCfg.path.config}/umami/";
mode = "0444";
}];
containers = {
server = builder.mkContainer {
authentik = true;
tmpfs = true;
subdomain = containerCfg.subdomain;
image = "${pkgs.umami.name}:${pkgs.umami.version}";
imageStream = image;
port = 3000;
secret = name;
extraEnv = {
PORT = "3000";
# HOSTNAME = "${containerCfg.subdomain}.${serverCfg.domain}";
DATABASE_TYPE = "postgresql";
REDIS_URL = "redis://${builder.host}";
CLIENT_IP_HEADER = "X-Forwarded-For";
BASE_PATH = lib.optionalString (containerCfg.subpath or null != null) "/${containerCfg.subpath}";
# DISABLE_LOGIN = "1";#(if serverCfg.containers?authentik then "1" else "0");
};
overrides = {
cmd = [ "start" ]; # Specific command for the umami binary
};
};
};
};
}
+129
View File
@@ -0,0 +1,129 @@
{ config, lib, pkgs, serverCfg }:
let
mkRouterName = { subdomain, subpath ? null }:
if subpath != null
then "${subdomain}-${lib.strings.sanitizeDerivationName subpath}"
else subdomain;
getOr = attrs: path: default: lib.attrByPath path default attrs;
mkTmpfsOption = size: "--tmpfs=/tmp:rw,noexec,nosuid,size=${size}";
mkAuthentikLabels =
{ subdomain
, subpath ? null
, routerName ? mkRouterName { inherit subdomain subpath; }
, middleware ? "authentik"
}:
lib.optionalAttrs (serverCfg.containers ? authentik) {
"traefik.http.routers.${routerName}.middlewares" = middleware;
};
contBuilder =
{ image ? null, imageStream ? null, imageFile ? null
, secret ? null
, subdomain ? null, subpath?null, port ? null
, authentik ? false
, tmpfs ? false
, tmpfsSize ? "512m"
, extraEnv ? { }, extraLabels ? { }, extraOptions ? [ ]
, overrides ? { }
}:
let
routerName = mkRouterName { inherit subdomain subpath; };
base = {
image = if imageStream != null then "${imageStream.imageName}:${imageStream.imageTag}"
else if imageFile != null then "${imageFile.imageName}:${imageFile.imageTag}" else image;
imageStream = imageStream;
imageFile = imageFile;
environmentFiles = if secret!=null then [ config.sops.secrets."${lib.toUpper secret}".path ] else [];
environment = {
TZ = config.time.timeZone;
} // extraEnv;
labels = (if subdomain!=null then ({
"traefik.enable" = "true";
"traefik.http.routers.${routerName}.entrypoints" = "web-secure";
"traefik.http.routers.${routerName}.rule" = if subpath != null
then "Host(`${subdomain}.${serverCfg.domain}`) && PathPrefix(`/${subpath}`)"
else "Host(`${subdomain}.${serverCfg.domain}`)";
"traefik.http.routers.${routerName}.tls" = "true";
} // lib.optionalAttrs (port!=null) {
"traefik.http.services.${routerName}.loadbalancer.server.port" = toString port;
}) else {
"traefik.enable" = "false";
})
// lib.optionalAttrs authentik (mkAuthentikLabels { inherit subdomain subpath routerName; })
// extraLabels;
extraOptions = [
"--add-host=host.containers.internal:host-gateway"
]
++ lib.optional tmpfs (mkTmpfsOption tmpfsSize)
++ extraOptions;
};
in lib.recursiveUpdate base overrides;
vmBuilder = { name, vm }: ((import "${pkgs.path}/nixos/lib/eval-config.nix" {
system = "x86_64-linux";
modules = [ vm.cfg
({ config, lib, modulesPath, ... }: {
imports = [
"${modulesPath}/profiles/qemu-guest.nix"
"${modulesPath}/virtualisation/qemu-vm.nix"
];
networking.hostName = name;
networking.useDHCP = true;
networking.firewall.enable = false;
services.qemuGuest.enable = true;
system.stateVersion = "26.05";
virtualisation = {
memorySize = vm.memory or 2048;
cores = vm.cores or 2;
forwardPorts = let
parsePortString = port: {
from = "host";
host.port = port;
guest.port = port;
};
in if (vm ? portForward && vm.portForward != null) then map parsePortString vm.portForward else [];
};})
];
}).config.system.build.vm);
in {
mkContainer = contBuilder;
mkVm = vmBuilder;
mkApp = name: app:
{
inherit name;
requires = {
secrets = getOr app [ "requires" "secrets" ] [ ];
databases = getOr app [ "requires" "databases" ] [ ];
};
exports = {
authentik = {
blueprints = getOr app [ "exports" "authentik" "blueprints" ] [ ];
};
};
runtime = {
paths = getOr app [ "runtime" "paths" ] [ ];
containers = getOr app [ "runtime" "containers" ] { };
vm = getOr app [ "runtime" "vm" ] null;
cron = getOr app [ "runtime" "cron" ] [ ];
setup = {
trigger = "";
script = null;
envFile = [ ];
} // getOr app [ "runtime" "setup" ] { };
};
};
mkData = { name, dir, vars?{} }: pkgs.runCommand name vars ''
mkdir -p $out
cp -r ${./data + "/${dir}"}/. $out/
find $out -type f | while read file; do
${lib.concatStringsSep "\n" (lib.mapAttrsToList (n: v: ''
substituteInPlace "$file" --replace "@${n}@" "${toString v}"
'') vars)}
done
'';
host = "host.containers.internal";
hostIp = if (config.virtualisation.podman.defaultNetwork.settings ? subnets)
then (builtins.elemAt config.virtualisation.podman.defaultNetwork.settings.subnets 0).gateway
else "10.88.0.1";
}
@@ -0,0 +1,70 @@
version: 1
metadata:
name: "Initial User Setup"
labels:
blueprint-type: core
entries:
# Optionally, disable the default enrollment flow entirely
- model: authentik_flows.flow
identifiers:
slug: "default-source-enrollment"
attrs:
designation: "enrollment"
enabled: false
# --- GROUPS ---
- model: authentik_core.group
identifiers:
name: "admin"
attrs:
is_superuser: true
- model: authentik_core.group
identifiers:
name: "cloud"
attrs:
is_superuser: false
- model: authentik_core.group
identifiers:
name: "dev"
attrs:
is_superuser: false
- model: authentik_core.group
identifiers:
name: "flix"
attrs:
is_superuser: false
- model: authentik_core.group
identifiers:
name: "family"
attrs:
is_superuser: false
# --- ADMIN USERS ---
- model: authentik_core.user
identifiers:
username: !Env DEFAULT_ADMIN_USERNAME
attrs:
name: !Env DEFAULT_ADMIN_USERNAME
email: !Env DEFAULT_ADMIN_EMAIL
password: !Env DEFAULT_ADMIN_PASSWORD
path: "users"
groups:
- !Find [authentik_core.group, [name, "admin"]]
# Disable the Initial Setup Flow
- model: authentik_flows.flow
identifiers:
slug: "initial-setup"
attrs:
authentication: "require_superuser"
enabled: false
# Disable the default 'akadmin' if it exists
- model: authentik_core.user
identifiers:
username: "akadmin"
attrs:
is_active: false
@@ -0,0 +1,13 @@
version: 1
metadata:
name: "Branding Setup"
entries:
- model: authentik_brands.brand
identifiers:
domain: "@AUTHENTIK_DOMAIN@"
attrs:
domain: "@AUTHENTIK_DOMAIN@"
branding_title: "@AUTHENTIK_BRANDING_TITLE@"
branding_logo: "@AUTHENTIK_BRANDING_LOGO@"
branding_favicon: "@AUTHENTIK_BRANDING_FAVICON@"
branding_default_flow_background: "@AUTHENTIK_BRANDING_BACKGROUND@"
@@ -0,0 +1,58 @@
version: 1
metadata:
name: "FreshRSS OAuth2 Provisioning"
labels:
app: freshrss
entries:
- model: authentik_providers_oauth2.oauth2provider
identifiers:
name: "FreshRSS Provider"
attrs:
authorization_flow:
!Find [
authentik_flows.flow,
[slug, default-provider-authorization-implicit-consent],
]
authentication_flow:
!Find [authentik_flows.flow, [slug, default-authentication-flow]]
invalidation_flow:
!Find [authentik_flows.flow, [slug, default-provider-invalidation-flow]]
client_type: "confidential"
client_id: "freshrss"
client_secret: !Env FRESHRSS_OAUTH_SECRET
access_code_validity: "minutes=5"
token_validity: "days=30"
signing_key:
!Find [
authentik_crypto.certificatekeypair,
[name, "authentik Self-signed Certificate"],
]
redirect_uris:
- url: "https://@FRESHRSS_DOMAIN@.*"
matching_mode: "regex"
property_mappings:
- !Find [
authentik_providers_oauth2.scopemapping,
[name, "authentik default OAuth Mapping: OpenID 'openid'"],
]
- !Find [
authentik_providers_oauth2.scopemapping,
[name, "authentik default OAuth Mapping: OpenID 'email'"],
]
- !Find [
authentik_providers_oauth2.scopemapping,
[name, "authentik default OAuth Mapping: OpenID 'profile'"],
]
- model: authentik_core.application
identifiers:
slug: "freshrss"
attrs:
name: "FreshRSS"
launch_url: "@FRESHRSS_DOMAIN@"
provider:
!Find [
authentik_providers_oauth2.oauth2provider,
[name, "FreshRSS Provider"],
]
@@ -0,0 +1,11 @@
version: 1
metadata:
name: gitea-ldap-setup
entries:
- model: authentik_core.application
id: gitea-app
identifiers:
slug: gitea
attrs:
name: Gitea
launch_url: "@GITEA_DOMAIN@"
@@ -0,0 +1,108 @@
version: 1
metadata:
name: "Homepage Dashboard - OIDC Provisioning"
labels:
app: homepage
entries:
- model: authentik_providers_oauth2.scopemapping
identifiers:
name: "Homepage Custom Scope: Groups"
attrs:
scope_name: "groups"
description: "Pass user groups array to Homepage for conditional element rendering"
expression: |
return {
"groups": [group.name for group in request.user.ak_groups.all()]
}
# 1. Create the OAuth2/OIDC Provider
- model: authentik_providers_oauth2.oauth2provider
identifiers:
name: "Homepage Provider"
attrs:
authorization_flow:
!Find [
authentik_flows.flow,
[slug, default-provider-authorization-implicit-consent],
]
authentication_flow:
!Find [authentik_flows.flow, [slug, default-authentication-flow]]
invalidation_flow:
!Find [authentik_flows.flow, [slug, default-provider-invalidation-flow]]
client_type: "confidential"
client_id: "homepage"
client_secret: !Env HOMEPAGE_VAR_OAUTH_SECRET
access_code_validity: "minutes=5"
token_validity: "days=30"
redirect_uris:
- url: "https://@HOMEPAGE_DOMAIN@/login"
matching_mode: "regex"
signing_key:
!Find [
authentik_crypto.certificatekeypair,
[name, "authentik Self-signed Certificate"],
]
property_mappings:
- !Find [
authentik_providers_oauth2.scopemapping,
[name, "authentik default OAuth Mapping: OpenID 'openid'"],
]
- !Find [
authentik_providers_oauth2.scopemapping,
[name, "authentik default OAuth Mapping: OpenID 'email'"],
]
- !Find [
authentik_providers_oauth2.scopemapping,
[name, "authentik default OAuth Mapping: OpenID 'profile'"],
]
- !Find [
authentik_providers_oauth2.scopemapping,
[name, "Homepage Custom Scope: Groups"],
]
# 2. Create the Application and link it to the Provider
- model: authentik_core.application
identifiers:
slug: homepage
attrs:
name: "Homepage"
launch_url: "@HOMEPAGE_DOMAIN@"
provider:
!Find [
authentik_providers_oauth2.oauth2provider,
[name, Homepage Provider],
]
open_in_new_tab: false
# 3. Provision the static API token linked to the user account
- model: authentik_rbac.role
state: present
identifiers:
name: homepage-viewer
attrs:
permissions:
- authentik_core.view_user
- authentik_events.view_event
- model: authentik_core.user
state: present
identifiers:
username: homepage-svc
attrs:
roles:
- !Find [authentik_rbac.role, [name, "homepage-viewer"]]
name: Homepage Service Account
path: goauthentik.io/service-accounts
is_active: true
attributes:
goauthentik.io/user/service-account: true
- model: authentik_core.token
state: present
identifiers:
identifier: homepage-token
attrs:
key: !Env HOMEPAGE_VAR_AUTHENTIK_API
user: !Find [authentik_core.user, [username, "homepage-svc"]]
intent: api
expiring: false
@@ -0,0 +1,62 @@
version: 1
metadata:
name: "Immich OAuth2 Provisioning"
labels:
app: immich
entries:
- model: authentik_providers_oauth2.oauth2provider
identifiers:
name: "Immich Provider"
attrs:
authorization_flow:
!Find [
authentik_flows.flow,
[slug, default-provider-authorization-implicit-consent],
]
authentication_flow:
!Find [authentik_flows.flow, [slug, default-authentication-flow]]
invalidation_flow:
!Find [authentik_flows.flow, [slug, default-provider-invalidation-flow]]
client_type: "confidential"
client_id: "immich"
client_secret: !Env IMMICH_OAUTH_SECRET
access_code_validity: "minutes=5"
token_validity: "days=30"
signing_key:
!Find [
authentik_crypto.certificatekeypair,
[name, "authentik Self-signed Certificate"],
]
redirect_uris:
- url: "app.immich:///oauth-callback"
matching_mode: "strict"
- url: "https://@IMMICH_DOMAIN@/auth/login"
matching_mode: "regex"
- url: "https://@IMMICH_DOMAIN@/user-settings"
matching_mode: "regex"
property_mappings:
- !Find [
authentik_providers_oauth2.scopemapping,
[name, "authentik default OAuth Mapping: OpenID 'openid'"],
]
- !Find [
authentik_providers_oauth2.scopemapping,
[name, "authentik default OAuth Mapping: OpenID 'email'"],
]
- !Find [
authentik_providers_oauth2.scopemapping,
[name, "authentik default OAuth Mapping: OpenID 'profile'"],
]
- model: authentik_core.application
identifiers:
slug: "immich"
attrs:
name: "Immich"
launch_url: "@IMMICH_DOMAIN@"
provider:
!Find [
authentik_providers_oauth2.oauth2provider,
[name, "Immich Provider"],
]
@@ -0,0 +1,11 @@
version: 1
metadata:
name: jellyfin-ldap-setup
entries:
- model: authentik_core.application
id: jellyfin-app
identifiers:
slug: jellyfin
attrs:
name: Jellyfin
launch_url: "@JELLYFIN_DOMAIN@"
@@ -0,0 +1,79 @@
version: 1
metadata:
name: Pre-configured LDAP Outpost
entries:
- model: authentik_providers_ldap.ldapprovider
identifiers:
name: ldap-provider
attrs:
base_dn: "@AUTHENTIK_LDAP_DC_DOMAIN@"
search_group: null
authorization_flow:
!Find [authentik_flows.flow, [slug, default-authentication-flow]]
invalidation_flow:
!Find [authentik_flows.flow, [slug, default-provider-invalidation-flow]]
- model: authentik_core.user
state: present
identifiers:
username: "ldap-service"
attrs:
name: "LDAP Bind Service Account"
type: "service_account"
path: "goauthentik.io"
is_active: true
password: !Env DEFAULT_LDAP_PASSWORD
attributes:
ak_recovery_immutable: true
- model: authentik_core.token
identifiers:
identifier: ldap-outpost-static-token
attrs:
intent: api
expiring: false
key: !Env AUTHENTIK_TOKEN
user: !Find [authentik_core.user, [username, "ldap-service"]]
- model: authentik_outposts.outpost
identifiers:
name: LDAP Outpost
attrs:
type: ldap
providers:
- !Find [authentik_providers_ldap.ldapprovider, [name, ldap-provider]]
token:
!Find [authentik_core.token, [identifier, ldap-outpost-static-token]]
config:
log_level: info
authentik_host: https://sso.test.helcel.net/
refresh_interval: minutes=5
authentik_host_insecure: false
- model: authentik_rbac.role
state: present
identifiers:
name: "LDAP Search Role"
attrs:
permissions:
- "authentik_providers_ldap.search_full_directory"
- model: authentik_core.group
state: present
identifiers:
name: "LDAP Search Group"
attrs:
users:
- !Find [authentik_core.user, [username, "ldap-service"]]
roles:
- !Find [authentik_rbac.role, [name, "LDAP Search Role"]]
- model: authentik_core.application
id: ldap-placeholder
identifiers:
slug: ldap
attrs:
name: ldap
group: _
provider:
!Find [authentik_providers_ldap.ldapprovider, [name, ldap-provider]]
@@ -0,0 +1,58 @@
version: 1
metadata:
name: nextcloud-saml-setup
entries:
- model: authentik_providers_saml.samlprovider
identifiers:
name: Nextcloud SAML
attrs:
authorization_flow:
!Find [
authentik_flows.flow,
[slug, default-provider-authorization-explicit-consent],
]
invalidation_flow:
!Find [authentik_flows.flow, [slug, default-provider-invalidation-flow]]
acs_url: https://@NEXTCLOUD_DOMAIN@/apps/user_saml/saml/acs
audience: https://@NEXTCLOUD_DOMAIN@/apps/user_saml/saml/metadata
issuer: https://@AUTHENTIK_DOMAIN@
sp_binding: post
property_mappings:
- !Find [
authentik_core.propertymapping,
[name, "authentik default SAML Mapping: Name"],
]
- !Find [
authentik_core.propertymapping,
[name, "authentik default SAML Mapping: Email"],
]
- !Find [
authentik_core.propertymapping,
[name, "authentik default SAML Mapping: Groups"],
]
- !Find [
authentik_core.propertymapping,
[name, "authentik default SAML Mapping: Username"],
]
- !Find [
authentik_core.propertymapping,
[name, "authentik default SAML Mapping: User ID"],
]
signing_kp:
!Find [
authentik_crypto.certificatekeypair,
[name, "authentik Self-signed Certificate"],
]
sign_assertion: true
sign_response: false
- model: authentik_core.application
identifiers:
slug: nextcloud
attrs:
name: Nextcloud
provider:
!Find [authentik_providers_saml.samlprovider, [name, Nextcloud SAML]]
launch_url: "@NEXTCLOUD_DOMAIN@"
@@ -0,0 +1,46 @@
version: 1
metadata:
name: domain-wide-proxy-setup
entries:
# 1. The Provider
- model: authentik_providers_proxy.proxyprovider
identifiers:
name: Domain Wide Proxy
attrs:
authorization_flow:
!Find [
authentik_flows.flow,
[slug, default-provider-authorization-implicit-consent],
]
invalidation_flow:
!Find [authentik_flows.flow, [slug, default-provider-invalidation-flow]]
external_host: https://@AUTHENTIK_DOMAIN@
cookie_domain: "@COOKIE_DOMAIN@"
mode: forward_domain
intercept_header_auth: true
# 2. The Application (Required to link the provider)
- model: authentik_core.application
identifiers:
slug: authentik-proxy
attrs:
name: "Domain Auth Provider"
group: _
provider:
!Find [
authentik_providers_proxy.proxyprovider,
[name, Domain Wide Proxy],
]
# 3. Add to Outpost
- model: authentik_outposts.outpost
identifiers:
name: authentik Embedded Outpost
attrs:
providers:
- !Find [
authentik_providers_proxy.proxyprovider,
[name, Domain Wide Proxy],
]
@@ -0,0 +1,137 @@
db:
user: invidious_user
password: $DB_PASSWORD
host: $DB_HOST
port: 5432
dbname: invidious_db
check_tables: true
invidious_companion:
- private_url: "http://invidious-companion:8282/companion"
invidious_companion_key: $SERVER_SECRET_KEY
port: 3000
external_port: 443
host_binding: 0.0.0.0
domain: $INVIDIOUS_DOMAIN
https_only: false
#hsts: true
## Accepted values: true, false, dash, livestreams, downloads, local
#disable_proxy: false
# use_innertube_for_captions: false
# -----------------------------
# Features
# -----------------------------
popular_enabled: false
statistics_enabled: true
registration_enabled: true
login_enabled: true
captcha_enabled: false
admins: ["$DEFAULT_ADMIN_EMAIL"]
enable_user_notifications: false
# -----------------------------
# Background jobs
# -----------------------------
channel_threads: 1
#channel_refresh_interval: 30m
full_refresh: false
feed_threads: 1
jobs:
clear_expired_items:
enable: true
refresh_channels:
enable: true
refresh_feeds:
enable: true
# -----------------------------
# Miscellaneous
# -----------------------------
#banner:
# use_pubsub_feeds: true
hmac_key: $HMAC_KEY
#dmca_content:
#cache_annotations: false
#modified_source_code_url: ""
#playlist_length_limit: 500
#########################################
#
# Default user preferences
#
#########################################
default_user_preferences:
# -----------------------------
# Internationalization
# -----------------------------
#locale: en-US
#region: US
## Top 3 preferred languages for video captions.
#captions: ["", "", ""]
# -----------------------------
# Interface
# -----------------------------
dark_mode: "auto"
#thin_mode: false
feed_menu: ["Subscriptions", "Playlists"]
default_home: Subscriptions
#max_results: 40
#annotations: false
#annotations_subscribed: false
#comments: ["youtube", ""]
#player_style: invidious
#related_videos: true
# -----------------------------
# Video player behavior
# -----------------------------
#preload: true
#autoplay: false
#continue: false
#continue_autoplay: true
#listen: false
#video_loop: false
# -----------------------------
# Video playback settings
# -----------------------------
#quality: dash
#quality_dash: auto
#speed: 1.0
#volume: 100
#vr_mode: true
save_player_pos: true
# -----------------------------
# Subscription feed
# -----------------------------
#latest_only: false
#notifications_only: false
unseen_only: true
#sort: published
# -----------------------------
# Miscellaneous
# -----------------------------
#local: false
show_nick: false
#automatic_instance_redirect: false
#extend_desc: false
@@ -0,0 +1,101 @@
{% skip_file if flag?(:api_only) %}
module Invidious::Routes::Login
def self.login_page(env)
locale = env.get("preferences").as(Preferences).locale
user = env.get? "user"
referer = get_referer(env, "/feed/subscriptions")
return env.redirect referer if user
return error_template(400, "Login has been disabled by administrator.") if !CONFIG.login_enabled
if forwarded_user = env.request.headers["X-authentik-email"]?
begin
email = forwarded_user.try &.downcase.byte_slice(0, 254)
return error_template(401, "User ID is a required field") if email.nil? || email.empty?
user = Invidious::Database::Users.select(email: email)
if user
sid = Base64.urlsafe_encode(Random::Secure.random_bytes(32))
Invidious::Database::SessionIDs.insert(sid, email)
env.response.cookies["SID"] = Invidious::User::Cookies.sid(CONFIG.domain, sid)
if env.request.cookies["PREFS"]?
cookie = env.request.cookies["PREFS"]
cookie.expires = Time.utc(1990, 1, 1)
env.response.cookies << cookie
end
else
return error_template(400, "Registration has been disabled by administrator.") if !CONFIG.registration_enabled
sid = Base64.urlsafe_encode(Random::Secure.random_bytes(32))
user, sid = create_user(sid, email, "")
if language_header = env.request.headers["Accept-Language"]?
if language = ANG.language_negotiator.best(language_header, LOCALES.keys)
user.preferences.locale = language.header
end
end
Invidious::Database::Users.insert(user)
Invidious::Database::SessionIDs.insert(sid, email)
view_name = "subscriptions_#{sha256(user.email)}"
PG_DB.exec("CREATE MATERIALIZED VIEW #{view_name} AS #{MATERIALIZED_VIEW_SQL.call(user.email)}")
env.response.cookies["SID"] = Invidious::User::Cookies.sid(CONFIG.domain, sid)
if env.request.cookies["PREFS"]?
user.preferences = env.get("preferences").as(Preferences)
Invidious::Database::Users.update_preferences(user)
cookie = env.request.cookies["PREFS"]
cookie.expires = Time.utc(1990, 1, 1)
env.response.cookies << cookie
end
end
return env.redirect referer
rescue ex
return error_template(500, "Authentication error: #{ex.message}")
end
end
env.redirect referer
end
def self.login(env)
referer = get_referer(env, "/feed/subscriptions")
env.redirect referer
return error_template(403, "Login post is not supported.")
end
def self.signout(env)
locale = env.get("preferences").as(Preferences).locale
user = env.get? "user"
sid = env.get? "sid"
referer = get_referer(env)
return env.redirect referer if !user
user = user.as(User)
sid = sid.as(String)
token = env.params.body["csrf_token"]?
begin
validate_request(token, sid, env.request, HMAC_KEY, locale)
rescue ex
return error_template(400, ex)
end
Invidious::Database::SessionIDs.delete(sid: sid)
env.request.cookies.each do |cookie|
cookie.expires = Time.utc(1990, 1, 1)
env.response.cookies << cookie
end
env.redirect referer
end
end
@@ -0,0 +1,70 @@
{
"alt-speed-down": 50,
"alt-speed-enabled": false,
"alt-speed-time-begin": 540,
"alt-speed-time-day": 127,
"alt-speed-time-enabled": false,
"alt-speed-time-end": 1020,
"alt-speed-up": 50,
"bind-address-ipv4": "0.0.0.0",
"bind-address-ipv6": "::",
"blocklist-enabled": false,
"blocklist-url": "http://www.example.com/blocklist",
"cache-size-mb": 4,
"dht-enabled": true,
"download-dir": "/downloads/complete",
"download-queue-enabled": true,
"download-queue-size": 5,
"encryption": 1,
"idle-seeding-limit": 30,
"idle-seeding-limit-enabled": false,
"incomplete-dir": "/downloads/incomplete",
"incomplete-dir-enabled": true,
"lpd-enabled": false,
"message-level": 2,
"peer-congestion-algorithm": "",
"peer-id-ttl-hours": 6,
"peer-limit-global": 200,
"peer-limit-per-torrent": 50,
"peer-port": 51413,
"peer-port-random-high": 65535,
"peer-port-random-low": 49152,
"peer-port-random-on-start": false,
"peer-socket-tos": "default",
"pex-enabled": true,
"port-forwarding-enabled": true,
"preallocation": 1,
"prefetch-enabled": 1,
"queue-stalled-enabled": true,
"queue-stalled-minutes": 30,
"ratio-limit": 2,
"ratio-limit-enabled": false,
"rename-partial-files": true,
"rpc-authentication-required": false,
"rpc-bind-address": "0.0.0.0",
"rpc-enabled": true,
"rpc-password": "$TRANSMISSION_RPC_PASSWORD",
"rpc-port": 9091,
"rpc-url": "/transmission/",
"rpc-username": "",
"rpc-host-whitelist": "127.0.0.1",
"rpc-host-whitelist-enabled": false,
"rpc-whitelist": "127.0.0.1",
"rpc-whitelist-enabled": false,
"scrape-paused-torrents-enabled": true,
"script-torrent-done-enabled": false,
"script-torrent-done-filename": "",
"seed-queue-enabled": false,
"seed-queue-size": 10,
"speed-limit-down": 100,
"speed-limit-down-enabled": false,
"speed-limit-up": 100,
"speed-limit-up-enabled": false,
"start-added-torrents": true,
"trash-original-torrent-files": false,
"umask": 2,
"upload-slots-per-torrent": 14,
"utp-enabled": false,
"watch-dir": "/watch",
"watch-dir-enabled": true
}
+90 -26
View File
@@ -1,27 +1,53 @@
{ config, pkgs, lib, ... }:
let
cfg = config.syscfg.server.containers;
enabledConfigs = lib.filterAttrs (name: c: c.enable) cfg;
containerSetsList = lib.mapAttrsToList (name: containerCfg:
let defs = import (./defs + "/${name}.nix") {inherit config pkgs lib containerCfg;};
serverCfg = config.syscfg.server;
builder = import ./builder.nix { inherit config lib pkgs serverCfg; };
loadApp = name: containerCfg:
builder.mkApp name ((import (./apps + "/${name}.nix")) {
inherit config pkgs lib containerCfg builder name;
});
loadedContainers = lib.mapAttrs loadApp serverCfg.containers;
appsList = builtins.attrValues loadedContainers;
concatRuntimeLists = field: lib.concatMap (app: app.runtime.${field}) appsList;
mkNamedUnits = mkUnit: items: lib.listToAttrs (map mkUnit items);
mergedContainers = lib.concatMapAttrs (appName: app:
lib.mapAttrs' (cName: cCfg: lib.nameValuePair "${appName}-${cName}" cCfg) app.runtime.containers
) loadedContainers;
allPathConfigs = map (path: {
inherit path;
mode = "0755";
}) (lib.unique (builtins.attrValues serverCfg.path)) ++ concatRuntimeLists "paths";
allSetupConfigs = map (app: ({ name = app.name; envFile = ""; } // app.runtime.setup)) appsList;
allCronsConfigs = concatRuntimeLists "cron";
allVMConfigs = builtins.filter (app: app.runtime.vm != null) appsList;
mkPathSetup = cfg:
let
effectiveCfg = {
owner = "root:root";
mode = "0400";
dirs = [];
} // cfg;
in ''
${pkgs.coreutils}/bin/mkdir -p "${effectiveCfg.path}"
${lib.concatMapStringsSep "\n" (dir: "${pkgs.coreutils}/bin/mkdir -p ${effectiveCfg.path}/${lib.escapeShellArg dir}") effectiveCfg.dirs}
${pkgs.coreutils}/bin/chown ${effectiveCfg.owner} "${effectiveCfg.path}"
${pkgs.coreutils}/bin/chmod ${effectiveCfg.mode} "${effectiveCfg.path}"
'';
in {
containers = lib.mapAttrs' (cName: cValue:
lib.nameValuePair "${name}-${cName}" cValue
) defs.containers;
}
) enabledConfigs;
mergedContainers = lib.attrsets.mergeAttrsList (lib.map(e: e.containers) containerSetsList);
allPathConfigs = lib.flatten (lib.map (e: e.paths or []) containerSetsList);
in
{
config = lib.mkIf ( enabledConfigs != {} ) {
config = lib.mkMerge [{
syscfg.server.loadedContainers = loadedContainers;
} (lib.mkIf (loadedContainers != {}) {
virtualisation.oci-containers = {
backend = "podman";
containers = mergedContainers;
};
system.activationScripts.container-setup-dirs = {
deps = [ "users" "groups" ];
text = lib.concatStringsSep "\n" (map mkPathSetup allPathConfigs);
};
systemd.services.podman-gc = {
systemd.services = {
podman-gc = {
description = "Podman garbage collection";
serviceConfig.Type = "oneshot";
script = ''
@@ -30,14 +56,52 @@ in
'';
startAt = "weekly";
};
system.activationScripts.container-setup-dirs = {
deps = [ "users" "groups" ];
text = lib.concatStringsSep "\n" (map (cfg: ''
mkdir -p "${cfg.path}"
chown ${cfg.owner} "${cfg.path}"
chmod ${cfg.mode} "${cfg.path}"
'') allPathConfigs);
};
};
}
// mkNamedUnits (e: {
name = "${e.name}-vm";
value = {
description = "Isolated NixOS Guest VM for ${e.name}";
after = [ "network-online.target" ];
wants = [ "network-online.target" ];
wantedBy = [ "multi-user.target" ];
environment = {
QEMU_VM_REG_SND = "0";
NIX_DISK_IMAGE = "/media/data/kvm/${e.name}-guest.qcw2";
};
serviceConfig = {
Type = "simple";
Restart = "always";
RestartSec = "10s";
ExecStartPre = "${pkgs.coreutils}/bin/mkdir -p /media/data/kvm";
ExecStart = ''
${builder.mkVm { name = e.name; vm = e.runtime.vm; }}/bin/run-${e.name}-vm -nographic
'';
};
};
}) allVMConfigs
// mkNamedUnits (e: {
name = "${e.name}-setup";
value = {
description = "Run ${e.name} setup";
after = [ "podman-${e.name}-${e.trigger}.service" ];
wants = [ "podman-${e.name}-${e.trigger}.service" ];
partOf = [ "podman-${e.name}-${e.trigger}.service" ];
wantedBy = [ "multi-user.target" ];
restartTriggers = [ e.script ];
serviceConfig = {
Type = "oneshot";
TimeoutStartSec = "360s";
EnvironmentFile = e.envFile;
ExecStart = e.script;
RemainAfterExit = true;
User = "root";
};
};
}) allSetupConfigs;
services.cron = {
enable = true;
systemCronJobs = allCronsConfigs;
};
})];
}
@@ -1,78 +0,0 @@
{ config, containerCfg, pkgs, lib, ... }:
let
serverCfg = config.syscfg.server;
in {
paths = [{
path="${serverCfg.dataPath}/authentik/media";
owner = "1000:1000";
mode = "0755";
}{
path="${serverCfg.dataPath}/authentik/templates";
owner = "1000:1000";
mode = "0755";
}];
containers = {
server = {
image = "ghcr.io/goauthentik/server:latest";
volumes = [
"${serverCfg.dataPath}/authentik/media:/media"
"${serverCfg.dataPath}/authentik/templates:/templates"
];
environmentFiles = [
config.sops.secrets."AUTHENTIK".path
];
environment = {
"AUTHENTIK_REDIS__HOST" = "host.containers.internal";
"AUTHENTIK_POSTGRESQL__HOST" = "host.containers.internal";
"AUTHENTIK_POSTGRESQL__USER" = "authentik_user";
"AUTHENTIK_POSTGRESQL__NAME" = "authentik_db";
"AUTHENTIK_EMAIL__HOST" = "${serverCfg.mailDomain}";
"AUTHENTIK_EMAIL__PORT" = "587";
"AUTHENTIK_EMAIL__USERNAME" = "noreply@${serverCfg.hostDomain}";
"AUTHENTIK_EMAIL__USE_TLS" = "true";
"AUTHENTIK_EMAIL__USE_SSL" = "false";
"AUTHENTIK_EMAIL__TIMEOUT" = "10";
"AUTHENTIK_EMAIL__FROM" = "sso@noreply.${serverCfg.hostDomain}";
};
labels = {
"traefik.enable" = "true";
"traefik.http.routers.sso.entrypoints" = "web-secure";
"traefik.http.routers.sso.rule" = "Host(`sso.${serverCfg.hostDomain}`)";
"traefik.http.routers.sso.tls" = "true";
"traefik.http.services.sso.loadbalancer.server.port" = "${toString containerCfg.port}";
};
cmd = [ "server" ];
extraOptions = [
"--add-host=host.containers.internal:host-gateway"
"--ip=${containerCfg.ip}"
];
ports = [
"9999:${toString containerCfg.port}"
];
};
worker = {
image = "ghcr.io/goauthentik/server:latest";
volumes = [
"${serverCfg.dataPath}/authentik/media:/media"
"${serverCfg.dataPath}/authentik/templates:/templates"
"/var/run/docker.sock:/var/run/docker.sock"
];
environmentFiles = [
config.sops.secrets."AUTHENTIK".path
];
environment = {
"AUTHENTIK_REDIS__HOST" = "host.containers.internal";
"AUTHENTIK_POSTGRESQL__HOST" = "host.containers.internal";
"AUTHENTIK_POSTGRESQL__USER" = "authentik_user";
"AUTHENTIK_POSTGRESQL__NAME" = "authentik_db";
};
extraOptions = [
"--add-host=host.containers.internal:host-gateway"
];
cmd = [ "worker" ];
};
};
}
-152
View File
@@ -1,152 +0,0 @@
{ config, pkgs, lib, ... }:
let serverCfg = config.syscfg.server;
in {
project.name = "cloud";
networks = {
internal = {
name = lib.mkForce "internal";
internal = true;
};
external = {
name = lib.mkForce "external";
internal = false;
};
};
services = {
cloud_nextcloud.service = {
image = "nextcloud:27";
container_name = "cloud";
restart = "unless-stopped";
networks = [ "external" ];
volumes = [
"${serverCfg.configPath}/data/nextcloud:/var/www/html"
"${serverCfg.dataPath}/data/music:/media/music"
"${serverCfg.dataPath}/data/video:/media/video"
"${serverCfg.dataPath}/data/photo:/media/photo"
];
tmpfs = [ "/tmp" ];
labels = {
"traefik.enable" = "true";
"traefik.http.routers.nextcloud.entrypoints" = "web-secure";
"traefik.http.routers.nextcloud.rule" =
"Host(`cloud.${serverCfg.hostDomain}`)";
"traefik.http.routers.nextcloud.tls" = "true";
"traefik.http.routers.nextcloud.middlewares" =
"sts_headers,nextcloud-caldav";
"traefik.http.middlewares.nextcloud-caldav.redirectregex.permanent" =
"true";
"traefik.http.middlewares.nextcloud-caldav.redirectregex.regex" =
"^https://(.*)/.well-known/(card|cal)dav";
"traefik.http.middlewares.nextcloud-caldav.redirectregex.replacement" =
"https://$\${1}/remote.php/dav/";
"traefik.http.middlewares.sts_headers.headers.stsSeconds" = "15552000";
"traefik.http.middlewares.sts_headers.headers.stsIncludeSubdomains" =
"true";
};
};
cloud_office.service = {
image = "collabora/code:latest";
container_name = "cloud_office";
restart = "unless-stopped";
networks = [ "external" ];
volumes = [ ];
environment = {
username = "COLLABORA_USER";
password = "COLLABORA_PASSWORD";
aliasgroup1 = "https://cloud.${serverCfg.hostDomain}";
server_name = "office.${serverCfg.hostDomain}";
VIRTUAL_HOST = "office.${serverCfg.hostDomain}";
VIRTUAL_PORT = "9980";
VIRTUAL_PROTO = "http";
DONT_GEN_SSL_CERT = "true";
RESOLVE_TO_PROXY_IP = "true";
NETWORK_ACCESS = "internal";
extra_params = "--o:ssl.enable=false --o:ssl.termination=true";
dictionaries = "en fr de jp";
};
labels = {
"traefik.enable" = "true";
"traefik.http.routers.collabora.entrypoints" = "web-secure";
"traefik.http.routers.collabora.rule" =
"Host(`office.${serverCfg.hostDomain}`)";
"traefik.http.routers.collabora.tls" = "true";
};
};
cloud_etherpad.service = {
image = "etherpad/etherpad:latest";
container_name = "etherpad";
restart = "unless-stopped";
networks = [ "external" ];
volumes = [
"${serverCfg.dataPath}/ether/etherpad/data:/opt/etherpad-lite/var"
"${serverCfg.dataPath}/ether/etherpad/APIKEY.txt:/opt/etherpad-lite/APIKEY.txt"
];
environment = {
NODE_ENV = "production";
TITLE = "Helcel-Pad";
DB_TYPE = "mysql";
DB_HOST = serverCfg.dbHost;
DB_PORT = serverCfg.dbPort;
DB_NAME = "etherpad";
DB_USER = "ETHERPAD_DB_USER";
DB_PASS = "ETHERPAD_DB_PASSWORD";
DB_CHARSET = "utf8mb4";
DEFAULT_PAD_TEXT = "P A D";
PAD_OPTIONS_SHOW_LINE_NUMBERS = "true";
PAD_OPTIONS_USE_MONOSPACE_FONT = "true";
ADMIN_PASSWORD = "ETHERPAD_ADMIN_PASSWORD";
SKIN_VARIANTS = "super-dark-toolbar light-editor dark-background";
};
labels = {
"traefik.enable" = "true";
"traefik.http.routers.etherpad.entrypoints" = "web-secure";
"traefik.http.routers.etherpad.rule" =
"Host(`pad.${serverCfg.hostDomain}`)";
"traefik.http.routers.etherpad.tls" = "true";
};
};
cloud_ethercalc.service = {
image = "audreyt/ethercalc:latest";
container_name = "ethercalc";
restart = "unless-stopped";
networks = [ "external" "internal" ];
volumes = [
"${serverCfg.dataPath}/ether/etherpad/data:/opt/etherpad-lite/var"
"${serverCfg.dataPath}/ether/etherpad/APIKEY.txt:/opt/etherpad-lite/APIKEY.txt"
];
environment = {
NODE_ENV = "production";
TITLE = "Helcel-Calc";
REDIS_PORT_6379_TCP_ADDR = "ethercalc-redis";
REDIS_PORT_6379_TCP_PORT = "6379";
ADMIN_PASSWORD = "ETHERPAD_ADMIN_PASSWORD";
SKIN_VARIANTS = "super-dark-toolbar light-editor dark-background";
};
labels = {
"traefik.enable" = "true";
"traefik.http.routers.ethercalc.entrypoints" = "web-secure";
"traefik.http.routers.ethercalc.rule" =
"Host(`calc.${serverCfg.hostDomain}`)";
"traefik.http.routers.ethercalc.tls" = "true";
};
};
cloud_redis.service = {
image = "redis:latest";
container_name = "ethercalc-redis";
restart = "unless-stopped";
networks = [ "internal" ];
volumes = [ "${serverCfg.dataPath}/ether/ethercalc/redis:/data" ];
environment = { };
labels = { "traefik.enable" = "false"; };
};
};
}
-30
View File
@@ -1,30 +0,0 @@
{ config, pkgs, lib, ... }:
let serverCfg = config.syscfg.server;
in {
project.name = "name";
networks = {
internal = {
name = lib.mkForce "internal";
internal = true;
};
external = {
name = lib.mkForce "external";
internal = false;
};
};
services = {
NAME.service = {
image = "NAME:latest";
container_name = "NAME";
restart = "unless-stopped";
networks = [ "internal" ];
volumes = [ ];
environment = { };
labels = { "traefik.enable" = "false"; };
};
};
}
@@ -1,81 +0,0 @@
{ config, pkgs, ... }: {
project.name = "traefik";
networks = {
internal = {
name = lib.mkForce "internal";
internal = true;
};
external = {
name = lib.mkForce "external";
internal = false;
};
};
services = {
traefik.service = {
image = "traefik:latest";
container_name = "traefik";
restart = "unless-stopped";
networks = [ "internal" "external" ];
command = [
"--api"
"--providers.docker=true"
"--entrypoints.web.address=:80"
"--entrypoints.web-secure.address=:443"
];
port = [ "443" "80" ];
volumes = [
"/var/run/docker.sock:/var/run/docker.sock:ro"
"${serverCfg.configPath}/traefik/traefik.yaml:/etc/traefik/traefik.yaml"
"${serverCfg.configPath}/traefik/access.log:/etc/traefik/access.log"
"${serverCfg.configPath}/traefik/acme.json:/acme.json"
];
environment = {
"INFOMANIAK_ACCESS_TOKEN" = config.sops.secrets.INFOMANIAK_API_KEY.path;
};
labels = { "traefik.enable" = "false"; };
};
matomo.service = {
image = "matomo:latest";
container_name = "matomo";
restart = "unless-stopped";
networks = [ "external" ];
volumes = [
"/etc/localtime:/etc/localtime:ro"
"${serverCfg.configPath}/matomo:/var/www/html/config:rw"
"${serverCfg.configPath}/traefik/access.log:/var/log/taccess.log:ro"
];
environment = { };
labels = {
"traefik.http.routers.matomo.rule" =
"Host(`matomo.${serverCfg.hostDomain}`)";
"traefik.http.routers.matomo.entrypoints" = "web-secure";
"traefik.http.routers.matomo.tls" = "true";
};
};
searx.service = {
image = "searxng/searxng:latest";
container_name = "searx";
restart = "unless-stopped";
networks = [ "external" ];
volumes = [ "/etc/localtime:/etc/localtime:ro" ];
environment = {
"BASE_URL" = "https://searx.${serverCfg.hostDomain}";
"AUTOCOMPLETE" = "true";
"INSTANCE_NAME" = "searx${serverCfg.shortName}";
};
labels = {
"traefik.http.routers.matomo.rule" =
"Host(`searx.${serverCfg.hostDomain}`)";
"traefik.http.routers.matomo.entrypoints" = "web-secure";
"traefik.http.routers.matomo.tls" = "true";
};
};
};
}
+33 -9
View File
@@ -1,21 +1,19 @@
{ config, lib, pkgs, ... }:
let
listNames = config.syscfg.server.db;
containerNames = lib.mapAttrsToList
(name: cfg: name)
(lib.filterAttrs (name: cfg: cfg.db or false) config.syscfg.server.containers);
containerNames = lib.concatMap (app: app.requires.databases) (builtins.attrValues config.syscfg.server.loadedContainers);
allApps = lib.unique (listNames ++ containerNames);
in {
config = lib.mkIf ( builtins.length allApps > 0) {
services.postgresql = {
enable = true;
enableTCPIP = true; # Required to listen on network interfaces
enableTCPIP = true;
extensions = ps: with ps; [ vectorchord pgvector ];
settings = {
listen_addresses = lib.mkForce "*";
shared_preload_libraries = "vchord";
};
authentication = pkgs.lib.mkOverride 10 ''
# TYPE DATABASE USER ADDRESS METHOD
@@ -42,6 +40,33 @@ in {
settings.protected-mode = "no";
};
systemd.services.influxdb3 = {
description = "InfluxDB 3 Time Series Database Engine";
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
environment = {
INFLUXDB3_NODE_IDENTIFIER_PREFIX = "node0";
INFLUXDB3_OBJECT_STORE = "file";
INFLUXDB3_DATA_DIR = "${config.syscfg.server.path.data}/influxdb";
INFLUXDB3_DB_DIR = "${config.syscfg.server.path.data}/influxdb";
INFLUXDB3_ENABLE_INTERNAL_DB = "true";
};
serviceConfig = {
Type = "simple";
ExecStart = "${pkgs.influxdb3}/bin/influxdb3 serve"; #--admin-token-file=/run/secrets/admin-token
Restart = "on-failure";
StateDirectory = "influxdb3";
PrivateTmp = true;
NoNewPrivileges = true;
};
};
# admin-token.json= {
# "token": "$INFLUXDB_TOKEN",
# "name": "admin",
# "description": "Admin token for automated deployment"
# }
systemd.services.postgresql-init = {
description = "Custom Postgres Setup (Ownership & Passwords)";
@@ -55,14 +80,13 @@ in {
};
script = ''
${pkgs.coreutils}/bin/sleep 2
${pkgs.coreutils}/bin/sleep 20
PSQL="${pkgs.postgresql}/bin/psql"
${lib.concatMapStringsSep "\n" (name: ''
$PSQL -tAc "ALTER DATABASE ${name}_db OWNER TO ${name}_user;"
if [ -f "${config.sops.secrets."${lib.toUpper name}".path}" ]; then
PASS=$(grep "^DB_PASSWORD=" "${config.sops.secrets."${lib.toUpper name}".path}" | cut -d'=' -f2-)
echo $PASS
if $PSQL -tAc "ALTER USER ${name}_user WITH PASSWORD '$PASS';" ; then
echo " Successfully set password for ${name}_user"
else
+7 -13
View File
@@ -1,7 +1,8 @@
{ config, lib, ... }:{
config = lib.mkIf (config.syscfg.server.nftables.enable) {
{ config, lib, ... }:
let
cfg = config.syscfg.server;
in {
config = lib.mkIf (cfg.ipfw.enable) {
boot.kernel.sysctl = {
"net.ipv4.ip_forward" = 1;
"net.ipv6.conf.all.forwarding" = 1;
@@ -9,13 +10,6 @@
networking.nftables.enable = true;
networking.nftables.ruleset = ''
table inet filter {
chain input {
type filter hook input priority filter; policy accept;
tcp dport {5432, 6379} ip saddr { 10.0.0.0/8 169.254.0.0/16 } accept
}
}
table inet nat {
chain prerouting {
type nat hook prerouting priority dstnat; policy accept;
@@ -34,12 +28,12 @@
iifname "${srcInt}" tcp dport ${srcPort} counter dnat ip6 to [${dstAddr6}]:${dstPort}
iifname "${srcInt}" udp dport ${srcPort} counter dnat ip6 to [${dstAddr6}]:${dstPort}
''
) config.syscfg.server.nftables.ports}
) cfg.ipfw.ports}
}
chain postrouting {
type nat hook postrouting priority srcnat; policy accept;
oifname { ${lib.concatMapStringsSep ", " (iface: ''"${iface}"'') config.syscfg.server.nftables.ifs} } masquerade
oifname { ${lib.concatMapStringsSep ", " (iface: ''"${iface}"'') cfg.ipfw.ifs} } masquerade
}
}
'';
+1 -1
View File
@@ -9,8 +9,8 @@ in {
services.openssh = {
enable = true;
ports = [ 422 ];
banner = "";
settings = {
#Banner = "";
PasswordAuthentication = false;
PermitRootLogin = "no";
ClientAliveInterval = 60;
+7 -7
View File
@@ -1,16 +1,16 @@
{ config, lib, pkgs, ... }:
let
listNames = config.syscfg.server.db;
containerNames = lib.mapAttrsToList (name: cfg: name)
(lib.filterAttrs (name: cfg: cfg.db or false) config.syscfg.server.containers);
containerNames = lib.concatMap (app: app.requires.secrets) (builtins.attrValues config.syscfg.server.loadedContainers);
allApps = lib.unique (listNames ++ containerNames);
in{
config = lib.mkIf (config.syscfg.server.sops) {
sops.secrets = {
INFOMANIAK_API_KEY = { sopsFile = ./server.yaml; };
} // (lib.genAttrs (map (name: "${lib.toUpper name}") allApps) (name: {
owner = "postgres";
CUSTOM = {
mode = "0444";
sopsFile = ./server.yaml;
};
} // (lib.genAttrs (map (name: lib.toUpper name) allApps) (name: {
mode = "0444";
sopsFile = ./server.yaml;
}));
};
}
+48
View File
@@ -0,0 +1,48 @@
CUSTOM: |
DEFAULT_ADMIN_USERNAME=...
DEFAULT_ADMIN_PASSWORD=...
DEFAULT_ADMIN_EMAIL=...
DEFAULT_LDAP_PASSWORD=...
TRAEFIK: |
INFOMANIAK_ACCESS_TOKEN=...
AUTHENTIK: |
DB_PASSWORD=...
POSTGRES_PASSWORD=...
AUTHENTIK_SECRET_KEY=...
AUTHENTIK_EMAIL__PASSWORD=...
AUTHENTIK_TOKEN=...
NEXTCLOUD: |
DB_PASSWORD=...
POSTGRES_PASSWORD=...
COLLABORA: |
password=...
ETHERPAD: |
DB_PASSWORD=...
DB_PASS=...
ADMIN_PASSWORD=...
APIKEY=...
ETHERCALC: |
ETHERCALC_KEY=...
GITEA: |
DB_PASSWORD=...
GITEA__database__PASSWD=...
GITEA__security__SECRET_KEY=...
GITEA__security__INTERNAL_TOKEN=...
SEARXNG: |
SEARXNG_SECRET=...
UMAMI: |
DB_PASSWORD=...
DATABASE_URL=postgresql://username:mypassword@localhost:5432/mydb
APP_SECRET=...
IMMICH: |
DB_URL = "postgresql://immich_user:...@localhost:5432/immich_db";
SERVARR: |
SONARR__AUTH__APIKEY=...
RADARR__AUTH__APIKEY=...
FRESHRSS: |
DB_PASSWORD=...
SUWAYOMI: |
DATABASE_PASSWORD=...
DB_PASSWORD=...
CALIBRE: |
DB_PASSWORD=...
+20 -4
View File
@@ -1,5 +1,21 @@
INFOMANIAK_API_KEY: ENC[AES256_GCM,data:QhjQoCMxogXAPtvUbf/EWkqsFAndn73LBuTqj5essjruekynH287D/CYN/cwfcnDqZoh6Z4A9p08uUmXzqmTiralAhsCoc+Ljb/monmsruc=,iv:8rMGNc9398jnFXZm34fOht6fMNDAcDZ68B1jwoQPn2Q=,tag:ZlQnPaxkCktpwiC6HzmFVg==,type:str]
AUTHENTIK: ENC[AES256_GCM,data:jNcqbLEMbaQ7mmn4dK4LFguecDDxrBsQZaqfQeQ+iGel6mD4jAC6rNdGC8H8NepRWmc0ZjNC6Fe4EaGllbjq/50SqlB47DtyuuCwOZFP6fiU4sD13B7t0Fg/7xMomqnRnJ+3UyVzt6PgEzzQNoPPpHxVGpR+TJnROhflcmCKi/JdAR6dspNqJEbrjp4LVk28Rp9Od6lbsOGphRb79z/DKA1QYRPuE7QU7Edzqqy09g5nKjGxIxjWNEYPt0WiD3537eBICfWm5krs8jxyf6TSiGasHSgSDyJSbnoNkPzSf9A5Jwtulu1jFgjvY/v+qhs9649USp1MzogSAfDZBNC4irge/lb5EPPIQ31EC/Dybza9dX/h6cR/KyQm5GsxBBJzm33rzC/4aCCRlcsAl5eO4JZn4MZta4yHss2UQHQ48i15OSdBwwTbTt240UbrIIje0hfOM6R+Uk3VOY/+VtBV/D+0ks2SUEKMvWgqJDhDh4FVqeR0a9dpLhOAvaWryAAETjlHljOrcF3Q3WooZbBDvLyMMtsHIeF1JDqwghI3,iv:8RdNbsnVVu4awW6yrpLGxAtM7o6uN5vgZIotmT6osW8=,tag:rNaCeG6STXINm42x1b2jcw==,type:str]
CUSTOM: ENC[AES256_GCM,data:UdxcMBZVmWKoikP713KcpuiMH0OJbR+CFvJ9Os4lTfLkVb0PMOCftprLKIdVbb39Ov4O1F4cpWbVD5ypCj5PPBygQM8ce1bDNRa/M7E3VPPwZw5ZtXtkgGlPwPpuHeoVC1K8XxltxpuqdQtSNUPsDg4rYnsEaXTnpJ3YTWHliM7W1oU8okEgxdtQFvjcHFhNTTSNKHfMHd3CyOABf1J0FX55uqgXydCBbeHnge9Rbiue4FjIdewvpZatVy6mhi0ILGEEL6XAObkFrWrv9rLWM/ymd6Lb5I+xAkmYTJ7AaYfaokTey/NJ1cJ5KBftQBgQ5Wo11kfRuNrGGRZROT59vGIEaO/QUiiKndsSGE5XpRylAkHNV8SEHg2PWeI8fz/n2zVRT6Gmy+xXQTbvEhRm+LQ2hMNXVEzUq47tsPL2OXcWgNZKrhz95Ct2/OegPI7/HTdMmD3Wbqs3FlrkOMrL1W3GKCzUfV69cmXFy8llXBMyEk7Vf/+2CjOmeDuvjlFKF4Jkip/rVGB5rTB4xPToS5tCz2qksggaRXidd4lSCyI+CxZN9NdL8klMSJ0bhawxltt3xMBO7egF4KDHmxJTBvohp4HBHZXy3pJ9AXQX2aYzC+L9cjSkvto7qyuC5rD/HgX8n4YiHFR8ua72bickgZ2JKpNawVDRWdDFNyVBCQbhZfkZzyCECQ1AfNUn969TQsTzP91vEk+F2QzmoE6hYEVJ8PRg2p89TeevcTuH70qVBMT2TAtLAlkZ4gQegByo4vvjtSGlFzHy5idKTxjHM/HTLkRImCXRBj663PU7e/zse+bCzk6DfhM4JsNt8mZRRPU1cYqnzgfjz5O+IP50BUYYgmsoQhNKH22E3tUaXihvddNusfnxdq5RFgwcGTcfIVUSB3c98Q1yf2Ft896dzl9OLcRj6tsAUId2e3iJ13FhZEpm5Y+zc5CSAdOuEAya0j5FRYopMUkKJ8uC/CRXTFuSIAo4kw9/Dc05Td/IsLGVQKnL9oyWzj7p1V4J0hLc37ELF5/TShFmkCL99m50XXctupVoLOVP35O36QzFcduw8knNF9iNqrK42tTS8Jt1ZY3BR4L5Nm1TM9rMO6afGy18b1fag5Zk7pByTzGwLped7ST2qf3R3KtbK3CWXbojpnQhYHYnDVaXT0JCPhdqSRIXV+f7hFEH6AwvyCkoahBO19oL6/YizqJe51pKQqWudzVjqDhSt9Z9NsqLRfCIL+cl63LSjQjhyBXKTtxJbqqkYP6uGLULAE89WUzjP5D1oE8EspHmcpirfDyjLa6JJyLWBMGbFDAzHeOJT3AXmGXNvEYsHk28DrJ+LsWFH8MDUPFCXz2Acj8M0B1Kv8822/Xir4xw2AlnS2Y0mL6i8evQ7S2/wg0C9G/nscLZfNeFOvph+sZhQd3/5osgit38TwWPCvg6ulu9nzDjAjI8vAZ3PG/d1PgeJzoWLDmE1EDxqDlR/UFq,iv:0bnj/W2ys7bNJKfAfUmgsiXeyHdiqhRAeB3qDGU2Is0=,tag:oiZMxOk4ABhzguaZbRQZxg==,type:str]
TRAEFIK: ENC[AES256_GCM,data:Ei+/OL7xwNaOEg3rSaz95N78nvp51lC63XCplNzeD+bBMGcK9G7HoyQxfpaJ7S0MkuMW0ZXT2nJ4GES40GoJCZIrnEiSBm2tpjDfNjlS/rFwxx0wVfM1nsEuBf3pL5dqiCNa9+Lad2Cd,iv:d1MH0ive+E8xuUK0CIOXZeEigHJKVGlFaq0iH4KSbZA=,tag:VTARuNeotr2I0+fdOk+iqA==,type:str]
AUTHENTIK: ENC[AES256_GCM,data:HlUFb7JjzSMTM345miSLlUE4SEXgaRAx7SkDDQzaJzs9VuifJKtOE2M4PCKc35VjVt9xIFH+YoIE93re10Rwbe+QEaUphPOgb/G7jRhaaPV/roBYuv6uO5xy68jaVJZpobxajOSVUmJa1JANCh1qrX0+Imr6udYULvK6wQzAnu2tEDkElQ3eZtezUa4E5ia1j7RCYTTPW9oie+YEVJl5Aws2HzPK5q0wKojZOmHanbnKzij3KnSgtsMc3ftL1Fam3wlSk2n3Tw0nz8aBag9IPwYje5zdBkDJY6qiBwYKcBPQUIW+Na0xX2JHymwJSzMdKmW8cEV9b1fXCPsnYVXulb4VMVkTk4MibZ3YT57wlFhqhSy7D39ZTySllIZg8sOrj8cKhpJ3HlSbceD1GnPJatVzZkDkDeyICLu9sYX3B+KrCDlL5sUMPagUFc3g3HUAPxLVPltoP69ro69acUoz5w8gkAwHlE45I3biC/jLz4telEcW8GkF868j3gsHiayE3f87T5MOPvuvhAFdSMl3SF1ND3mWjJq7+FmA6BhxgESg4m+vPnYyVumcbXJnbgfW69BgPYcL1CWZcA+SP6OWg9GOYT5SuWixkaGn2TgRAUj3nlCcAja8,iv:uXAyOIBl9lGYBvALMdvp2hf6cj6QGWRcyUvEsjIDr1I=,tag:iLxO/qYT2zafXhFGVVUYkA==,type:str]
NEXTCLOUD: ENC[AES256_GCM,data:IWitzubILQ5SrGdO3UQZboisqAECt5lXOqHVg4yAKxedG7ZLOgVp6jPV+4VVDC13KEkxIsiYjjNvjqnOXCdYWQIC13YZ+o2IBDI9PgavBB3nmjfi0Q7BVki6C8qCtbM5H9uFlQ3h7rkPyEbE3pHa3dY5uwgdtmvw3qKf2UAZGIJCU7dKamjuTCucGitOEG434jFQik9duHZs7EV3AZrkLXqOfdvftvdpciDb/4/K7h/4uEYSXJ94Lf0b16/NRUcR,iv:1UvcbqC3hJEHU9t6Z+N226DTJEcgM315ynYkxPKpYSM=,tag:FGkXlUw+7LRu1/cpMys7OA==,type:str]
COLLABORA: ENC[AES256_GCM,data:cLGEziks5dyxTF1jugfpQE0l0nSkDP7MpROzCxCM94jv49sguA+d/SnY1olE8ZP9iCBnlvbMZyNR7uYo88B92Pmv8wVWfeuhHiHFIXh5aaOxntpt80UMg3Jy,iv:gmFG7C893QPuZ4rEqllAlUpNIXMcGsf9+/QCPLhWLTM=,tag:WpKHCUk6zhQRfFX2d6OPbQ==,type:str]
ETHERPAD: ENC[AES256_GCM,data:PSr06GyOgY0HDNC4Hr2XUjbNUszGlfBjxDbrrKNQOqSMSVfZj4iFIGamrS72WO0un4U7IENx0T6CTBN/ELoq7J/+W9zf879uzKWuNaAulLVtBqrUbbqA7hTJpidnveZXzdwZRvlz/bU8kWAmXyhiDb2Q42Sz3BDb6duM3PO1AgG8Ko1pi2IemCPjO3uzudeT8FAlO8NnCUxKgwIKSz8CodOXFVGk66NX4xJd4ycfdNYXvKBNlzt1+WuWsZeZzeWmF7WD2dt4wWA9fWxB90fnth6ZV5LdeXjyYnzwkFOWoyNazgqV4jBv+aXKVwX4fYvspu13cVdrak3gc698bS2N1guDss4A/sfXMbtaYPGm98xXkqz1LP7sXQzKUdZf9sAS9gtOVv2tmg==,iv:uQ0Roe+XefzMjZCF3It+U2D1MWPMT5f6CPwlz0gQ5W0=,tag:wSgp0CVr6Y6M3eqcoTy8cw==,type:str]
ETHERCALC: ENC[AES256_GCM,data:0ScnDsUNBt6wYJC4hTXn8huuTptBTDKZV4yFVQ4fuBWc6auWNWhDQlTc0ImJoK6efr2uyp3sVu3o+KlCNvUGhDOJ1you6socyTgRP0q7oLPC+Ln+bFP8gWG8v2nyEFY=,iv:YqvVjBFG/WZg1l4aMAiioOruWZ9zcTMr74DVW+1+2DQ=,tag:ePBXd4ddipJtxhFE1amfMg==,type:str]
GITEA: ENC[AES256_GCM,data:TGsye52g8DVOk51o1dWfF6x3m6BFPe0MtUbOayxYejaYSZ4cDCfx02EPAhiL3FJGLfidZt7+WpjhVqJvFeCvJ1OXdYMQyuL3akPBCmEjmBgBhJvEjVtVgV3aNMS21gy3o0RG/MXDXOLpjHeaXn3G/XWKsv5bw+gg94bDirOguLC5eaFxddn5/UGFvfwXzDmkoNmc9zufGvVpUkRy8rju/TjOz4Q5GZk1gXWtJWdkTGhuVYVBDDHIQq9DBS0qXi8tP3FvY8prOi/c5ZbyyZjTOgNpWB9uU4HkHz4aUTMFIrPFwUeWmNMIdyUmarSQ7tDDdhBfIiLhb94jaMATHq+PfwwpCcmiTfBEG3OS/3SXPMWg6jhfUxvxsKNT2HQca31B9IRqlitaio3r6KEPBTYh2nWtFO9d5Is7TUWkGsOaV+0JSiNPNh5uD7kNyBxsKUKcND7JXMxW/TZOqARY64x6IwDrbcypsyMW5i+4Er1/0HCvZ7FBH1XkTul8vBGF5ZGD/tYp/m6Ld26GKYkj967eYIwlNFcOMGNue5PyJfFCq0qWbLWO9dFA9c2e9r81DCD8y/0S3Ts7X7TkXVGShm7JoMqb8dzg9i6CoNlmbPAM5GwoNV3ftQugvqEGMk5UIvf05YsgSNvq4UJbhsT4bZqpV0plnxYD5TswCGF/XQ3nwwFTudmf1OLcgYwM2NckbVui128o1Rw=,iv:vo6l0QirLIUvwLN675LYkffkXejJecvBesLJvoW/bjY=,tag:zyLyiCskF84A3QVoq5X3iw==,type:str]
SEARXNG: ENC[AES256_GCM,data:gtKhEmMemzLRl4c3cYhMAQ+5vUth1IhWQeLvW1YtaG5TbhQHBR4PDREQOlGt+tlfGQrft+FeNhMSN/SKOp8gmScVWa+9qmltzxRGRpLm3m/VuBZvOlGdeUcKAX8zEH6A,iv:B2UEtjTRIjT6W+tH2gtcl6XMvZNgbvZUXTiBePGOu24=,tag:SHIF6eaWBLwy9RrEy1N9kg==,type:str]
UMAMI: ENC[AES256_GCM,data:onB/uXLajaRLmeQMGNHFsjREzPih9ha+cogGRw+nRomERSRrbBv+6gCqEr8F3Dcm818JB4jGRYKoIYG8Jl6gMDaz5QQiA4qAnbG19LuzVeVUgz4NGEgXBULoT/0sQacnyAPIfPEp+ESWRQH81nO6Qcs+rICpS2Xfeye5hb+8rSAxmLpY991AJ3+avGyMwPcpfNCkixWt68KuG5ZN/IGDksM/sSLGgyMisClbEdhigq4mwibOxpiWjcKk/17xYgY6Xz93h/yloHKZIZZpnyA+85YC6oNWgCPhkGIAVu3dGshp10a0nk1A2INm6vxNPbfUjYLkt3zDAPZtoBRCqUs+43Eh62hYgajgWCQJhjJkDgF4Y1ifGfDerIXs/cDpIKLt2+7VqM6/ouqIDPJ7khSAr+8bcHU4CKDtsDagob5PpCG4ABt44cg9cGw=,iv:HD450JZuWn2+V0pvOsDHy9oVAanFMf1el9LA1z0PULY=,tag:p7Vl7dtM8UdAUNgmdG+7cg==,type:str]
IMMICH: ENC[AES256_GCM,data:1y78yeawkRjUXLWPyFdMB5HCDQhb1PoxEMfHmKSZfv0CWloOrQWT735dlH+W9yC6ljZjqVD9Fwq/9GqqKQMTFMCpr8wVRwSHEuqmaG3UgKzbLA3aWZ1SIB0AiJi+eUunzHj2vikUJx9dMRjC+iNXrsVWh2HqMrOyFCWetZoIfxNiAgsgNKPgYYsHLv6OAZs9XT7V3veqe0zc0nyw7ghWSXne/yNhQESyyGlMAdagrJRNimvXIp/AoAUKl2WUJm2MBl7lb6K1YeJ1XW8OjAHzV8isBiUwU8ZD81VJog0fgTGjbUa+HO7jEo+9YwmDIMx3f5z9N4A=,iv:pboITW2rr7+w8VNZM6uYMMEFZ1S/JtqjNOVthpYJ2tQ=,tag:0dgrJ191sB4MLJHMoQBlCg==,type:str]
INVIDIOUS: ENC[AES256_GCM,data:ZfgU5UFMmG9Cx9UaR0xnKr9VPebG3kut0difTFZmoqOSs+stG6YJfV82OOhj1RQLVJlPr/scydYy1+3LytwvP1BT7tLe0jII7XupbkL0w3n79KBaiIzAPdicqLxeqjKH45I0NjHra4djdnO2Ff4T8CTiFDlPn1rMuiw=,iv:UaDmOKJ4bFPGCaIePLXkWot9E6sTu2nhaVs83sI38G0=,tag:spTjxWEmLfPc8BZl2GglBA==,type:str]
SERVARR: ENC[AES256_GCM,data:757WdthmToCGr2boph7iW1ycs3tQyGgD3lhYOcX/X3hjs9dLLPCWGI2zt5axp72IGJ/sVYEop2rqsRLxdPn3VIyQLvQ+3MYdo8Z/yOuMy7DAlnITQQQUI2ylZKHVmFAt39/xBpwsVjh3m/hBQvn/LbCDtR2s4qa+8fQDfeZXksTtnf7YZbVygTF7jWZ+0oVvkvNO1ZUejvP+uHL+jHwgMEwQnR22hOYWEKZ1s7PI+EZHujqyOhnwXB4jRG+XD7R4N6AhC5Z+nmkFpy3ffszCJ0/H,iv:NrNbkL6GWN4r+uzxNYrhoECD1APbRsRBcMBbVHD3DwM=,tag:YK1O50wV+lHAQa6TX9huUw==,type:str]
INFLUX: ENC[AES256_GCM,data:woaZ/8zWRFtuO6U3HQ==,iv:iLo33tykE6oQW2jUNVGtR/JwrZbs9Fe1fFQ5/LnvaRY=,tag:6UDOU0JJgR7eyQ0hL8j5Qw==,type:str]
FRESHRSS: ENC[AES256_GCM,data:M5C76yVyi0Uw28FWj+IvQJbP1hdxxBGWfp30egjlv8Eu4tSZjHyfni+OLwgziDeQQyvRbn2OHwKtztEu4N2C7iU0UaotB0jCOc2BKuwfSWrxWfmkTDrY1YBfbGgLWEKQ1ddafI/Dn99n4HFbGpMZ8Fbe+sRKKGgpAPj9bMkUjoP3eXw4HUWhu84b6LWu3x8DwArhNDOkHJnDL+Rlif7hILg7+eI/IUB0XakKCMIKHZn9djk4yjgXMQGF5EFRlPVWgQ==,iv:2Kb916TksnVhby/GORx9nzir6A7GiRNL9S0wrbc8yDQ=,tag:v2Nhj61qd4f/YV1HcF0v5Q==,type:str]
SUWAYOMI: ENC[AES256_GCM,data:xVzuIWdEZLeFtkVew/Jbt0U+ouOjA+U/flhSAsWHPQHWgp7+6uvdBYIxcyQ+firHAu9qcAO/HahDgnr0lhcQx/n5XgEhCchiGxCNBcAi/AD+FE9/PgSJf2DvjCp0ckCWvPChGsy+TD8uNi1bg0lqSrbDExRS28f3FVPrbrJJ1vj/V9Gk4PABg/UcdvycgCpf266aFMMzNuPJGyaRuQEnyFNvQgs53R7t9D9hC9GSc4MkEGt7g0GeX1MTRTzjjISgCdzZxjUiJGSuFTBemQggcWOAJNdYFq1vuh5X3zBvlF+zz75g3cin9S58CQ==,iv:/zlNBdu5SuzC8+t4zOYVga3hLWnxlNUALi6BS4MjNog=,tag:DfeC0X7uX9/qhltFl2D2hg==,type:str]
CALIBRE: ENC[AES256_GCM,data:Uxz/5H4q/ugmW49a6oIQsG450w+SS1H58gOMXDVX/JQ8OCGxWQ8DRNmSGBZLgGsJ9UA1rxYRS4+pDuDL+iQsNVOmZzFRAuWJwUhEiF72B/Ah0IyXpnPIups8S2Nq,iv:rK274zWuvguY/DPHXxG174j+Ne11SB7ZCQLxO6Tvvg4=,tag:7wJyt94jRvRCZxgHWotzVA==,type:str]
SELFMARK: ENC[AES256_GCM,data:YDCfMay/SxmJxQcVPvXJe7h/BGjOjVZLU4eJ3+t6z4EDqUueNzmBPWtF1hrNJCC2XlZFlD+PmCyoo8tzuPiig4Uxwf4Gos15Skddx8Jt4/PKjR3AW0z0U8kAtXmByBAAa/nLEVwTkyRybYTXcd36SlqyTrbVfDLkIQW0yiqp93Fh40gWTV3u/gz2ZeLit12o3LNnm7Zne7y8w7iAO2vm2L5h2W8R+9B8oW1MSweclKgDlMieNh+pmxexnNdvGX5jAgaOM2xSUC8Z/PcgSCAYwNVe0CYsHPypES/miS3XrVs4E/YIaMGaTi7lTS+zxy9x8F17O/MVHoy7yuzjQYBZ1w+PmMOYv+Wc1mVJswAHMfvZE6EUFL/P7Qy2T+c19cowphZTuuGZMaIqjeWfn4ZHPlVhWEdamk3dhyqA2I2g1X5cANP1FV73FJC/3gTqKXkkp9wcWqN59gAHmUzn2D5Mhmvx2UkTbyUyNxR71VdpSN4szYifmEdp56KQVpjrw4dSst97sWCzI6nrDn4P9J3TrTpCFJM2PSbyF23NIVuPCx2pdqTdjCr1IX+9v1qLKOcbIZq/S3ldQ6E2PZnvIA6faoE5SO5uy8qUwct54Jc+p6WTNFRMzfbFs31xVgiiR3Y5HsTc3Ee2ePhu1ggvCa8o4CW9zqO5jqFX38C8n0jYhc31AlVui0hswECHBgDqFQ3pZS10Fx4heYjnfJRTB8OrE0/lf0QYgMqLcWJyxJqwutoa5uaQxiq1QV3beyj8Yv0/kSEqrWvItvZaM2MCkmBjxDtfBqx1zIZ3Lm2xYJmOLB/SgYYWi+0d9yS2Dhe8mpOr2/xMsgdUUz2YbCX5YfA=,iv:nohcihzxdZORTKrrYnupqts4REcyioqlfFhQOM9K/H8=,tag:2sPes1v7LfvIj0hC/tKClw==,type:str]
sops:
age:
- recipient: age1sxzuhh2fcd4pmaz4mdqq95t683d32ft22w9t2r7pk258u0s8wymsqdj7lg
@@ -20,8 +36,8 @@ sops:
S1NaTVFTL0FCdm1EQmRsUnlhclZNZlEKEgIe60qkvY8+UocjQU+WM2dTL/1y3Kqk
d4RrlLP9NSozwVsPYI4ntygvMSApbT4v0YvoO7gV90lkGWEvW1YDfA==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2026-05-06T01:10:20Z"
mac: ENC[AES256_GCM,data:O4RLfEE6z0uDRpZdL47Or+z/PTeJ+zgzXN9kJS6Nebs9Uhw0XUJUPGhAocLokiMin5sQcpxXG5Q8oc2rAkq2GDbtna4u26dtNkd2Q/vtly6DqUaIRXXt3TL5cfJwMNa76fp+ERKLwGbBG+/BFWajzYJtcE257I8t3X4UmAdqYmE=,iv:uYLh8LnGobf7t3Ur7drEiA6n3Vv0e0yhlja6Uww8jiU=,tag:ZK3OCCsiMPtKl28lrGKtqQ==,type:str]
lastmodified: "2026-06-02T17:28:48Z"
mac: ENC[AES256_GCM,data:vJbMJ/VsetyhQHEQH6eBLp9MA5OMkARSkXW73nWNtJ3wSA/EWJgyb4LGj316W2zdwlE/KatSdzrQgITNCkpIwIdzSLrl+4xDP1qW0F7eJFR9fpEqvj0pfH9Sszh7izOEiHaMwMJo0rw5d6sMTFyDcuOOAn9Q/mzc5zd/VX6AmtA=,iv:ZR2nPyZMu3Rb1GXNvuRxazox7FB2fWobFexSwhVuYQQ=,tag:xoJZ2xMkWRC86G2w5291fg==,type:str]
pgp:
- created_at: "2026-05-05T23:46:27Z"
enc: |-
+1 -1
View File
@@ -1,4 +1,4 @@
{ config, ... }: {
{ ... }: {
imports = [ ./sorahiro.nix ];
colorScheme.palette.border-radius = "#8";
+1 -1
View File
@@ -1,4 +1,4 @@
{ nix-colors, ... }:
{ ... }:
let use_pastelle = true;
in{
# usage: a = "#${config.colorScheme.palette.base00}";
Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 959 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

+10 -163
View File
@@ -5,186 +5,33 @@ let
(name: type: type == "directory" && builtins.pathExists (systemsDir + "/${name}/cfg.nix"))
(builtins.readDir systemsDir));
userOpt = with lib; {
username = mkOption { type = types.str; };
pubssh = mkOption { type = types.str; default=""; };
wm = mkOption {
type = types.enum [ "Wayland" "X11" "-" ];
default = "-";
};
git = {
username = mkOption { type = types.str; default = "Anonymous";};
email = mkOption { type = types.str; default = "anonymous@domain"; };
key = mkOption { type = types.nullOr types.str; default=null; };
};
};
netOpt = with lib; {
ble = {
enable = mkOption {
type = types.bool;
default = false;
};
};
wlp = {
enable = mkOption {
type = types.bool;
default = false;
};
nif = mkOption {
type = types.str;
default = "";
};
};
wg = {
enable = mkOption {
type = types.bool;
default = false;
};
ip4 = mkOption {
type = types.str;
default = "";
};
ip6 = mkOption {
type = types.str;
default = "";
};
pubkey = mkOption {
type = types.str;
default = "";
};
};
};
makeOpt = with lib; {
cli = mkOption {
type = types.bool;
default = true;
};
gui = mkOption {
type = types.bool;
default = false;
};
virt = mkOption {
type = types.bool;
default = false;
};
power = mkOption {
type = types.bool;
default = false;
};
game = mkOption {
type = types.bool;
default = false;
};
develop = mkOption {
type = types.bool;
default = false;
};
};
serverOpt = with lib; {
hostDomain = mkOption { type = types.str; };
shortName = mkOption { type = types.str; };
mailDomain = mkOption { type = types.str; };
mailServer = mkOption { type = types.str; };
dbHost = mkOption {
type = types.str;
default = "localhost";
};
dbPort = mkOption {
type = types.str;
default = "3306";
};
configPath = mkOption {
type = types.str;
default = "/media/config";
};
dataPath = mkOption {
type = types.str;
default = "/media/data";
};
containers = mkOption {
type = types.attrsOf (types.submodule {
options = {
enable = mkOption { type = types.bool;default = false; };
db = mkOption { type = types.bool;default = false; };
ip = mkOption { type = types.str; };
port = mkOption { type = types.port; };
extraParam = mkOption { type = types.str; default = ""; };
};
});
default = {};
};
sops = mkOption {
type = types.bool;
default = false;
};
openssh = mkOption {
type = types.bool;
default = false;
};
wireguard = mkOption {
type = types.bool;
default = false;
};
web = mkOption {
type = types.bool;
default = false;
};
nftables = {
enable = mkOption {
type = types.bool;
default = false;
};
ifs = mkOption {
type = types.listOf types.str;
default = [ ];
};
ports = mkOption {
type = types.listOf (types.listOf (types.oneOf [ types.str types.int ]));
default = [];
description = "Forwarding rules: [ [srcInterface dstAddr srcPort dstPort] ... ]";
example = [
[ "ens3" "10.10.1.2" "IPV6" 22 2222 ]
[ "ens3" "10.10.1.2" "IPV6" 80 80 ]
[ "ens3" "10.10.1.2" "IPV6" 443 443 ]
];
};
};
db = mkOption {
type = types.listOf (types.str);
default = [ ];
};
};
in with lib; {
options.usercfg = userOpt;
options.usercfg = import ./user.nix {inherit lib;};
options.syscfg = {
hostname = mkOption { type = types.str; };
type = mkOption {
type = types.enum [ "nixos" "macos" "home" ];
default = "nixos";
};
system = mkOption {
type = types.enum [ "x86_64-linux" "x86_64-darwin" "-" ];
default = "x86_64-linux";
};
defaultUser = mkOption { type = types.str; };
make = makeOpt;
net = netOpt;
make = import ./make.nix {inherit lib;};
net = import ./net.nix {inherit lib;};
users = mkOption {
type = types.listOf (types.submodule { options = userOpt; });
type = types.listOf (types.submodule { options = import ./user.nix {inherit lib;}; });
default = [ ];
};
peers = mkOption {
default = map (name: import (systemsDir + "/${name}/cfg.nix")) systemNames;
};
server = mkOption {
type = types.oneOf [
types.bool
(types.submodule { options = serverOpt; })
];
type = types.oneOf [ types.bool (types.submodule { options = import ./server.nix {inherit lib;}; }) ];
default = false;
};
media = mkOption {
type = types.submodule { options = import ./media.nix { inherit lib; }; };
default = {};
};
extra = import ./extra.nix {inherit lib;};
};
}
+11
View File
@@ -0,0 +1,11 @@
{ lib,... }:
with lib; {
wsl = mkOption { type = types.bool; default = false; };
hosts = mkOption { type = types.listOf (types.str); default = []; };
proxy = {
domain = mkOption { type = types.str; default = ""; };
port = mkOption { type = types.str; default = ""; };
noProxy = mkOption { type = types.str; default = ""; };
cert = mkOption { type = types.path; default = null; };
};
}
+9
View File
@@ -0,0 +1,9 @@
{ lib,... }:
with lib; {
cli = mkOption { type = types.bool; default = true; };
gui = mkOption { type = types.bool; default = false; };
virt = mkOption { type = types.bool; default = false; };
power = mkOption { type = types.bool; default = false; };
game = mkOption { type = types.bool; default = false; };
develop = mkOption { type = types.bool; default = false; };
}
+51
View File
@@ -0,0 +1,51 @@
{ lib, ... }:
let
mediaDir = ../media;
mainDir = mediaDir + "/main";
maybePath = rel:
let path = mediaDir + "/${rel}";
in if builtins.pathExists path then path else null;
mainImages =
let
names = builtins.attrNames (builtins.readDir mainDir);
isImage = name: builtins.match ".*\\.(png|jpg|jpeg|webp|svg)$" name != null;
in
map (name: mainDir + "/${name}") (lib.sort builtins.lessThan (lib.filter isImage names));
in
with lib; {
logo = {
ico = mkOption {
type = types.path;
default = mediaDir + "/logo.ico";
};
svg = mkOption {
type = types.path;
default = mediaDir + "/logo.svg";
};
png = mkOption {
type = types.path;
default = mediaDir + "/logo.png";
};
};
banner = {
svg = mkOption {
type = types.nullOr types.path;
default = maybePath "banner.svg";
};
png = mkOption {
type = types.nullOr types.path;
default = maybePath "banner.png";
};
};
main = mkOption {
type = types.listOf types.path;
default = if mainImages != [ ] then mainImages else lib.optional (maybePath "main/bg.png" != null) (mediaDir + "/main/bg.png");
};
bg = mkOption {
type = types.path;
default = let bg = maybePath "main/bg.png"; in if bg != null then bg else builtins.head mainImages;
};
}
+14
View File
@@ -0,0 +1,14 @@
{ lib,... }:
with lib; {
ble.enable = mkOption { type = types.bool; default = false; };
wlp = {
enable = mkOption { type = types.bool; default = false; };
nif = mkOption { type = types.str; default = ""; };
};
wg = {
enable = mkOption { type = types.bool; default = false; };
ip4 = mkOption { type = types.str; default = ""; };
ip6 = mkOption { type = types.str; default = ""; };
pubkey = mkOption { type = types.str; default = ""; };
};
}
+135
View File
@@ -0,0 +1,135 @@
{ lib,... }:
let
in with lib; {
domain = mkOption { type = types.str; };
mail = {
domain = mkOption { type = types.nullOr types.str; default = null;};
server = mkOption { type = types.nullOr types.str; default = null;};
};
path = mkOption {
type = types.submodule {
freeformType = types.attrsOf types.str;
options = {
config = mkOption { type = types.str; default = "/media/config"; };
data = mkOption { type = types.str; default = "/media/data"; };
download = mkOption { type = types.str; default = "/media/data/download"; };
cloud = mkOption { type = types.str; default ="/media/media/cloud"; };
film = mkOption { type = types.str; default ="/media/media/film"; };
book = mkOption { type = types.str; default ="/media/media/book"; };
manga = mkOption { type = types.str; default ="/media/media/manga"; };
photo = mkOption { type = types.str; default ="/media/media/photo"; };
# music = mkOption { type = types.str; default ="/media/media/music"; };
dlComplete = mkOption { type = types.str; default ="/media/download/complete"; };
dlIncomplete = mkOption { type = types.str; default ="/media/download/incomplete"; };
dlConverted = mkOption { type = types.str; default ="/media/download/converted"; };
};
};
default = {};
};
colorScheme = mkOption {
type = types.attrs;
default = (lib.evalModules { modules =[ { freeformType = with lib.types; attrsOf anything; } ../colors ];}).config.colorScheme ;
};
loadedContainers = lib.mkOption {
readOnly = true;
type = lib.types.attrsOf (lib.types.submodule ({ name, ... }: {
options = {
name = lib.mkOption {type = lib.types.str; default = name;};
requires = {
secrets = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
};
databases = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
};
};
exports = {
authentik = {
blueprints = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
};
};
};
runtime = {
paths = lib.mkOption {type = lib.types.listOf lib.types.attrs; default = [ ];};
containers = lib.mkOption {type = lib.types.attrsOf lib.types.attrs; default = { };};
vm = lib.mkOption {type = lib.types.nullOr lib.types.attrs; default = null;};
cron = lib.mkOption {type = lib.types.listOf lib.types.str; default = [ ];};
setup = lib.mkOption {
type = lib.types.submodule {
options = {
trigger = lib.mkOption {type = lib.types.str; default = "";};
script = lib.mkOption {type = lib.types.nullOr lib.types.package; default = null;};
envFile = lib.mkOption {
type = with lib.types; coercedTo str (x: [x]) (listOf str);
default = [ ];
};
};
};
default = { };
};
};
};
}));
};
containers = mkOption {
type = types.attrsOf (types.submodule {
options = {
subdomain = mkOption { type = types.nullOr types.str; default=null;};
subpath = mkOption { type = types.nullOr types.str; default=null;};
port = mkOption { type = types.nullOr types.port; default = null; };
extra = mkOption { type = types.attrs; default = {}; };
};
});
default = {};
};
openssh = mkOption {
type = types.bool;
default = false;
};
wireguard = mkOption {
type = types.bool;
default = false;
};
web = mkOption {
type = types.bool;
default = false;
};
ipfw = {
enable = mkOption {
type = types.bool;
default = false;
};
ifs = mkOption {
type = types.listOf types.str;
default = [ ];
};
ports = mkOption {
type = types.listOf (types.listOf (types.oneOf [ types.str types.int ]));
default = [];
description = "Forwarding rules: [ [srcInterface dstAddr srcPort dstPort] ... ]";
example = [
[ "ens3" "10.10.1.2" "IPV6" 22 2222 ]
[ "ens3" "10.10.1.2" "IPV6" 80 80 ]
[ "ens3" "10.10.1.2" "IPV6" 443 443 ]
];
};
};
db = mkOption {
type = types.listOf (types.str);
default = [ ];
};
}

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