From 80d0f8fc9fb158217e6e75b4a71d1987ea9b5a79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tristan=20Dani=C3=ABl=20Maat?= Date: Sun, 19 Oct 2025 03:19:30 +0800 Subject: [PATCH 01/24] WIP: Auth --- configuration/default.nix | 1 + configuration/services/auth/authelia.nix | 87 ++++++++++++++++++++++++ configuration/services/auth/default.nix | 5 ++ configuration/services/postgres.nix | 5 ++ keys/staging.yaml | 10 ++- 5 files changed, 105 insertions(+), 3 deletions(-) create mode 100644 configuration/services/auth/authelia.nix create mode 100644 configuration/services/auth/default.nix diff --git a/configuration/default.nix b/configuration/default.nix index f2d1615..041c885 100644 --- a/configuration/default.nix +++ b/configuration/default.nix @@ -13,6 +13,7 @@ "${modulesPath}/profiles/minimal.nix" (import ../modules) + ./services/auth ./services/backups.nix ./services/battery-manager.nix ./services/conduit diff --git a/configuration/services/auth/authelia.nix b/configuration/services/auth/authelia.nix new file mode 100644 index 0000000..f05e3f0 --- /dev/null +++ b/configuration/services/auth/authelia.nix @@ -0,0 +1,87 @@ +{ config, ... }: +let + instanceName = config.services.authelia.instances.main.name; +in +{ + services.authelia.instances.main = { + enable = true; + + settings = { + theme = "auto"; + default_2fa_method = "totp"; + + authentication_backend = { + password_reset.disable = true; + password_change.disable = true; + + file = { + inherit (config.sops.secrets."authelia/users") path; + + search = { + email = true; + case_insensitive = false; + }; + }; + }; + + storage.postgres = { + address = "unix:///run/postgresql"; + database = "authelia"; + username = "authelia"; + }; + + session.cookies = [ + { + domain = config.services.nginx.domain; + authelia_url = "https://auth.${config.services.nginx.domain}"; + } + ]; + + notifier.filesystem.filename = ''{{ env "RUNTIME_DIRECTORY" }}/authelia-notifications''; + + access_control = { + + }; + + server = { + # Maybe a systemd socket can be used for this in the future, + # see: + # https://github.com/systemd/systemd/issues/23067#issuecomment-1212232155 + address = "unix://${config.systemd.sockets."authelia-${instanceName}".socketConfig.ListenStream}"; + }; + }; + + secrets = { + jwtSecretFile = config.sops.secrets."authelia/jwt".path; + storageEncryptionKeyFile = config.sops.secrets."authelia/storage".path; + }; + }; + + systemd.sockets."authelia-${instanceName}" = { + socketConfig = { + Accept = false; + ListenStream = "/var/run/authelia.sock"; + SocketGroup = "authelia"; + SocketMode = "0660"; + }; + }; + + systemd.services."authelia-${instanceName}" = { + requires = [ "authelia-${instanceName}.socket" ]; + + serviceConfig = { + RuntimeDirectory = "authelia-${instanceName}"; + SupplementaryGroups = [ "authelia" ]; + }; + }; + + # TODO: Need to map these to systemd creds to pass them into the + # service because user permissions + sops.secrets = { + "authelia/users" = { }; + "authelia/jwt" = { }; + "authelia/storage" = { }; + }; + + users.groups.authelia = { }; +} diff --git a/configuration/services/auth/default.nix b/configuration/services/auth/default.nix new file mode 100644 index 0000000..4b9c671 --- /dev/null +++ b/configuration/services/auth/default.nix @@ -0,0 +1,5 @@ +{ + imports = [ + ./authelia.nix + ]; +} diff --git a/configuration/services/postgres.nix b/configuration/services/postgres.nix index 85a6843..ddb2c82 100644 --- a/configuration/services/postgres.nix +++ b/configuration/services/postgres.nix @@ -17,6 +17,10 @@ # that operation needs to be performed manually on the system as # well. ensureUsers = [ + { + name = "authelia"; + ensureDBOwnership = true; + } { name = "grafana"; ensureDBOwnership = true; @@ -28,6 +32,7 @@ ]; ensureDatabases = [ + "authelia" "grafana" "nextcloud" ]; diff --git a/keys/staging.yaml b/keys/staging.yaml index 6fa212d..caa5707 100644 --- a/keys/staging.yaml +++ b/keys/staging.yaml @@ -1,3 +1,7 @@ +authelia: + users: "" + jwt: ENC[AES256_GCM,data:oKA1B7zZAzTZL4nBdHvPENVx7M2BgbMBmNtetri0qCVB7qNFIgbnwVCJFiDvjKxxNdedqUKBZZL5QJbTlPNRxCVdFgBBMFiib3khxMP8kzqff2MgJZxumonlJt5Jmh8tVxwLRJwE/2fp/N9w2hRs0vhfmMyAA4y7RZv3R9/eaKM=,iv:2iTAwP6dipPBMskyygnBJHJ53E0nmHYcGyWDrODEs1Q=,tag:koSEZtQQzOzpbQBgUP5ZHw==,type:str] + storage: ENC[AES256_GCM,data:bO+bHu6jRvfbLU6xIDaE2JwXpNnMK916Upv43ycg9fCb+U5hqQfqBBwC2xVEVXtCBRq1VER+gc8rs8/XDT9vZkvMUqAHj4RqXHyzX0UjwsvccBJSLfoLUiT6obk3oVLo5CY7R2TukPuyFXPbMUOrBk9gnbk7z4IWzcwNnuOKBT4=,iv:RmKIS/cgZ0tUQDFF2yfaJnfTvPaeadjG0LPXKIzYFrA=,tag:XmqDhDf3Ja1BsyrYmzTKDg==,type:str] porkbun: api-key: ENC[AES256_GCM,data:A5J1sqwq6hs=,iv:77Mar3IX7mq7z7x6s9sSeGNVYc1Wv78HptJElEC7z3Q=,tag:eM/EF9TxKu+zcbJ1SYXiuA==,type:str] secret-api-key: ENC[AES256_GCM,data:8Xv+jWYaWMI=,iv:li4tdY0pch5lksftMmfMVS729caAwfaacoztaQ49az0=,tag:KhfElBGzVH4ByFPfuQsdhw==,type:str] @@ -32,8 +36,8 @@ turn: #ENC[AES256_GCM,data:bxhKzU5Tzezl749CDu8e8kxa7ahGuZFaPa9K3kxuD+4sg5Hi3apgDlC0n8oK0DeiK4Ks7+9Cyw==,iv:T/zVJUpNAv1rR0a9+6SDTG08ws2A1hFBs5Ia3TpT0uk=,tag:uGXb1VryM+lIJ8r0I5durA==,type:comment] ssl-cert: ENC[AES256_GCM,data:xHUr14CjKslgbGh/n5jYSOuCw9JRxS6YXE4fxS+aJzFcNeSeGNqoipPeuJupZGBnQP/FCqohiHY=,iv:/OEsVqRshGL9NIvntMC42EPZSNL0u6EfhtUBqgV7qog=,tag:4pxtNjuvy/ibm6nDtKdSkw==,type:str] sops: - lastmodified: "2025-02-07T17:43:24Z" - mac: ENC[AES256_GCM,data:akmD/bfgeTyFzW1quvM16cdj0fC6+CbJ8WyX9173H11yKGxvE1USQYcErpl1SHOx9Jk8LVb7f+MsUm2fjQF1MEq6xaWI74jem12lZ9CGXFaTL7e87JvfbK7pV+aKpxSBBNFyJgbYm30ibdUwxwKmNVfPb1e0HT9qwenvoV7RobM=,iv:mKqOW0ULXL711uczUbRf9NPo6uPTQoS/IbR46S+JID4=,tag:vE6NYzYLbQHDImov1XGTcg==,type:str] + lastmodified: "2025-10-20T20:04:21Z" + mac: ENC[AES256_GCM,data:kRrmVm3PQooRA/MoHgDb9EaRnoKY9CJxAflus9Po8NBmyQxV6Ehjf8DlI6yf7ZpPlhV+VHZJamyPD+hsHp1hSr8krvr0o52ZQdKn4MJQzSQXa4K9i3i0+glj7cNGs2SzTJnKwN9lxBywZpbVDlkXmvRQYLE9tWPWoSBdurOibjw=,iv:2iBQ1cYT85mCc7jf2GTEOjNiHBlR/F76Dvjl/k5dyLA=,tag:Z7dY2i0KWmmoVp7VJjq1Sw==,type:str] pgp: - created_at: "2025-10-03T21:38:26Z" enc: |- @@ -67,4 +71,4 @@ sops: -----END PGP MESSAGE----- fp: 2f5caa73e7ceea4fcc8d2881fde587e6737d2dbc unencrypted_suffix: _unencrypted - version: 3.9.2 + version: 3.11.0 From 79eb8f94248a171100c8225f98a695d0dd87bcfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tristan=20Dani=C3=ABl=20Maat?= Date: Thu, 13 Nov 2025 01:29:19 +0800 Subject: [PATCH 02/24] feat(checks): Fully rework lints This is currently copied from my dotfiles. Should probably consider using flake-parts to DRY this out. --- checks/default.nix | 94 ++--- checks/lib.nix | 35 ++ flake.lock | 864 ++++++--------------------------------------- flake.nix | 48 ++- 4 files changed, 221 insertions(+), 820 deletions(-) create mode 100644 checks/lib.nix diff --git a/checks/default.nix b/checks/default.nix index f3db4ce..674b19d 100644 --- a/checks/default.nix +++ b/checks/default.nix @@ -1,61 +1,45 @@ -{ - self, - nixpkgs, - deploy-rs, - system, - ... -}: +{ flake-inputs }: let - pkgs = nixpkgs.legacyPackages.${system}; - - statix' = pkgs.statix.overrideAttrs (old: { - patches = old.patches ++ [ - (pkgs.fetchpatch { - url = "https://github.com/oppiliappan/statix/commit/925dec39bb705acbbe77178b4d658fe1b752abbb.patch"; - hash = "sha256-0wacO6wuYJ4ufN9PGucRVJucFdFFNF+NoHYIrLXsCWs="; - }) - ]; - }); - - runNuCheck = + inherit (flake-inputs.nixpkgs) lib; + pkgs = flake-inputs.nixpkgs.legacyPackages.x86_64-linux; + checkLib = pkgs.callPackage ./lib.nix { }; +in +{ + x86_64-linux = lib.mergeAttrsList [ { - name, - packages, - check, - }: - pkgs.stdenvNoCC.mkDerivation { - inherit name; + nix = checkLib.mkLint { + name = "nix-lints"; + fileset = lib.fileset.fileFilter (file: file.hasExt "nix") ../.; - src = nixpkgs.lib.cleanSourceWith { - src = self; - filter = nixpkgs.lib.cleanSourceFilter; + checkInputs = lib.attrValues { + inherit (pkgs) deadnix nixfmt-rfc-style; + + statix = pkgs.statix.overrideAttrs (old: { + patches = old.patches ++ [ + (pkgs.fetchpatch { + url = "https://github.com/oppiliappan/statix/commit/925dec39bb705acbbe77178b4d658fe1b752abbb.patch"; + hash = "sha256-0wacO6wuYJ4ufN9PGucRVJucFdFFNF+NoHYIrLXsCWs="; + }) + ]; + }); + }; + + script = '' + statix check **/*.nix + deadnix --fail **/*.nix + nixfmt --check --strict **/*.nix + ''; }; - dontPatch = true; - dontConfigure = true; - dontBuild = true; - dontInstall = true; - dontFixup = true; - doCheck = true; + lockfile = checkLib.mkLint { + name = "nix-lockfile"; + fileset = ../flake.lock; + checkInputs = lib.attrValues { inherit (flake-inputs.flint.packages.x86_64-linux) flint; }; - checkInputs = nixpkgs.lib.singleton pkgs.nushell ++ packages; - - checkPhase = '' - nu ${check} - ''; - }; -in -nixpkgs.lib.recursiveUpdate { - lints = runNuCheck { - name = "lints"; - - packages = [ - pkgs.deadnix - pkgs.nixfmt-rfc-style - pkgs.shellcheck - statix' - ]; - - check = ./lints.nu; - }; -} (deploy-rs.lib.${system}.deployChecks self.deploy) + script = '' + flint --fail-if-multiple-versions + ''; + }; + } + ]; +} diff --git a/checks/lib.nix b/checks/lib.nix new file mode 100644 index 0000000..d38cd21 --- /dev/null +++ b/checks/lib.nix @@ -0,0 +1,35 @@ +{ pkgs, lib, ... }: +{ + mkLint = + { + name, + fileset, + checkInputs ? [ ], + script, + }: + pkgs.stdenvNoCC.mkDerivation { + inherit name; + + src = lib.fileset.toSource { + root = ../.; + fileset = lib.fileset.difference fileset ( + lib.fileset.fileFilter ( + file: file.type != "regular" || file.name == "hardware-configuration.nix" + ) ../. + ); + }; + + checkInputs = [ pkgs.nushell ] ++ checkInputs; + + checkPhase = '' + nu -c '${script}' | tee $out + ''; + + dontPatch = true; + dontConfigure = true; + dontBuild = true; + dontInstall = true; + dontFixup = true; + doCheck = true; + }; +} diff --git a/flake.lock b/flake.lock index a62c357..705d87c 100644 --- a/flake.lock +++ b/flake.lock @@ -10,89 +10,17 @@ "sonnenshift", "crate2nix" ], - "nixpkgs": "nixpkgs_3", - "pre-commit-hooks": [ - "sonnenshift", - "crate2nix" + "git-hooks": "git-hooks", + "nixpkgs": [ + "nixpkgs" ] }, "locked": { - "lastModified": 1709700175, - "narHash": "sha256-A0/6ZjLmT9qdYzKHmevnEIC7G+GiZ4UCr8v0poRPzds=", + "lastModified": 1760971495, + "narHash": "sha256-IwnNtbNVrlZIHh7h4Wz6VP0Furxg9Hh0ycighvL5cZc=", "owner": "cachix", "repo": "cachix", - "rev": "be97b37989f11b724197b5f4c7ffd78f12c8c4bf", - "type": "github" - }, - "original": { - "owner": "cachix", - "ref": "latest", - "repo": "cachix", - "type": "github" - } - }, - "cachix_2": { - "inputs": { - "devenv": [ - "sonnenshift", - "crate2nix", - "crate2nix_stable" - ], - "flake-compat": [ - "sonnenshift", - "crate2nix", - "crate2nix_stable" - ], - "nixpkgs": "nixpkgs_4", - "pre-commit-hooks": [ - "sonnenshift", - "crate2nix", - "crate2nix_stable" - ] - }, - "locked": { - "lastModified": 1716549461, - "narHash": "sha256-lHy5kgx6J8uD+16SO47dPrbob98sh+W1tf4ceSqPVK4=", - "owner": "cachix", - "repo": "cachix", - "rev": "e2bb269fb8c0828d5d4d2d7b8d09ea85abcacbd4", - "type": "github" - }, - "original": { - "owner": "cachix", - "ref": "latest", - "repo": "cachix", - "type": "github" - } - }, - "cachix_3": { - "inputs": { - "devenv": [ - "sonnenshift", - "crate2nix", - "crate2nix_stable", - "crate2nix_stable" - ], - "flake-compat": [ - "sonnenshift", - "crate2nix", - "crate2nix_stable", - "crate2nix_stable" - ], - "nixpkgs": "nixpkgs_5", - "pre-commit-hooks": [ - "sonnenshift", - "crate2nix", - "crate2nix_stable", - "crate2nix_stable" - ] - }, - "locked": { - "lastModified": 1716549461, - "narHash": "sha256-lHy5kgx6J8uD+16SO47dPrbob98sh+W1tf4ceSqPVK4=", - "owner": "cachix", - "repo": "cachix", - "rev": "e2bb269fb8c0828d5d4d2d7b8d09ea85abcacbd4", + "rev": "c5bfd933d1033672f51a863c47303fc0e093c2d2", "type": "github" }, "original": { @@ -105,16 +33,21 @@ "crate2nix": { "inputs": { "cachix": "cachix", - "crate2nix_stable": "crate2nix_stable", - "devshell": "devshell_3", - "flake-compat": "flake-compat_4", - "flake-parts": "flake-parts_3", - "nix-test-runner": "nix-test-runner_3", - "nixpkgs": [ + "crate2nix_stable": [ "sonnenshift", + "crate2nix" + ], + "devshell": "devshell", + "flake-compat": [ + "deploy-rs", + "flake-compat" + ], + "flake-parts": "flake-parts", + "nix-test-runner": "nix-test-runner", + "nixpkgs": [ "nixpkgs" ], - "pre-commit-hooks": "pre-commit-hooks_3" + "pre-commit-hooks": "pre-commit-hooks" }, "locked": { "lastModified": 1739473963, @@ -130,81 +63,12 @@ "type": "github" } }, - "crate2nix_stable": { - "inputs": { - "cachix": "cachix_2", - "crate2nix_stable": "crate2nix_stable_2", - "devshell": "devshell_2", - "flake-compat": "flake-compat_3", - "flake-parts": "flake-parts_2", - "nix-test-runner": "nix-test-runner_2", - "nixpkgs": "nixpkgs_7", - "pre-commit-hooks": "pre-commit-hooks_2" - }, - "locked": { - "lastModified": 1719760004, - "narHash": "sha256-esWhRnt7FhiYq0CcIxw9pvH+ybOQmWBfHYMtleaMhBE=", - "owner": "nix-community", - "repo": "crate2nix", - "rev": "1dee214bb20855fa3e1e7bb98d28922ddaff8c57", - "type": "github" - }, - "original": { - "owner": "nix-community", - "ref": "0.14.1", - "repo": "crate2nix", - "type": "github" - } - }, - "crate2nix_stable_2": { - "inputs": { - "cachix": "cachix_3", - "crate2nix_stable": "crate2nix_stable_3", - "devshell": "devshell", - "flake-compat": "flake-compat_2", - "flake-parts": "flake-parts", - "nix-test-runner": "nix-test-runner", - "nixpkgs": "nixpkgs_6", - "pre-commit-hooks": "pre-commit-hooks" - }, - "locked": { - "lastModified": 1712821484, - "narHash": "sha256-rGT3CW64cJS9nlnWPFWSc1iEa3dNZecVVuPVGzcsHe8=", - "owner": "nix-community", - "repo": "crate2nix", - "rev": "42883afcad3823fa5811e967fb7bff54bc3c9d6d", - "type": "github" - }, - "original": { - "owner": "nix-community", - "ref": "0.14.0", - "repo": "crate2nix", - "type": "github" - } - }, - "crate2nix_stable_3": { - "inputs": { - "flake-utils": "flake-utils" - }, - "locked": { - "lastModified": 1702842982, - "narHash": "sha256-A9AowkHIjsy1a4LuiPiVP88FMxyCWK41flZEZOUuwQM=", - "owner": "nix-community", - "repo": "crate2nix", - "rev": "75ac2973affa6b9b4f661a7b592cba6e4f51d426", - "type": "github" - }, - "original": { - "owner": "nix-community", - "ref": "0.12.0", - "repo": "crate2nix", - "type": "github" - } - }, "deploy-rs": { "inputs": { "flake-compat": "flake-compat", - "nixpkgs": "nixpkgs", + "nixpkgs": [ + "nixpkgs" + ], "utils": "utils" }, "locked": { @@ -223,56 +87,6 @@ }, "devshell": { "inputs": { - "flake-utils": "flake-utils_2", - "nixpkgs": [ - "sonnenshift", - "crate2nix", - "crate2nix_stable", - "crate2nix_stable", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1717408969, - "narHash": "sha256-Q0OEFqe35fZbbRPPRdrjTUUChKVhhWXz3T9ZSKmaoVY=", - "owner": "numtide", - "repo": "devshell", - "rev": "1ebbe68d57457c8cae98145410b164b5477761f4", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "devshell", - "type": "github" - } - }, - "devshell_2": { - "inputs": { - "flake-utils": "flake-utils_3", - "nixpkgs": [ - "sonnenshift", - "crate2nix", - "crate2nix_stable", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1717408969, - "narHash": "sha256-Q0OEFqe35fZbbRPPRdrjTUUChKVhhWXz3T9ZSKmaoVY=", - "owner": "numtide", - "repo": "devshell", - "rev": "1ebbe68d57457c8cae98145410b164b5477761f4", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "devshell", - "type": "github" - } - }, - "devshell_3": { - "inputs": { - "flake-utils": "flake-utils_4", "nixpkgs": [ "sonnenshift", "crate2nix", @@ -280,11 +94,11 @@ ] }, "locked": { - "lastModified": 1711099426, - "narHash": "sha256-HzpgM/wc3aqpnHJJ2oDqPBkNsqWbW0WfWUO8lKu8nGk=", + "lastModified": 1762521437, + "narHash": "sha256-RXN+lcx4DEn3ZS+LqEJSUu/HH+dwGvy0syN7hTo/Chg=", "owner": "numtide", "repo": "devshell", - "rev": "2d45b54ca4a183f2fdcf4b19c895b64fbf620ee8", + "rev": "07bacc9531f5f4df6657c0a02a806443685f384a", "type": "github" }, "original": { @@ -315,16 +129,18 @@ }, "dream2nix": { "inputs": { - "nixpkgs": "nixpkgs_8", + "nixpkgs": [ + "nixpkgs" + ], "purescript-overlay": "purescript-overlay", "pyproject-nix": "pyproject-nix" }, "locked": { - "lastModified": 1735160684, - "narHash": "sha256-n5CwhmqKxifuD4Sq4WuRP/h5LO6f23cGnSAuJemnd/4=", + "lastModified": 1754978539, + "narHash": "sha256-nrDovydywSKRbWim9Ynmgj8SBm8LK3DI2WuhIqzOHYI=", "owner": "nix-community", "repo": "dream2nix", - "rev": "8ce6284ff58208ed8961681276f82c2f8f978ef4", + "rev": "fbec3263cb4895ac86ee9506cdc4e6919a1a2214", "type": "github" }, "original": { @@ -371,112 +187,7 @@ "type": "github" } }, - "flake-compat_2": { - "locked": { - "lastModified": 1696426674, - "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", - "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", - "revCount": 57, - "type": "tarball", - "url": "https://api.flakehub.com/f/pinned/edolstra/flake-compat/1.0.1/018afb31-abd1-7bff-a5e4-cff7e18efb7a/source.tar.gz" - }, - "original": { - "type": "tarball", - "url": "https://flakehub.com/f/edolstra/flake-compat/1.tar.gz" - } - }, - "flake-compat_3": { - "locked": { - "lastModified": 1696426674, - "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", - "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", - "revCount": 57, - "type": "tarball", - "url": "https://api.flakehub.com/f/pinned/edolstra/flake-compat/1.0.1/018afb31-abd1-7bff-a5e4-cff7e18efb7a/source.tar.gz" - }, - "original": { - "type": "tarball", - "url": "https://flakehub.com/f/edolstra/flake-compat/1.tar.gz" - } - }, - "flake-compat_4": { - "locked": { - "lastModified": 1696426674, - "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", - "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", - "revCount": 57, - "type": "tarball", - "url": "https://api.flakehub.com/f/pinned/edolstra/flake-compat/1.0.1/018afb31-abd1-7bff-a5e4-cff7e18efb7a/source.tar.gz" - }, - "original": { - "type": "tarball", - "url": "https://flakehub.com/f/edolstra/flake-compat/1.tar.gz" - } - }, - "flake-compat_5": { - "flake": false, - "locked": { - "lastModified": 1696426674, - "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", - "owner": "edolstra", - "repo": "flake-compat", - "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", - "type": "github" - }, - "original": { - "owner": "edolstra", - "repo": "flake-compat", - "type": "github" - } - }, "flake-parts": { - "inputs": { - "nixpkgs-lib": [ - "sonnenshift", - "crate2nix", - "crate2nix_stable", - "crate2nix_stable", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1719745305, - "narHash": "sha256-xwgjVUpqSviudEkpQnioeez1Uo2wzrsMaJKJClh+Bls=", - "owner": "hercules-ci", - "repo": "flake-parts", - "rev": "c3c5ecc05edc7dafba779c6c1a61cd08ac6583e9", - "type": "github" - }, - "original": { - "owner": "hercules-ci", - "repo": "flake-parts", - "type": "github" - } - }, - "flake-parts_2": { - "inputs": { - "nixpkgs-lib": [ - "sonnenshift", - "crate2nix", - "crate2nix_stable", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1719745305, - "narHash": "sha256-xwgjVUpqSviudEkpQnioeez1Uo2wzrsMaJKJClh+Bls=", - "owner": "hercules-ci", - "repo": "flake-parts", - "rev": "c3c5ecc05edc7dafba779c6c1a61cd08ac6583e9", - "type": "github" - }, - "original": { - "owner": "hercules-ci", - "repo": "flake-parts", - "type": "github" - } - }, - "flake-parts_3": { "inputs": { "nixpkgs-lib": [ "sonnenshift", @@ -498,93 +209,23 @@ "type": "github" } }, - "flake-utils": { + "flint": { "inputs": { - "systems": "systems_2" + "nixpkgs": [ + "nixpkgs" + ] }, "locked": { - "lastModified": 1694529238, - "narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "ff7b65b44d01cf9ba6a71320833626af21126384", + "lastModified": 1762437643, + "narHash": "sha256-nQ2ItqrkvOYEjJr1HcXkIEFS4SEy5q1ax0Y1CTuKhHs=", + "owner": "NotAShelf", + "repo": "flint", + "rev": "36c565edd971166718d21ae973c792b194ca737d", "type": "github" }, "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, - "flake-utils_2": { - "inputs": { - "systems": "systems_3" - }, - "locked": { - "lastModified": 1701680307, - "narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "4022d587cbbfd70fe950c1e2083a02621806a725", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, - "flake-utils_3": { - "inputs": { - "systems": "systems_4" - }, - "locked": { - "lastModified": 1701680307, - "narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "4022d587cbbfd70fe950c1e2083a02621806a725", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, - "flake-utils_4": { - "inputs": { - "systems": "systems_5" - }, - "locked": { - "lastModified": 1701680307, - "narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "4022d587cbbfd70fe950c1e2083a02621806a725", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, - "flake-utils_5": { - "inputs": { - "systems": "systems_6" - }, - "locked": { - "lastModified": 1710146030, - "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", + "owner": "NotAShelf", + "repo": "flint", "type": "github" } }, @@ -608,61 +249,43 @@ "type": "github" } }, + "git-hooks": { + "inputs": { + "flake-compat": [ + "sonnenshift", + "crate2nix", + "cachix", + "flake-compat" + ], + "gitignore": "gitignore", + "nixpkgs": [ + "sonnenshift", + "crate2nix", + "cachix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1762868777, + "narHash": "sha256-QqS72GvguP56oKDNUckWUPNJHjsdeuXh5RyoKz0wJ+E=", + "owner": "cachix", + "repo": "git-hooks.nix", + "rev": "c5c3147730384576196fb5da048a6e45dee10d56", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "git-hooks.nix", + "type": "github" + } + }, "gitignore": { "inputs": { "nixpkgs": [ "sonnenshift", "crate2nix", - "crate2nix_stable", - "crate2nix_stable", - "pre-commit-hooks", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1709087332, - "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", - "owner": "hercules-ci", - "repo": "gitignore.nix", - "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", - "type": "github" - }, - "original": { - "owner": "hercules-ci", - "repo": "gitignore.nix", - "type": "github" - } - }, - "gitignore_2": { - "inputs": { - "nixpkgs": [ - "sonnenshift", - "crate2nix", - "crate2nix_stable", - "pre-commit-hooks", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1709087332, - "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", - "owner": "hercules-ci", - "repo": "gitignore.nix", - "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", - "type": "github" - }, - "original": { - "owner": "hercules-ci", - "repo": "gitignore.nix", - "type": "github" - } - }, - "gitignore_3": { - "inputs": { - "nixpkgs": [ - "sonnenshift", - "crate2nix", - "pre-commit-hooks", + "cachix", + "git-hooks", "nixpkgs" ] }, @@ -696,55 +319,7 @@ "type": "github" } }, - "nix-test-runner_2": { - "flake": false, - "locked": { - "lastModified": 1588761593, - "narHash": "sha256-FKJykltAN/g3eIceJl4SfDnnyuH2jHImhMrXS2KvGIs=", - "owner": "stoeffel", - "repo": "nix-test-runner", - "rev": "c45d45b11ecef3eb9d834c3b6304c05c49b06ca2", - "type": "github" - }, - "original": { - "owner": "stoeffel", - "repo": "nix-test-runner", - "type": "github" - } - }, - "nix-test-runner_3": { - "flake": false, - "locked": { - "lastModified": 1588761593, - "narHash": "sha256-FKJykltAN/g3eIceJl4SfDnnyuH2jHImhMrXS2KvGIs=", - "owner": "stoeffel", - "repo": "nix-test-runner", - "rev": "c45d45b11ecef3eb9d834c3b6304c05c49b06ca2", - "type": "github" - }, - "original": { - "owner": "stoeffel", - "repo": "nix-test-runner", - "type": "github" - } - }, "nixpkgs": { - "locked": { - "lastModified": 1743014863, - "narHash": "sha256-jAIUqsiN2r3hCuHji80U7NNEafpIMBXiwKlSrjWMlpg=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "bd3bac8bfb542dbde7ffffb6987a1a1f9d41699f", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixpkgs-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "nixpkgs_2": { "locked": { "lastModified": 1760782625, "narHash": "sha256-qeSmHF66cMiHObiBhm8IngmqDBEcqNdBSSoAjuE6tTw=", @@ -760,197 +335,32 @@ "type": "github" } }, - "nixpkgs_3": { - "locked": { - "lastModified": 1700612854, - "narHash": "sha256-yrQ8osMD+vDLGFX7pcwsY/Qr5PUd6OmDMYJZzZi0+zc=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "19cbff58383a4ae384dea4d1d0c823d72b49d614", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixos-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "nixpkgs_4": { - "locked": { - "lastModified": 1715534503, - "narHash": "sha256-5ZSVkFadZbFP1THataCaSf0JH2cAH3S29hU9rrxTEqk=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "2057814051972fa1453ddfb0d98badbea9b83c06", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixos-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "nixpkgs_5": { - "locked": { - "lastModified": 1715534503, - "narHash": "sha256-5ZSVkFadZbFP1THataCaSf0JH2cAH3S29hU9rrxTEqk=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "2057814051972fa1453ddfb0d98badbea9b83c06", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixos-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "nixpkgs_6": { - "locked": { - "lastModified": 1719506693, - "narHash": "sha256-C8e9S7RzshSdHB7L+v9I51af1gDM5unhJ2xO1ywxNH8=", - "path": "/nix/store/4p0avw1s3vf27hspgqsrqs37gxk4i83i-source", - "rev": "b2852eb9365c6de48ffb0dc2c9562591f652242a", - "type": "path" - }, - "original": { - "id": "nixpkgs", - "type": "indirect" - } - }, - "nixpkgs_7": { - "locked": { - "lastModified": 1719506693, - "narHash": "sha256-C8e9S7RzshSdHB7L+v9I51af1gDM5unhJ2xO1ywxNH8=", - "path": "/nix/store/4p0avw1s3vf27hspgqsrqs37gxk4i83i-source", - "rev": "b2852eb9365c6de48ffb0dc2c9562591f652242a", - "type": "path" - }, - "original": { - "id": "nixpkgs", - "type": "indirect" - } - }, - "nixpkgs_8": { - "locked": { - "lastModified": 1729850857, - "narHash": "sha256-WvLXzNNnnw+qpFOmgaM3JUlNEH+T4s22b5i2oyyCpXE=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "41dea55321e5a999b17033296ac05fe8a8b5a257", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixpkgs-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, "pre-commit-hooks": { "inputs": { "flake-compat": [ "sonnenshift", "crate2nix", - "crate2nix_stable", - "crate2nix_stable", "flake-compat" ], - "gitignore": "gitignore", + "gitignore": [ + "sonnenshift", + "crate2nix", + "cachix", + "git-hooks", + "gitignore" + ], "nixpkgs": [ - "sonnenshift", - "crate2nix", - "crate2nix_stable", - "crate2nix_stable", - "nixpkgs" - ], - "nixpkgs-stable": [ - "sonnenshift", - "crate2nix", - "crate2nix_stable", - "crate2nix_stable", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1719259945, - "narHash": "sha256-F1h+XIsGKT9TkGO3omxDLEb/9jOOsI6NnzsXFsZhry4=", - "owner": "cachix", - "repo": "pre-commit-hooks.nix", - "rev": "0ff4381bbb8f7a52ca4a851660fc7a437a4c6e07", - "type": "github" - }, - "original": { - "owner": "cachix", - "repo": "pre-commit-hooks.nix", - "type": "github" - } - }, - "pre-commit-hooks_2": { - "inputs": { - "flake-compat": [ - "sonnenshift", - "crate2nix", - "crate2nix_stable", - "flake-compat" - ], - "gitignore": "gitignore_2", - "nixpkgs": [ - "sonnenshift", - "crate2nix", - "crate2nix_stable", - "nixpkgs" - ], - "nixpkgs-stable": [ - "sonnenshift", - "crate2nix", - "crate2nix_stable", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1719259945, - "narHash": "sha256-F1h+XIsGKT9TkGO3omxDLEb/9jOOsI6NnzsXFsZhry4=", - "owner": "cachix", - "repo": "pre-commit-hooks.nix", - "rev": "0ff4381bbb8f7a52ca4a851660fc7a437a4c6e07", - "type": "github" - }, - "original": { - "owner": "cachix", - "repo": "pre-commit-hooks.nix", - "type": "github" - } - }, - "pre-commit-hooks_3": { - "inputs": { - "flake-compat": [ - "sonnenshift", - "crate2nix", - "flake-compat" - ], - "flake-utils": "flake-utils_5", - "gitignore": "gitignore_3", - "nixpkgs": [ - "sonnenshift", - "crate2nix", - "nixpkgs" - ], - "nixpkgs-stable": [ "sonnenshift", "crate2nix", "nixpkgs" ] }, "locked": { - "lastModified": 1712055707, - "narHash": "sha256-4XLvuSIDZJGS17xEwSrNuJLL7UjDYKGJSbK1WWX2AK8=", + "lastModified": 1762868777, + "narHash": "sha256-QqS72GvguP56oKDNUckWUPNJHjsdeuXh5RyoKz0wJ+E=", "owner": "cachix", "repo": "pre-commit-hooks.nix", - "rev": "e35aed5fda3cc79f88ed7f1795021e559582093a", + "rev": "c5c3147730384576196fb5da048a6e45dee10d56", "type": "github" }, "original": { @@ -961,7 +371,10 @@ }, "purescript-overlay": { "inputs": { - "flake-compat": "flake-compat_5", + "flake-compat": [ + "deploy-rs", + "flake-compat" + ], "nixpkgs": [ "tlaternet-webserver", "dream2nix", @@ -970,11 +383,11 @@ "slimlock": "slimlock" }, "locked": { - "lastModified": 1728546539, - "narHash": "sha256-Sws7w0tlnjD+Bjck1nv29NjC5DbL6nH5auL9Ex9Iz2A=", + "lastModified": 1750754571, + "narHash": "sha256-qquerCyD6WhVccAV0qI7NFGwSN7rXslhogPrTaFvWto=", "owner": "thomashoneyman", "repo": "purescript-overlay", - "rev": "4ad4c15d07bd899d7346b331f377606631eb0ee4", + "rev": "81513816356c43c6ef604abdf5da41834306a2d5", "type": "github" }, "original": { @@ -984,18 +397,23 @@ } }, "pyproject-nix": { - "flake": false, + "inputs": { + "nixpkgs": [ + "tlaternet-webserver", + "dream2nix", + "nixpkgs" + ] + }, "locked": { - "lastModified": 1702448246, - "narHash": "sha256-hFg5s/hoJFv7tDpiGvEvXP0UfFvFEDgTdyHIjDVHu1I=", - "owner": "davhau", + "lastModified": 1762427963, + "narHash": "sha256-CkPlAbIQ87wmjy5qHibfzk4DmMGBNqFer+lLfXjpP5M=", + "owner": "pyproject-nix", "repo": "pyproject.nix", - "rev": "5a06a2697b228c04dd2f35659b4b659ca74f7aeb", + "rev": "4540ea004e04fcd12dd2738d51383d10f956f7b9", "type": "github" }, "original": { - "owner": "davhau", - "ref": "dream2nix", + "owner": "pyproject-nix", "repo": "pyproject.nix", "type": "github" } @@ -1004,8 +422,9 @@ "inputs": { "deploy-rs": "deploy-rs", "disko": "disko", + "flint": "flint", "foundryvtt": "foundryvtt", - "nixpkgs": "nixpkgs_2", + "nixpkgs": "nixpkgs", "sonnenshift": "sonnenshift", "sops-nix": "sops-nix", "tlaternet-webserver": "tlaternet-webserver" @@ -1107,81 +526,6 @@ "type": "github" } }, - "systems_2": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } - }, - "systems_3": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } - }, - "systems_4": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } - }, - "systems_5": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } - }, - "systems_6": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } - }, "tlaternet-webserver": { "inputs": { "dream2nix": "dream2nix", diff --git a/flake.nix b/flake.nix index 76d612f..386de98 100644 --- a/flake.nix +++ b/flake.nix @@ -3,19 +3,44 @@ inputs = { nixpkgs.url = "github:nixos/nixpkgs/nixos-25.05-small"; + + ## Nix/OS utilities + disko = { url = "github:nix-community/disko"; inputs.nixpkgs.follows = "nixpkgs"; }; - deploy-rs.url = "github:serokell/deploy-rs"; + + deploy-rs = { + url = "github:serokell/deploy-rs"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + sops-nix = { url = "github:Mic92/sops-nix"; inputs.nixpkgs.follows = "nixpkgs"; }; - tlaternet-webserver = { - url = "git+https://gitea.tlater.net/tlaternet/tlaternet.git"; + + ## Programs + + flint = { + url = "github:NotAShelf/flint"; inputs.nixpkgs.follows = "nixpkgs"; }; + + ## Services + + tlaternet-webserver = { + url = "git+https://gitea.tlater.net/tlaternet/tlaternet.git"; + inputs = { + nixpkgs.follows = "nixpkgs"; + dream2nix.inputs = { + nixpkgs.follows = "nixpkgs"; + purescript-overlay.inputs.flake-compat.follows = "deploy-rs/flake-compat"; + }; + }; + }; + foundryvtt = { url = "github:reckenrode/nix-foundryvtt"; inputs.nixpkgs.follows = "nixpkgs"; @@ -23,7 +48,20 @@ sonnenshift = { url = "git+ssh://git@github.com/sonnenshift/battery-manager"; - inputs.nixpkgs.follows = "nixpkgs"; + inputs = { + nixpkgs.follows = "nixpkgs"; + + crate2nix.inputs = { + nixpkgs.follows = "nixpkgs"; + flake-compat.follows = "deploy-rs/flake-compat"; + cachix.inputs.nixpkgs.follows = "nixpkgs"; + pre-commit-hooks.inputs.gitignore.follows = "sonnenshift/crate2nix/cachix/git-hooks/gitignore"; + + # Yes, they do this insanity: + # https://github.com/nix-community/crate2nix/issues/371 + crate2nix_stable.follows = "sonnenshift/crate2nix"; + }; + }; }; }; @@ -90,7 +128,7 @@ ######### # Tests # ######### - checks.${system} = import ./checks (inputs // { inherit system; }); + checks = import ./checks { flake-inputs = inputs; }; ########################### # Garbage collection root # From d82c3533294e405f31d3464baa981563760a3c58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tristan=20Dani=C3=ABl=20Maat?= Date: Thu, 13 Nov 2025 05:16:12 +0800 Subject: [PATCH 03/24] fix(vm): Set disk size declaratively --- configuration/hardware-specific/vm.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/configuration/hardware-specific/vm.nix b/configuration/hardware-specific/vm.nix index 71870fb..aa360aa 100644 --- a/configuration/hardware-specific/vm.nix +++ b/configuration/hardware-specific/vm.nix @@ -48,6 +48,7 @@ memorySize = 3941; cores = 2; graphics = false; + diskSize = 1024 * 20; }; virtualisation.qemu = { From 767a14ab6e0c54d5e8ca98d059583e8ff06a47db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tristan=20Dani=C3=ABl=20Maat?= Date: Thu, 13 Nov 2025 04:07:10 +0800 Subject: [PATCH 04/24] refactor(nginx): Clean up nginx configuration --- configuration/default.nix | 2 +- configuration/nginx.nix | 77 ------------------ configuration/nginx/default.nix | 22 +++++ configuration/nginx/logging.nix | 84 ++++++++++++++++++++ configuration/nginx/ssl.nix | 68 ++++++++++++++++ configuration/services/metrics/exporters.nix | 29 +------ modules/default.nix | 7 +- modules/nginxExtensions.nix | 59 -------------- 8 files changed, 177 insertions(+), 171 deletions(-) delete mode 100644 configuration/nginx.nix create mode 100644 configuration/nginx/default.nix create mode 100644 configuration/nginx/logging.nix create mode 100644 configuration/nginx/ssl.nix delete mode 100644 modules/nginxExtensions.nix diff --git a/configuration/default.nix b/configuration/default.nix index f2d1615..54f17c2 100644 --- a/configuration/default.nix +++ b/configuration/default.nix @@ -27,7 +27,7 @@ ./services/wireguard.nix # ./services/starbound.nix -- Not currently used ./services/postgres.nix - ./nginx.nix + ./nginx ./sops.nix ]; diff --git a/configuration/nginx.nix b/configuration/nginx.nix deleted file mode 100644 index 935b5ac..0000000 --- a/configuration/nginx.nix +++ /dev/null @@ -1,77 +0,0 @@ -{ config, lib, ... }: -{ - services = { - nginx = { - enable = true; - recommendedTlsSettings = true; - recommendedOptimisation = true; - recommendedGzipSettings = true; - recommendedProxySettings = true; - clientMaxBodySize = "10G"; - - statusPage = true; # For metrics, should be accessible only from localhost - - commonHttpConfig = '' - log_format upstream_time '$remote_addr - $remote_user [$time_local] ' - '"$request" $status $body_bytes_sent ' - '"$http_referer" "$http_user_agent" ' - 'rt=$request_time uct="$upstream_connect_time" ' - 'uht="$upstream_header_time" urt="$upstream_response_time"'; - ''; - }; - - logrotate.settings = { - # Override the default, just keep fewer logs - nginx.rotate = 6; - } - // lib.mapAttrs' ( - virtualHost: _: - lib.nameValuePair "/var/log/nginx/${virtualHost}/access.log" { - frequency = "daily"; - rotate = 2; - compress = true; - delaycompress = true; - su = "${config.services.nginx.user} ${config.services.nginx.group}"; - postrotate = "[ ! -f /var/run/nginx/nginx.pid ] || kill -USR1 `cat /var/run/nginx/nginx.pid`"; - } - ) config.services.nginx.virtualHosts; - - backups.acme = { - user = "acme"; - paths = lib.mapAttrsToList ( - virtualHost: _: "/var/lib/acme/${virtualHost}" - ) config.services.nginx.virtualHosts; - }; - }; - - systemd.tmpfiles.rules = lib.mapAttrsToList ( - virtualHost: _: - # - "d /var/log/nginx/${virtualHost} 0750 ${config.services.nginx.user} ${config.services.nginx.group}" - ) config.services.nginx.virtualHosts; - - security.acme = { - defaults.email = "tm@tlater.net"; - acceptTerms = true; - - certs."tlater.net" = { - extraDomainNames = [ - "*.tlater.net" - "tlater.com" - "*.tlater.com" - ]; - dnsProvider = "porkbun"; - group = "ssl-cert"; - credentialFiles = { - PORKBUN_API_KEY_FILE = config.sops.secrets."porkbun/api-key".path; - PORKBUN_SECRET_API_KEY_FILE = config.sops.secrets."porkbun/secret-api-key".path; - }; - }; - }; - - users.groups.ssl-cert = { }; - - systemd.services.nginx.serviceConfig.SupplementaryGroups = [ - config.security.acme.certs."tlater.net".group - ]; -} diff --git a/configuration/nginx/default.nix b/configuration/nginx/default.nix new file mode 100644 index 0000000..dab0259 --- /dev/null +++ b/configuration/nginx/default.nix @@ -0,0 +1,22 @@ +{ lib, ... }: +{ + imports = [ + ./logging.nix + ./ssl.nix + ]; + + options.services.nginx.domain = lib.mkOption { + type = lib.types.str; + description = "The base domain name to append to virtual domain names"; + }; + + config.services.nginx = { + enable = true; + recommendedTlsSettings = true; + recommendedOptimisation = true; + recommendedGzipSettings = true; + recommendedProxySettings = true; + clientMaxBodySize = "10G"; + statusPage = true; # For metrics, should be accessible only from localhost + }; +} diff --git a/configuration/nginx/logging.nix b/configuration/nginx/logging.nix new file mode 100644 index 0000000..0c6a955 --- /dev/null +++ b/configuration/nginx/logging.nix @@ -0,0 +1,84 @@ +{ config, lib, ... }: +let + hostNames = lib.attrNames config.services.nginx.virtualHosts; + logPath = name: "/var/log/nginx/${name}/access.log"; + logFormat = lib.concatStringsSep " " [ + "$remote_addr - $remote_user [$time_local]" + ''"$request" $status $body_bytes_sent'' + ''"$http_referer" "$http_user_agent"'' + ''rt=$request_time uct="$upstream_connect_time"'' + ''uht="$upstream_header_time" urt="$upstream_response_time"'' + ]; +in +{ + # Extend the default configuration for nginx virtual hosts; we'd + # like to create log files for each of them, so that the prometheus + # nginxlog exporter can process per-host logs. + options.services.nginx.virtualHosts = lib.mkOption { + type = lib.types.attrsOf ( + lib.types.submodule ( + { name, ... }: + { + config.extraConfig = '' + access_log ${logPath name} upstream_time; + ''; + } + ) + ); + }; + + config = { + # Create directories for host-specific logs with systemd tmpfiles + systemd.tmpfiles.settings."10-nginx-logs" = lib.listToAttrs ( + map ( + name: + lib.nameValuePair "/var/log/nginx/${name}" { + d = { + inherit (config.services.nginx) user group; + mode = "0750"; + }; + } + ) hostNames + ); + + services = { + # Set the nginx-wide log format + nginx.commonHttpConfig = '' + log_format upstream_time '${logFormat}'; + ''; + + # Set up nginxlog to read the file and log format defined above + # for each virtual host + prometheus.exporters.nginxlog = { + enable = true; + listenAddress = "127.0.0.1"; + group = "nginx"; + + settings.namespaces = map (name: { + inherit name; + metrics_override.prefix = "nginxlog"; + namespace_label = "vhost"; + format = logFormat; + source.files = [ (logPath name) ]; + }) hostNames; + }; + + logrotate.settings = { + # Override the nginx module default, just keep fewer logs + nginx.rotate = 6; + + # Configure logrotate for host-specific logs + nginxVirtualHosts = { + files = map logPath hostNames; + + frequency = "daily"; + rotate = 2; + compress = true; + delaycompress = true; + su = "${config.services.nginx.user} ${config.services.nginx.group}"; + postrotate = "[ ! -f /var/run/nginx/nginx.pid ] || kill -USR1 `cat /var/run/nginx/nginx.pid`"; + }; + }; + }; + }; +} diff --git a/configuration/nginx/ssl.nix b/configuration/nginx/ssl.nix new file mode 100644 index 0000000..4cea508 --- /dev/null +++ b/configuration/nginx/ssl.nix @@ -0,0 +1,68 @@ +{ + pkgs, + config, + lib, + ... +}: +{ + options = { + # Add a custom per-host option to enable HSTS + services.nginx.virtualHosts = lib.mkOption { + type = lib.types.attrsOf ( + lib.types.submodule ( + { config, ... }: + { + options.enableHSTS = lib.mkEnableOption "HSTS"; + config.extraConfig = lib.mkIf config.enableHSTS '' + add_header Strict-Transport-Security "max-age=15552000; includeSubDomains" always; + ''; + } + ) + ); + }; + }; + + config = { + # Certificate settings + security.acme = { + defaults.email = "tm@tlater.net"; + acceptTerms = true; + + certs."tlater.net" = { + extraDomainNames = [ + "*.tlater.net" + "tlater.com" + "*.tlater.com" + ]; + dnsProvider = "porkbun"; + group = config.users.groups.ssl-cert.name; + credentialFiles = { + PORKBUN_API_KEY_FILE = config.sops.secrets."porkbun/api-key".path; + PORKBUN_SECRET_API_KEY_FILE = config.sops.secrets."porkbun/secret-api-key".path; + }; + }; + }; + users.groups.ssl-cert = { }; + + # Back up the SSL certificate, just in case + services.backups.acme = { + user = "acme"; + paths = [ "/var/lib/acme/tlater.net" ]; + }; + + systemd.services = { + nginx.serviceConfig.SupplementaryGroups = [ config.security.acme.certs."tlater.net".group ]; + + # Don't attempt to retrieve a certificate if the domain name + # doesn't *actually* match the cert name + # + # TODO(tlater): Set up pebble to retrieve certs "properly" + # instead + "acme-tlater.net".serviceConfig.ExecCondition = + let + confirm = ''[[ "tlater.net" = "${config.services.nginx.domain}" ]]''; + in + ''${pkgs.runtimeShell} -c '${confirm}' ''; + }; + }; +} diff --git a/configuration/services/metrics/exporters.nix b/configuration/services/metrics/exporters.nix index 52c2a46..ea57c9b 100644 --- a/configuration/services/metrics/exporters.nix +++ b/configuration/services/metrics/exporters.nix @@ -1,9 +1,4 @@ -{ - config, - pkgs, - lib, - ... -}: +{ pkgs, ... }: let yaml = pkgs.formats.yaml { }; in @@ -68,28 +63,6 @@ in enable = true; listenAddress = "127.0.0.1"; }; - - nginxlog = { - enable = true; - listenAddress = "127.0.0.1"; - group = "nginx"; - - settings.namespaces = lib.mapAttrsToList (name: _: { - inherit name; - metrics_override.prefix = "nginxlog"; - namespace_label = "vhost"; - - format = lib.concatStringsSep " " [ - "$remote_addr - $remote_user [$time_local]" - ''"$request" $status $body_bytes_sent'' - ''"$http_referer" "$http_user_agent"'' - ''rt=$request_time uct="$upstream_connect_time"'' - ''uht="$upstream_header_time" urt="$upstream_response_time"'' - ]; - - source.files = [ "/var/log/nginx/${name}/access.log" ]; - }) config.services.nginx.virtualHosts; - }; }; # TODO(tlater): diff --git a/modules/default.nix b/modules/default.nix index 89f1752..9483c66 100644 --- a/modules/default.nix +++ b/modules/default.nix @@ -1,6 +1 @@ -{ - imports = [ - ./crowdsec - ./nginxExtensions.nix - ]; -} +{ imports = [ ./crowdsec ]; } diff --git a/modules/nginxExtensions.nix b/modules/nginxExtensions.nix deleted file mode 100644 index bd505d3..0000000 --- a/modules/nginxExtensions.nix +++ /dev/null @@ -1,59 +0,0 @@ -{ - config, - pkgs, - lib, - ... -}: -{ - options = { - services.nginx.domain = lib.mkOption { - type = lib.types.str; - description = "The base domain name to append to virtual domain names"; - }; - - services.nginx.virtualHosts = - let - extraVirtualHostOptions = - { name, config, ... }: - { - options = { - enableHSTS = lib.mkEnableOption "Enable HSTS"; - - addAccessLog = lib.mkOption { - type = lib.types.bool; - default = true; - description = '' - Add special logging to `/var/log/nginx/''${serverName}` - ''; - }; - }; - - config = { - extraConfig = lib.concatStringsSep "\n" [ - (lib.optionalString config.enableHSTS '' - add_header Strict-Transport-Security "max-age=15552000; includeSubDomains" always; - '') - (lib.optionalString config.addAccessLog '' - access_log /var/log/nginx/${name}/access.log upstream_time; - '') - ]; - }; - }; - in - lib.mkOption { type = lib.types.attrsOf (lib.types.submodule extraVirtualHostOptions); }; - }; - - config = { - # Don't attempt to run acme if the domain name is not tlater.net - systemd.services = - let - confirm = ''[[ "tlater.net" = ${config.services.nginx.domain} ]]''; - in - lib.mapAttrs' ( - cert: _: - lib.nameValuePair "acme-${cert}" { - serviceConfig.ExecCondition = ''${pkgs.runtimeShell} -c '${confirm}' ''; - } - ) config.security.acme.certs; - }; -} From 7fcaa34b28c617ba25caeddaad29d44e1d84d6d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tristan=20Dani=C3=ABl=20Maat?= Date: Fri, 14 Nov 2025 10:54:51 +0800 Subject: [PATCH 05/24] refactor(sops): Move secret definitions to specific modules --- configuration/default.nix | 2 +- configuration/nginx/ssl.nix | 5 ++ configuration/services/backups.nix | 13 +++ configuration/services/battery-manager.nix | 5 ++ configuration/services/conduit/default.nix | 7 ++ .../services/conduit/heisenbridge.nix | 6 ++ .../services/conduit/matrix-hookshot.nix | 6 ++ configuration/services/metrics/grafana.nix | 11 +++ .../services/metrics/victoriametrics.nix | 8 +- configuration/services/nextcloud.nix | 5 ++ configuration/services/starbound.nix | 3 + configuration/services/wireguard.nix | 6 ++ configuration/sops.nix | 89 ------------------- keys/production.yaml | 10 +-- keys/staging.yaml | 10 +-- 15 files changed, 81 insertions(+), 105 deletions(-) delete mode 100644 configuration/sops.nix diff --git a/configuration/default.nix b/configuration/default.nix index 54f17c2..631e93e 100644 --- a/configuration/default.nix +++ b/configuration/default.nix @@ -28,7 +28,6 @@ # ./services/starbound.nix -- Not currently used ./services/postgres.nix ./nginx - ./sops.nix ]; nixpkgs.overlays = [ (_: prev: { local = import ../pkgs { pkgs = prev; }; }) ]; @@ -124,6 +123,7 @@ services.sudo.rssh = true; }; }; + sops.defaultSopsFile = ../keys/production.yaml; # Remove some unneeded packages environment.defaultPackages = [ ]; diff --git a/configuration/nginx/ssl.nix b/configuration/nginx/ssl.nix index 4cea508..7abc38e 100644 --- a/configuration/nginx/ssl.nix +++ b/configuration/nginx/ssl.nix @@ -64,5 +64,10 @@ in ''${pkgs.runtimeShell} -c '${confirm}' ''; }; + + sops.secrets = { + "porkbun/api-key".owner = "acme"; + "porkbun/secret-api-key".owner = "acme"; + }; }; } diff --git a/configuration/services/backups.nix b/configuration/services/backups.nix index 688f5f9..0ae8abf 100644 --- a/configuration/services/backups.nix +++ b/configuration/services/backups.nix @@ -265,5 +265,18 @@ in }; groups.backup = { }; }; + + sops.secrets = { + "restic/storagebox-backups" = { + owner = "root"; + group = "backup"; + mode = "0440"; + }; + "restic/storagebox-ssh-key" = { + owner = "backup"; + group = "backup"; + mode = "0040"; + }; + }; }; } diff --git a/configuration/services/battery-manager.nix b/configuration/services/battery-manager.nix index 9da7e32..0c58c7b 100644 --- a/configuration/services/battery-manager.nix +++ b/configuration/services/battery-manager.nix @@ -13,4 +13,9 @@ log_level = "DEBUG"; }; }; + + sops.secrets = { + "battery-manager/email" = { }; + "battery-manager/password" = { }; + }; } diff --git a/configuration/services/conduit/default.nix b/configuration/services/conduit/default.nix index 4ba5271..6e01e81 100644 --- a/configuration/services/conduit/default.nix +++ b/configuration/services/conduit/default.nix @@ -179,4 +179,11 @@ in systemd.services.coturn.serviceConfig.SupplementaryGroups = [ config.security.acme.certs."tlater.net".group ]; + + sops.secrets = { + "turn/env" = { }; + "turn/secret" = { + owner = "turnserver"; + }; + }; } diff --git a/configuration/services/conduit/heisenbridge.nix b/configuration/services/conduit/heisenbridge.nix index f0f7e49..5441639 100644 --- a/configuration/services/conduit/heisenbridge.nix +++ b/configuration/services/conduit/heisenbridge.nix @@ -75,4 +75,10 @@ in # AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ]; }; }; + + sops.secrets = { + # Accessed via systemd cred through /run/secrets/heisebridge + "heisenbridge/as-token" = { }; + "heisenbridge/hs-token" = { }; + }; } diff --git a/configuration/services/conduit/matrix-hookshot.nix b/configuration/services/conduit/matrix-hookshot.nix index 6b788b2..c1fec82 100644 --- a/configuration/services/conduit/matrix-hookshot.nix +++ b/configuration/services/conduit/matrix-hookshot.nix @@ -163,4 +163,10 @@ in metrics.enabled = true; }; }; + + sops.secrets = { + # Accessed via systemd cred through /run/secrets/matrix-hookshot + "matrix-hookshot/as-token" = { }; + "matrix-hookshot/hs-token" = { }; + }; } diff --git a/configuration/services/metrics/grafana.nix b/configuration/services/metrics/grafana.nix index b30806c..f4b6956 100644 --- a/configuration/services/metrics/grafana.nix +++ b/configuration/services/metrics/grafana.nix @@ -67,4 +67,15 @@ in }; }; }; + + sops.secrets = { + "grafana/adminPassword" = { + owner = "grafana"; + group = "grafana"; + }; + "grafana/secretKey" = { + owner = "grafana"; + group = "grafana"; + }; + }; } diff --git a/configuration/services/metrics/victoriametrics.nix b/configuration/services/metrics/victoriametrics.nix index f37b8b0..96e09e5 100644 --- a/configuration/services/metrics/victoriametrics.nix +++ b/configuration/services/metrics/victoriametrics.nix @@ -4,7 +4,7 @@ let blackbox_port = config.services.prometheus.exporters.blackbox.port; in { - config.services.victoriametrics = { + services.victoriametrics = { enable = true; extraOptions = [ "-storage.minFreeDiskSpaceBytes=5GB" ]; @@ -96,4 +96,10 @@ in victorialogs.targets = [ config.services.victorialogs.bindAddress ]; }; }; + + sops.secrets."forgejo/metrics-token" = { + owner = "forgejo"; + group = "metrics"; + mode = "0440"; + }; } diff --git a/configuration/services/nextcloud.nix b/configuration/services/nextcloud.nix index 4af77a9..ef2a6ac 100644 --- a/configuration/services/nextcloud.nix +++ b/configuration/services/nextcloud.nix @@ -100,4 +100,9 @@ in # Ensure that this service doesn't start before postgres is ready systemd.services.nextcloud-setup.after = [ "postgresql.service" ]; + + sops.secrets."nextcloud/tlater" = { + owner = "nextcloud"; + group = "nextcloud"; + }; } diff --git a/configuration/services/starbound.nix b/configuration/services/starbound.nix index f5b23c3..609d4c0 100644 --- a/configuration/services/starbound.nix +++ b/configuration/services/starbound.nix @@ -114,4 +114,7 @@ in paths = [ "/var/lib/private/starbound/storage/universe/" ]; pauseServices = [ "starbound.service" ]; }; + + # Accessed via systemd cred through /run/secrets/steam + sops.secrets."steam/tlater" = { }; } diff --git a/configuration/services/wireguard.nix b/configuration/services/wireguard.nix index a6b7763..d4ab090 100644 --- a/configuration/services/wireguard.nix +++ b/configuration/services/wireguard.nix @@ -62,4 +62,10 @@ }; }; }; + + sops.secrets."wireguard/server-key" = { + owner = "root"; + group = "systemd-network"; + mode = "0440"; + }; } diff --git a/configuration/sops.nix b/configuration/sops.nix deleted file mode 100644 index 0337438..0000000 --- a/configuration/sops.nix +++ /dev/null @@ -1,89 +0,0 @@ -{ - sops = { - defaultSopsFile = ../keys/production.yaml; - - secrets = { - "battery-manager/email" = { }; - - "battery-manager/password" = { }; - - # Gitea - "forgejo/metrics-token" = { - owner = "forgejo"; - group = "metrics"; - mode = "0440"; - }; - - # Grafana - "grafana/adminPassword" = { - owner = "grafana"; - group = "grafana"; - }; - "grafana/secretKey" = { - owner = "grafana"; - group = "grafana"; - }; - - # Heisenbridge - "heisenbridge/as-token" = { }; - "heisenbridge/hs-token" = { }; - - # Matrix-hookshot - "matrix-hookshot/as-token" = { }; - "matrix-hookshot/hs-token" = { }; - - # Nextcloud - "nextcloud/tlater" = { - owner = "nextcloud"; - group = "nextcloud"; - }; - - # Porkbub/ACME - "porkbun/api-key" = { - owner = "acme"; - }; - "porkbun/secret-api-key" = { - owner = "acme"; - }; - - # Restic - "restic/local-backups" = { - owner = "root"; - group = "backup"; - mode = "0440"; - }; - "restic/storagebox-backups" = { - owner = "root"; - group = "backup"; - mode = "0440"; - }; - "restic/storagebox-ssh-key" = { - owner = "backup"; - group = "backup"; - mode = "0040"; - }; - - # Steam - "steam/tlater" = { }; - - # Turn - "turn/env" = { }; - "turn/secret" = { - owner = "turnserver"; - }; - "turn/ssl-key" = { - owner = "turnserver"; - }; - "turn/ssl-cert" = { - owner = "turnserver"; - }; - - # Wireguard - "wireguard/server-key" = { - owner = "root"; - group = "systemd-network"; - mode = "0440"; - }; - }; - }; -} diff --git a/keys/production.yaml b/keys/production.yaml index a5a4674..a686ed3 100644 --- a/keys/production.yaml +++ b/keys/production.yaml @@ -22,18 +22,14 @@ matrix-hookshot: wireguard: server-key: ENC[AES256_GCM,data:mXb7ZznJHf5CgV8rI4uzPBATMRbmd7LimgtCkQM9kAjbIaGwUBqJZBN3fXs=,iv:3Po1Orinzov9rnEm9cLzgJY1PeD+5Jl9115MriABHh8=,tag:E/2CjDO1JCvJzxCnqKcNyw==,type:str] restic: - local-backups: ENC[AES256_GCM,data:NLNVlR9G9bLSZOkMoPvkbBbAZlKkmiUbdWHOFDnaefuy9wNLH53ctOIyS0rSsQLaJCSBTpgPSWIIXUSuzoK/eA==,iv:DzuujmyJJP4GiE5z7KOOGUEzUgOwmtf/7UYhwkyLe9g=,tag:cElFhpVC7S6HYlB6UyN7PQ==,type:str] storagebox-backups: ENC[AES256_GCM,data:UyT8jCkKlfYJXjWLI9MbYfeVhY5d89N3aj1Olj54/aBOP3gwcrx6gU56Pwa1xKZ3lR13AVs/b4wF9sbvP7Kqqg==,iv:0HM+DgH4iCiWpjRvAYCFQGEy4xIBQwAM+PkkzOsizw0=,tag:jbrqo1In2O4jVM5e7fjOzg==,type:str] storagebox-ssh-key: ENC[AES256_GCM,data:7aYlKX7I8Bsur3nm4nV9eSW3lmIxBCeCUMbPX3qgcotPbyPYaUqD3MOCnFRepajYkFXAgMX4jknqLfoO9xYc4bavDFjOY8Ww/KmLay7ces6tDnkK6tTRxcNRPUBqEzaFiPNsZc2UwHnmHOF0rKvQusvhCOplYao3xxz5McTHC7IEriUApSNudWCg3qGbyAmxkGEw7tRfh6IiUXEOFeaXDZd78dWZlWIIeospmA1hcVhkLGrjMmoikt/YANHUpWPbd+B9E6x+s2eIzFdvztRjarBluWPZuX981b+hcOm1/+HY/tJ/jzgyVbX1rjmdgZ9jZqdKO/vkOkijHWXlwpQ0QJ2s8p5MURPGRsC7W5jfmGbVKrubxfQC2mSJRJgBaj1wX3yI4GbfCXNdbpseAMy7t8OmN/iMN57lGnD3uX8CWWD327PIWp3SgwMDIZtJRlMBu3vMUrBdNnrrlYoLgf821tX7JWW6L5g1EK/bcBZqZZ/6rE+Q9fLiJHTsj2lyTzQZRLKsn0YePlcIMOWHO2CG/aWrfycdSKKjaKGG,iv:OVnEIMFB4h/EQ8zV3XOpVXLDrV5t4roNYDFQz99m4sQ=,tag:mKWF12uD1TLla/MoJs2zNg==,type:str] turn: env: ENC[AES256_GCM,data:kt5nhVo9pb/ZbPUEcqSYXxN9YMgQKnFb5VRfFFS/qoIaJ73uD2fuJKqcxAyVRrdLqnSAWSQBgTgunBzdP7xqLAK2qt8DYAQWHkIe9uxFbSXZpdmw,iv:9lq6SFwTFN4GGm6gPiJpUMasMdnHVF6XLGYrsyG3kjU=,tag:428Qf9DOiiHt/Wjb188b8g==,type:str] secret: ENC[AES256_GCM,data:si7ee6Xfhdgdyzbp6aQpF7pz3TmTBb7iQ82lRPVXNDg9JfHI+lbmgAsSnRLX5qMCA6P9R045sSMosqidL8QwRg==,iv:SrhpZKK8D45yxCEfDb9P3TwtA14+qEI+wcRqcN/a6pw=,tag:PiwV+mOL9xHJgJft6sc61g==,type:str] - ssl-key: ENC[AES256_GCM,data:CHFH8XTcyAkIDwclwUpAyDkvP0awoW7mtZ2PgR81DyQZrKIAjuhox7x+8Gl9vfjdug2WLISycwoCJ2w+Y6Qpuk+XgXOmZeBU4UyMX5sfwGYAt0fjyQPix3Xni3cfDPQ3bJ9Ez0Nn6pxmKvSUY2nCOPJe8gK5+kaMt3d2SWS1bteH/rOUXSx3hczdQ54M8kScTUHceG7G65rfrmpTIVS1GNRYm7N8F8uXToP3qdTJBF03qSfdbAvWw5RhkNQoF35Hc8HT2W5hYmUb6ceuo4b3tCyTVDGzNtqn760+VSqXcJ5acc4O22cH/TqeF7VSS7akUqZzYot92riqSL0lHbET6VlXuYdidNIgW5uoUgtJLj9KRkYljOHkP1H90acWTW0+T3jS9cesVBVDWCHaYt2rFKwQgViNvgU27f1u9PT4wGKyqQMnLE5iLdmUT4qAwNw8ErXnpIOlXDizZ0sYXtNEmlNGdOk3jrjW3C1TGBw77PmWzHHRBrRNMO/6s3QWuRgTdQixF/UzTvyRf5yjt7Cev8fDm16FL8c+2uduDTkxTKwEVrt6U1wsJMqS52u7jAj5xtpmWSGJRqxc/bu9l+fcRDrZt1EaktxKCc3zSFp8Mv+LwMdj6dXcYoFQZmh5N2rySmxdCeHT+Me8htReesVxDPG5/pIZJ7n5DYK60V84KnDaqiw0DM0XEY6Y9UJ0Vx6qvk8lVouQaPbxEnfwc3775jVojEA/nCBd7LiyRfUEILtSgm+IjhIvVjDr3qzzKo7Ao66trEN6Sb7UtYo11rUyGfKqOwAZTsb/bXk6pYWo/qhzwx9EfxCrrMt06mXjN7JGzIZeBlV0azHEw1jt/CJ2lPFSUuTWIjjtTfZzpx6jYBJmTN9rAMbKHMknO3hEKWwpzYG/w7TLO2JKkXrOdT58IdvfxOKwaQ/kJRycYvuWcF8NqdcRhbZvrVaKHRWhWldhWkOj5a+G0DnFsgak6D1FOvDx0doN8hibFemn1MfLzk6/UJ5AxAbPCRSaEumkMZ6n/Xx5M+C7TswWrYVkrIeRxSU6+B24ZmLeVvfvNq6jfBBgk3MBXHU6bwr/nsfcQ0wlJwYCc/YVP02d/oAhN/MobUSKY+bG+ZirmyRb3MZ5C5ZjLY60wcSSWpty7jPzMUu0AoJVs6CA04frw9FqmW+Y0f3apFfaPI8OSOqT8UUURORjWK0j35v9nrpoDGTqLdq1d0oVQW3vxQqE6gmyPv4/ZqPgdUZhZ6BYiDc9L79c4misxmxsy6pZBxwb0vY34PyCwpamTWZC8u7JxRoXYu1l/UTdz9Oj2z1smaouy7yNKMK/BitF4uv3sKi0lWOpE6oTtqNtEoC265RUot3CGjaI3aZnwgku8jztvSn0JVwQYZyWZx7eFRh1acNIt3zRP8Pc4SL+XCm8RSufH2+j1kF0rbhCGTngDaLWdz/1FIWTizj3sjLnMEQYYw02QE1W+b9TJAMmgdh6LFhjrIFXtQFQtNSgH6edh8nreFWnpDvI8nyT9w/cCfJhDZhEvOYIDOVlc0mPH2k0yNfpA4x0DOvBNZJx9Gu0CcyOLRZGlyAwlDTIqBYLLV7bqwTsOdfOYBE581T2r+o9w/tgRF4EORM29U1KjGjSl2/XBXQVZHbRnXBM5l7kypyJfHD7X5DUp2hkuSdmHgBI5psnZXSjHh95Nn5YjFAAPDapqdIJO2AJhXkaY57mSUFjLrI9DRFU8pcqeY33si4i1QOqsRdkfh72+vCwsj4gFteV0Sh6N2Tp92+NkY0to3hk6Za9O6ESzqCiokcf9DaCyWv4ELd6Wed2DVVfrPXLABF5hnfzYw/tvtpXMFOOofKyOYY4KT5FRVUGlsst/x0F9wr2hYJ1qwTIRpp9wEWjiecO+wBeuNbLHv727pMCPcLX7YEoa/fcQuesuEAXQDKFTVJmBAHk1J5GpOu0R5F6qXAxsR9MFDRMHo2en4jkTAWgWFa7n6UqRlTTu5lj9RtFgQekayC71GyTEqvBY2tdLfHYoNsKKVBand7fmpwGDG2kivEUMNDitT2AG2F2GxbM+zbLewu3WwlXCzyEYpPq34+WK+C+QZuY+b4bOVnNTl1aiyMTIauomDTdvUbGAlnoIeukc65O1nvlI5uEQwgcxA/nFGYPdiXJKlgW0rZPaJX+7xZUlosP9fYR+1purREYHJBFPGs7Qx3Fq/RuPEcA2S9+CynhXm3TRRLOfzKYXeny/qdpJw==,iv:lpQ/utPri3QEIvB8V5MzHmgyeyGmlVGbkbNKKrb10lM=,tag:eWFSV+nRqDfYERXTXT8eAQ==,type:str] - #ENC[AES256_GCM,data:DkvLWzxK8Xp/TXnJNZ15Wa+ojB7qGxh44zvwdm50ENgTPaqIl2468q+HeOUIUE7OiXCQWj2PVg==,iv:J9EkotYGBY1FZo62wieUSFaTCn1NnBb6aOKnUOCELS8=,tag:vmxNYyjCYfi6n9nBZ8DwPg==,type:comment] - ssl-cert: ENC[AES256_GCM,data:frN9jDBZA2gyrCASsw6T5f5ld0CH+2m1YQOPWh53UfQnr9RxQUhvBYvwMtNJ2+DaT7GHTxZDC7Ft48DkrCKOawt3Z9BKkukYz3Wy9ozDUQ6dB4Hil4O76TQ+Lb3Yn5rKrJWIyR/QUwnv/HdpjxbrHMYw6Uc04uLj4djE+l5e2Iof3gYX64TDaB0ZQkvSAGWO92a3K2cwOKORNltR6KlJ9VzWixHBX8INJaGlPAnHMQkwu9ppkz8waYiIC9sWqwlMep8gN9mvPpq7fxMOvb8az3CkTmLTgXlDx5wxIx56uHlHkNlYKMvz3/HV8PAAIrMod6DCESeDIIP/g004rQRVpxMP5+G9uWklNFDcSpVIzHvl1GLqw05FCQGk93Tj+NmPt7dJiZssPz4dtthXEHg7/phOQJykJkyNVHcfIheqJHK1EQ3WztDJR9hGHUVepnRKo4VseVu6wIeOPa4En9OQ850+AfiekWcivfH9QMW2H3hsknC6Z2+Nb6X6AQBX9GPYwSjmioSbMSOIJpbvTxiM5K9AP25o13NHRH1xwydOqpTPxk0tf47o7PUyJ5gM/0zK60IwRVjRHb4974bpX10K3zLDgin9/WfjetsCm5o7QSs0JfzhTwxZeIC14ZrVwutdR9/KevK+305F7wpQO2jI3WHxniJsc+ov2JbESlj+2VR5+c/zC6yH20cBAOWnM3hblkbsVhXWG/LJtUKR58PGAYAMjrz4xwwvwUhEyEsw55kM0pzggkUJMrYDhQwf2FUsOl06b0qfUf63E7V1nI0jEEfEserO7geSWWf5t0ZQoiCzPxzX/ILd5dqtuzHiaMRonD/gZ/fUWkr1pWuZzi3agRVJSpEglZHwZuPd0L8jFaIyyF4StPiBkt1Ez+OAVS1vKJAQCzQYiFduJtBlExayioyV6iMjlOdcIlneWcgcAvmBAkjkBI2GlDS1Dtaev89tLJUcFQXxKMYIPjoFMNgINxN2ShSAukd9SnsGKCstDa8iwNJ88Ue0YGBTYLPnUnaBW3TJEdQyFF7SwL2/4FHIbX7thlr9vWC4pPthArmQCUDR8iYldSSI96eNoS1YI6qFEuRwUEh08yuDyHMHIFAHO9IAWWc5vza+BXsMcOt/NTuwhUAVWx3NC+TxAwGQJfXqHtsqZpCRZtdtWtacH/eBCPlhb0GZ5wQvIfxNi/okqGfk26d357jxNBgzJEAo9N9KdF4Wys5FzY78lQTF9pZEm8/Vzmodc1b4DZWYFeX9cCFtlEnqDPvknXSn64W/Yg2vO1siZk/HgY8BMHviiI008UMxpzMNiV9X+7csTjbCrf1GDjv8QmZwTUKLJbDgX4++HGM9TK/+wy4sPSic4Y0E+9+Z1QSFaXCogjUUCBwuI8+YnvRwzrK7HXJG7NzW7FM1FSkYjotv6zEQJ1xOQJvxwSa8c15GQ1tdq0ZhXEAxaUjABjFdUKKsOJp/SwQZScEAXbznWeAPa12CevFrDkVv/JZCr55bg+PCbxXwblxq8dVoHzkxPbRiG2fS9ue8rlp8lL2IubrpPaKAYxlL+sK1uOSxfa2cGF91vzNIb+a9mxplsPnXzxODfS+KVqEHLXY81bjIytq5ex3Fow3+0B9BSNRQygayPqyEPHiLzbabWOv58oKlgbCGBLBtPN6CGyH/i6GwqvGf1de9lYsoYRYwdYJN/PZvfgzcIW97eirlXB1hdSxAKlicjejrqyZP8gGupTi/htm9jB+TzYTcsAUt29djdEIdgGgAb1/zHET1em1DSR1v5qmyiMG/YmwyE6dAoyPhiGrAOPwTgJUahNB+Ok7bbOp5ZAM5/uhFumF+5LsNWAZc50vuo1bbYxpP5RXxK7+eF1xZ+Jj5JgFuZ2tn0gZlLBNjHPMVLEUn3XbsaVgBplBuE04GpMRAk5Hia5CLJqB+x+KHDUrG2e1WeZttv2KLm+WaPdogF+n6CO684Om1JMga3pRF1EvHvUFVmKYtIn/Iwhh8fbfTrZmN02K+RH5ZVnCrfn4uFjOJ5FMuYCvEV6Bwbd5a5nDCY0MhYv6jzg5P+C00ZUXwE2e4Pa+vVy9t4IXatdWSfxVG6hKBu9EeJYEQ2/zcZmjV8sBXcHXczDlo3CD4lqQD4cCN42+Px9hKP9FfvOTgWSBpXOQsns0D2A+wXYdcN4DpCVVQ4Y/ESjwVzfUn3jwsirowYl2XOIeidS6wrQ2KvG3WxfPq2cFNflpe4jDx1fFuY2/r35XpYS5nf4Lderldef1dYhYOb9eN45U8NX6Nn8Vwmewd52fC600upp2R7LoSTfIZQbzK8/rU84pIClvs+UzpzByZqTrrDhwkvJlbMeZVekwBJj83gdzHHrHRjPnapsLBmQ4DnRNHeabr3i7q87aFFYntREeQdqx0/HSocT4bRNYbQXydKus2Q5/cZAL3ptXTTrJpM5Sjf6JTxuN8tPCJ024KGxzvQo1Q2NkMIPR6JhAHhfK4UerhRtwVDTusoEpM6I6izJ1l68b5KULAIujSOGxZCU43Zuggn6ECLIaFo+qe1AA9cnLApoXH4IQu19xXchETsyPqkJJnnXbTFssSkeuCrvdAwyxqUmAwbIIN8tlw+Kf2+CisUB2APqhy79S0Z/2ehJyq2jfHf6zw4ZI+syU8diA3OJQzVJj5dhcTCwEkvdgb692QGk4FWmpoUTRl5FhwfAyNBrQqvDDB1MIP61tWlahBpPLeLbYTlSzrnFPyThtwd+IEZt1mZze7OghuKLqa1hOxb7aFVjwuHYWLoNXbJ8lqdrlM/wNRuZemhwhH7p8bob2x6a+cZ+DEHnVNMCHv9vgks2k2ng6FRB2wsC9pMIU4nU/aMhC+B643RIqvGUkgqyeQcpgAdqDuovLwt4wWYWZAuTbwFm8rL+fzwwyb6a24FrHAfIbcPbZWtSoQl3mOiQi6NogqDUslEYoVwnAL5v/WRjSTO+Bg6AfOA9JeR1OUqTXW1k1w5zngN1aP6CxsHu1wuhgUiWCTwomA5eg989GgRXc85neP97+Gx+jij5QbOyOqh/45VtPeT7sMWCyJW9gDRkNKdT9DuTtySWvic02+Yf79xq6cv5JafuAgl6JGyTOqqmqDuhLDJ/62KQ4Tn0VHGWIKqOoitt31nouueZHnbKMJ9QsJO0Mucsch05MBdPVqwOgrIIYOpqS0/jZnonOh+O/tL0SeOLb2yc04T+TbeCVAlod7r9J9YjRjUq0+GN5ZkQ/jwW1JiwXYnr+qFy5sWoDZIpCXp7kQBOb9CYiXVXCdq/3x5hemj1fJ7Rsn/xopJZXTqych/o9Nydg89lJJwzaNVOA+Uru9UBBejb4+SIn17X2VDb78r93Fqu8dsBEnDMJsqPTz9sJAQTjJauX7Ty+JmDioio0oopJAqORea5pJFfPnaGkseIKUoRD0VmxWKBnGA0baiQgbA5j8CcoIZujZa7hIcyDinYUpQcdPeZto9UGF8qxH2++dL+lblFuO72UKEtq0UHqDF9ByhmtLDZuY5V34UeIQCa+63etBBg2T4+M5Ev99zhcjEh4ILhoRJ+TtzO9qk0n3edvnThT67igBsCTttsgKfINN3TNDqzrvQK+BscSq2WUKMBq3ySe2JWsk6Nw+XNoadjEWO8r8Xm0jP9R+aRoVbsmeYVOjr/uJ4DgXkR9LijIiAKHMeOuBlTNbEWEUVM89mLCvHO7w/+Pt9Z30DhHM0dCCB967rRrMCP+xUCBtpjM0b5dA1AAEp8v3EJtL14JGqvtd4PhKOlTDumVLTj2jz7rHwQj5ZUxa7aFKkvVWBDywGcwUB2jj5CtKdGtFNPYguivVIjBY7dkPDY+rFi7UwhpDKEqUu6F56P1Bbl/YrV7fejDLAZaM///sYd2pJmYb0J9Fm9f8DP1AGLvLWrX8l/Ewp+hFDXo0xDi7zfowpjjnCa4AQZ7ZT2WCGWixypojetpyo+VPbocuFTvlYQIb0VK9vn9cLgDOrSKsyb1q2vJ1t1T1LOJKcbvT+d4GUMlIgbmhqZKEHuIYu3UHShh7R2WcSuB4IXQYEKJlwm8seVVeLu8Yub8DNIgO8BcKas2dnaI6hIEELuS/lZ3uonPX1rGv4QOWA83E6fNHMEM1jLclkknho07NkIx90yjm6A+S6kDaxIVmtS7AuLYyqgsabYMwz2ruiH+HU2hoGQ4qWCywGti0zNrvTXjf4AOdvb+TD2BONVuWIWRZbQoq8hdiI2sOkk0jaFNqM4u5XA1qL/GG8s/ldvoWvkNygIQmTjLZt8gDq9QacZe7VN1JXQ71+cy2Kak3EawyGlzeP2UtAkOSoGlTzzN0rDVUY/kTJtTVOsGQv5A2ENS3O/uduy0d8zFv589h/fSOxnBsYy7X1Yn9r3fu7pN44WP+vUvoGGp9Rty735TqL4uf2bSbELBT60ukh+OQKQiSKC5fw9fpc7adGM46veLRuhdN9Wu8GpCbgcSmJ3YFXdLy2GGH7Y+w/9ZV6+q57FvobuegwJ1iKwxP9PJejLooElW1AIP7LuDfOz/KwfkrGcEhE5f3AdAB5StqoQhULwy7H/UnPrt6+cAFX8d+MLWOcBr9KxICE4VhZWg44JDVaoZU5P0KADb+e7oOMM9LsgcMEFFvtvQ7ULSHYGwy3Lo4rPGhAwsPP5XwPOBBdj9nd9SrfNWcBb+TsYPbGObfh7+BgjKShIy3r1E9wzkD2cQA1Lj6/fgK/zuIrXQiaQEY0YjjCYEBbCNpun/6tYgPN0bcZS2vzZEaiOQvUdvKO0Be+RGX27Sl+iPd7snNia4aZK59B0EaGGdLq/TOncNOjotEDRnPBrkq7De20kfGCp2L/zMf5L/y4VESCwnMqPdSFk/BTuySp9R53xbj+07KKcAy4kj9czHNE2F0ng/YgvjJqaYcz/xQMCZGZ2P+O91TCuV9MVNHjsJMuGYBadoPUT20RW5mCdqItZYe8MZyzhO9ryTUWJsNWEYRlNHREpoEYPHz00BPeiU9JYLInU2pVgsQuuv4MMMAWJ4j3MbGLyZGSnZ7Ln7vCOUBC4jIDleaLfz1keJJ/GZE5RNU0aM/6qFnaL6/QN66/pa/NGi7iNAzHArgckp/8qbnpM2gbsu43FypDwR9eHroMxkEY3F6qP7xZN1t72DkQVaoZc+91+d8TB3VSfxjtIOFLJZs6hr9+L9bLQ9P6803BPwhjqS/HrMybM3b7aJ5pL1QE9vIe3RO/FUMH8I+OXzZRJZUGLoyekj1gaffPgibU+m5WiGeh1y668wF7hTaz8CdZPS/0ADEXv0FWnnCsg6o2PfUxZvv9gHPJPWBEg2KSsBFE/kyaNWyvC2T0PWh8/RbiCyK4AhXXi/xFO/jFcoj8Fy1pAl5fRAN/dFdyLjuvwNSGbrYFl0ivkTpNhjmnxx92iJHcegkvP4bCKOfnMqcd62i/qdLIi7fCSRNZal+namZ0oloseiLt9vCAjj7J2nbexVVK+BxzszlhS+EFX8RLwekhZodPgXCbSEuOoRNm1Vdphn+40G1lCmbPAPb//onX2kaeloqQ+PTOY1YBbVbw1FTcw7mGH0lK7R3TN57hMZFJBqHyIPiZiOWSFoscktQ4fhJV5Wx1+MGfc0vmFEDoGS7srUGGJJjXtdCvnjINhD+khIWApVKGqJt84Z83Eh2MGxtyl5uymQl3vWbZe7h04ai2sAOA7+Cmjtg6AiYEDRF7pS46f/OFbTrDNumb7O2sF8W5oIMlNfSu79/EkNpVrtwjFrMFFAyNwsilW/GNr02eIzRy3yS6lek3NOvZ0yT37HCUNOvjJqzqTUAFg6hqUgVyjp41UBN1rLR7GZQl2gyhjNWCqeSb0lgL3btkMIzwokoOJlMO1KBX7UwXm3t0tKvNXPDlAXepvk8B7uH2ON9hhAbNfTdNkR84RjQ1sZ7g5QO62kKF4cFfxFK7DDSmpW732fvCKxFowDi2GGSn6E8Ay5uO2kPdXmwsX9EdwjjXEpsFxYWTOAtyjJIRGfpvMsroskNd8CaQT6yaG2pR/1ZE4pLiIICpMax8dHbukpK7WWE9cDvFz96BC1dsx14vbUfwkm0sKcSaWllVn5Elw9EP8FTVcEr/NejFQbYh5auG2JrpheCKuk+YvM8QvpC2vPASnG6ww7W1oiZFtqGZBo8I5kh7DYadOeE3sRXxLr0YDNusReEiWP3eWskoSMCBX7xSVtg9hfdzSDd3Y7EeiI/vEx8nllOSI0lVVu01CQAnVK1z7EtUEe7bFRY/bIinm/rPA184220rHlatQaNVo8MOmo4fK/SMJOPHNj8xoy7dA==,iv:JvoC84+dJ+sKdDDNvzIRWJUKHftgW4hhT6fiJQXR4rs=,tag:wrFQF7ZqG0us3E98i/gkyA==,type:str] sops: - lastmodified: "2025-02-07T19:44:49Z" - mac: ENC[AES256_GCM,data:+0hpd/E7GxK/27f2Itf0hDV+3Ga4gHb8xxLutJ32HLBWLZ5Y+dN03xgkz8jBTiM+BeHwS4gz70Cs9X3zLMHbosWVuIV9DLuRaHRq/IU9KiADwqmCySZALqCf3+T5QKZr3Qs4AZJHwaAXkRX9HbnRFriIAFDJW/BGdIHdoROquxY=,iv:TeXI8LGqHVa5wo61sGdNbZ2nJvSlPdgn9R3Lq5qUggU=,tag:TFort5wxVTdi9LMlMeT/DQ==,type:str] + lastmodified: "2025-11-19T07:44:21Z" + mac: ENC[AES256_GCM,data:u95fSyrfZGan7Zwm/Vv23mVVXSqZhZOLQHFwSclOeIvnXXgv1b+sYlxlGZekQ8Bg24+5G/fUCFIrhfFtCNuB/R5Ynj6t6QhYYD8o3kTvUKa4nf3501gLAIzqbyK4PYpJx4Olv1OkGHHdr9UpP1m6cIhaAZtbBfqW+WL26HjE/CY=,iv:gk5tlrbON64+2y+5PHrhwSPh7B9bvYckRgrepHTiQDA=,tag:FopFqhMgCmoKun1QmjtC0w==,type:str] pgp: - created_at: "2025-10-03T21:38:48Z" enc: |- @@ -87,4 +83,4 @@ sops: -----END PGP MESSAGE----- fp: 0af7641adb8aa843136cf6d047f71da3e5ad79f9 unencrypted_suffix: _unencrypted - version: 3.9.2 + version: 3.11.0 diff --git a/keys/staging.yaml b/keys/staging.yaml index 6fa212d..dc9059b 100644 --- a/keys/staging.yaml +++ b/keys/staging.yaml @@ -22,18 +22,14 @@ matrix-hookshot: wireguard: server-key: ENC[AES256_GCM,data:FvY897XdKoa/mckE8JQLCkklsnYD6Wz1wpsu5t3uhEnW3iarnDQxF9msuYU=,iv:jqGXfekM+Vs+J9b5nlZ5Skd1ZKHajoUo2Dc4tMYPm1w=,tag:EehikjI/FCU8wqtpvJRamQ==,type:str] restic: - local-backups: ENC[AES256_GCM,data:3QjEv03t7wE=,iv:y/6Lv4eUbZZfGPwUONykz8VNL62cAJuWaJy9yk3aAmk=,tag:wMlGsepuG9JjwtUKGWSibw==,type:str] storagebox-backups: ENC[AES256_GCM,data:NEHk57B3YtI=,iv:0/qnqMVK0662sgfDQoLxcW7L09SKF8E5liCnjaQ2+2k=,tag:RU0BPwGgvI9bgOPr8VItmA==,type:str] storagebox-ssh-key: ENC[AES256_GCM,data:65+kbJPO90y+rRh3Q5cqLDtQa3VFfbaDPPo1nJLqxgAB7Wm3J7K4qUYAKPcYnkWV4/xFz63R2uCNaq5xv+vuZA==,iv:O7AeE/ujp5p1P7nff7PpghQfN2tQUYBSWL+EHRbE5yA=,tag:Pu/+bEAQuqwmD1Rc//t0cA==,type:str] turn: env: ENC[AES256_GCM,data:xjIz/AY109lyiL5N01p5T3HcYco/rM5CJSRTtg==,iv:16bW6OpyOK/QL0QPGQp/Baa9xyT8E3ZsYkwqmjuofk0=,tag:J5re3uKxIykw3YunvQWBgg==,type:str] secret: ENC[AES256_GCM,data:eQ7dAocoZtg=,iv:fgzjTPv30WqTKlLy+yMn5MsKQgjhPnwlGFFwYEg3gWs=,tag:1ze33U1NBkgMX/9SiaBNQg==,type:str] - ssl-key: ENC[AES256_GCM,data:RYfwHjBvwFXgXxXIEuWUzaycTdrCvmPivsNvvUIwDRynS5G2Dl6RCVp1w9zuLvoNun5ncUPGGuLMmVqN2wkJlw==,iv:UKI3bVTY7iTDNvp5UqrZ3QlQkMZ5p2bjgODEc6DCBfQ=,tag:sz7VTyRWyZxAsP4nE48DnA==,type:str] - #ENC[AES256_GCM,data:bxhKzU5Tzezl749CDu8e8kxa7ahGuZFaPa9K3kxuD+4sg5Hi3apgDlC0n8oK0DeiK4Ks7+9Cyw==,iv:T/zVJUpNAv1rR0a9+6SDTG08ws2A1hFBs5Ia3TpT0uk=,tag:uGXb1VryM+lIJ8r0I5durA==,type:comment] - ssl-cert: ENC[AES256_GCM,data:xHUr14CjKslgbGh/n5jYSOuCw9JRxS6YXE4fxS+aJzFcNeSeGNqoipPeuJupZGBnQP/FCqohiHY=,iv:/OEsVqRshGL9NIvntMC42EPZSNL0u6EfhtUBqgV7qog=,tag:4pxtNjuvy/ibm6nDtKdSkw==,type:str] sops: - lastmodified: "2025-02-07T17:43:24Z" - mac: ENC[AES256_GCM,data:akmD/bfgeTyFzW1quvM16cdj0fC6+CbJ8WyX9173H11yKGxvE1USQYcErpl1SHOx9Jk8LVb7f+MsUm2fjQF1MEq6xaWI74jem12lZ9CGXFaTL7e87JvfbK7pV+aKpxSBBNFyJgbYm30ibdUwxwKmNVfPb1e0HT9qwenvoV7RobM=,iv:mKqOW0ULXL711uczUbRf9NPo6uPTQoS/IbR46S+JID4=,tag:vE6NYzYLbQHDImov1XGTcg==,type:str] + lastmodified: "2025-11-19T16:42:43Z" + mac: ENC[AES256_GCM,data:4YivckDS+jBX3Bkon0bTAm3SXya4v2ieZyqeBXjBUYZeCmelIng7bn2dP7791O6RK6RvSXAGhiykWgGRW/boG3QM8VLxDMSRTKovJo5k6oxtFJC8OLDJoh1EC5BQLznJDKl4So6FgYPEtdQ6rx+Q6Ah7JSMtQilxRoe/hYapT90=,iv:9BGtS585gVbvH6l96/YYZiY1DrwB565vPaNNtFC9vbk=,tag:HsZuDMqPFHTMPxQsD36LNQ==,type:str] pgp: - created_at: "2025-10-03T21:38:26Z" enc: |- @@ -67,4 +63,4 @@ sops: -----END PGP MESSAGE----- fp: 2f5caa73e7ceea4fcc8d2881fde587e6737d2dbc unencrypted_suffix: _unencrypted - version: 3.9.2 + version: 3.11.0 From 77cf850467a09cc2ca4cf644b321dba5c57a23c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tristan=20Dani=C3=ABl=20Maat?= Date: Tue, 18 Nov 2025 15:49:18 +0800 Subject: [PATCH 06/24] fix(lock): Add missing lockfile update --- flake.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/flake.lock b/flake.lock index 705d87c..ad4903f 100644 --- a/flake.lock +++ b/flake.lock @@ -136,11 +136,11 @@ "pyproject-nix": "pyproject-nix" }, "locked": { - "lastModified": 1754978539, - "narHash": "sha256-nrDovydywSKRbWim9Ynmgj8SBm8LK3DI2WuhIqzOHYI=", + "lastModified": 1763413832, + "narHash": "sha256-dkqBwDXiv8MPoFyIvOuC4bVubAP+TlVZUkVMB78TTSg=", "owner": "nix-community", "repo": "dream2nix", - "rev": "fbec3263cb4895ac86ee9506cdc4e6919a1a2214", + "rev": "5658fba3a0b6b7d5cb0460b949651f64f644a743", "type": "github" }, "original": { @@ -356,11 +356,11 @@ ] }, "locked": { - "lastModified": 1762868777, - "narHash": "sha256-QqS72GvguP56oKDNUckWUPNJHjsdeuXh5RyoKz0wJ+E=", + "lastModified": 1763319842, + "narHash": "sha256-YG19IyrTdnVn0l3DvcUYm85u3PaqBt6tI6VvolcuHnA=", "owner": "cachix", "repo": "pre-commit-hooks.nix", - "rev": "c5c3147730384576196fb5da048a6e45dee10d56", + "rev": "7275fa67fbbb75891c16d9dee7d88e58aea2d761", "type": "github" }, "original": { From 4e59582de2c311a9f1263af6a8821c1ffa2943bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tristan=20Dani=C3=ABl=20Maat?= Date: Wed, 19 Nov 2025 15:51:53 +0800 Subject: [PATCH 07/24] refactor: Remove local package overlay --- configuration/default.nix | 2 -- configuration/services/starbound.nix | 11 +++++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/configuration/default.nix b/configuration/default.nix index 631e93e..9881db2 100644 --- a/configuration/default.nix +++ b/configuration/default.nix @@ -30,8 +30,6 @@ ./nginx ]; - nixpkgs.overlays = [ (_: prev: { local = import ../pkgs { pkgs = prev; }; }) ]; - nix = { extraOptions = '' experimental-features = nix-command flakes diff --git a/configuration/services/starbound.nix b/configuration/services/starbound.nix index 609d4c0..a667b57 100644 --- a/configuration/services/starbound.nix +++ b/configuration/services/starbound.nix @@ -1,4 +1,9 @@ -{ pkgs, lib, ... }: +{ + flake-inputs, + pkgs, + lib, + ... +}: let inherit (lib) concatStringsSep; in @@ -11,7 +16,9 @@ in after = [ "network.target" ]; serviceConfig = { - ExecStart = "${pkgs.local.starbound}/bin/launch-starbound ${./configs/starbound.json}"; + ExecStart = "${ + flake-inputs.self.packages.${pkgs.system}.starbound + }/bin/launch-starbound ${./configs/starbound.json}"; Type = "simple"; From f2ef91672b5937255fbcde644efc003eb4820ecf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tristan=20Dani=C3=ABl=20Maat?= Date: Thu, 13 Nov 2025 05:20:09 +0800 Subject: [PATCH 08/24] test(nginx): Add simple tests to assert nginx features work --- checks/default.nix | 2 ++ configuration/nginx/logging.nix | 58 +++++++++++++++++++++++++++++- configuration/nginx/ssl.nix | 64 +++++++++++++++++++++++++++++++++ modules/default.nix | 7 +++- modules/serviceTests/mocks.nix | 27 ++++++++++++++ modules/serviceTests/stub.nix | 20 +++++++++++ 6 files changed, 176 insertions(+), 2 deletions(-) create mode 100644 modules/serviceTests/mocks.nix create mode 100644 modules/serviceTests/stub.nix diff --git a/checks/default.nix b/checks/default.nix index 674b19d..737313d 100644 --- a/checks/default.nix +++ b/checks/default.nix @@ -6,6 +6,8 @@ let in { x86_64-linux = lib.mergeAttrsList [ + flake-inputs.self.nixosConfigurations.hetzner-1.config.serviceTests + { nix = checkLib.mkLint { name = "nix-lints"; diff --git a/configuration/nginx/logging.nix b/configuration/nginx/logging.nix index 0c6a955..e41bfae 100644 --- a/configuration/nginx/logging.nix +++ b/configuration/nginx/logging.nix @@ -1,4 +1,10 @@ -{ config, lib, ... }: +{ + flake-inputs, + pkgs, + config, + lib, + ... +}: let hostNames = lib.attrNames config.services.nginx.virtualHosts; logPath = name: "/var/log/nginx/${name}/access.log"; @@ -80,5 +86,55 @@ in }; }; }; + + serviceTests = + let + testHostConfig = + { config, ... }: + { + imports = [ + ./. + ../../modules/serviceTests/mocks.nix + ]; + + networking.firewall.allowedTCPPorts = [ 80 ]; + + services.nginx = { + domain = "testHost"; + virtualHosts."${config.services.nginx.domain}".locations."/".return = "200 ok"; + }; + }; + in + { + nginxMetricsWork = pkgs.testers.runNixOSTest { + name = "nginx-metrics-work"; + node.specialArgs = { inherit flake-inputs; }; + + nodes = { + testHost = testHostConfig; + + client = + { pkgs, ... }: + { + environment.systemPackages = [ pkgs.curl ]; + }; + }; + + testScript = '' + import time + + start_all() + + testHost.wait_for_unit("nginx.service") + client.succeed("curl --max-time 10 http://testHost") + + # Wait a bit for the prometheus exporter to scrape our logs + time.sleep(5) + + res = testHost.succeed("curl localhost:${builtins.toString config.services.prometheus.exporters.nginxlog.port}/metrics") + assert 'nginxlog_http_response_count_total{method="GET",status="200",vhost="testHost"} 1' in res, res + ''; + }; + }; }; } diff --git a/configuration/nginx/ssl.nix b/configuration/nginx/ssl.nix index 7abc38e..56bfa78 100644 --- a/configuration/nginx/ssl.nix +++ b/configuration/nginx/ssl.nix @@ -1,4 +1,5 @@ { + flake-inputs, pkgs, config, lib, @@ -69,5 +70,68 @@ "porkbun/api-key".owner = "acme"; "porkbun/secret-api-key".owner = "acme"; }; + + serviceTests = + let + testHostConfig = + { config, ... }: + { + imports = [ + ./. + ../../modules/serviceTests/mocks.nix + ]; + + networking.firewall.allowedTCPPorts = [ 443 ]; + + security.acme.certs."tlater.net".extraDomainNames = [ config.services.nginx.domain ]; + + services.nginx = { + domain = "testHost"; + + virtualHosts."${config.services.nginx.domain}" = { + useACMEHost = "tlater.net"; + onlySSL = true; + enableHSTS = true; + locations."/".return = "200 ok"; + }; + }; + }; + in + { + testNginxSSL = pkgs.testers.runNixOSTest { + name = "test-nginx-ssl"; + + node.specialArgs = { inherit flake-inputs; }; + nodes = { + testHost = testHostConfig; + + client = + { pkgs, ... }: + { + environment.systemPackages = [ pkgs.curl ]; + }; + }; + + testScript = '' + start_all() + + testHost.wait_for_unit("nginx.service") + testHost.copy_from_vm("/var/lib/acme/tlater.net/", "certs") + client.copy_from_host(f"{testHost.out_dir}/certs", "/certs") + + res = client.succeed(" ".join([ + "curl", + "--show-error", + "--silent", + "--dump-header -", + "--cacert /certs/tlater.net/fullchain.pem", + "https://testHost", + "-o /dev/null" + ])) + + assert "strict-transport-security: max-age=15552000; includeSubDomains" in res + ''; + }; + }; }; } diff --git a/modules/default.nix b/modules/default.nix index 9483c66..1bf5314 100644 --- a/modules/default.nix +++ b/modules/default.nix @@ -1 +1,6 @@ -{ imports = [ ./crowdsec ]; } +{ + imports = [ + ./crowdsec + ./serviceTests/stub.nix + ]; +} diff --git a/modules/serviceTests/mocks.nix b/modules/serviceTests/mocks.nix new file mode 100644 index 0000000..f864253 --- /dev/null +++ b/modules/serviceTests/mocks.nix @@ -0,0 +1,27 @@ +/** + Module containing mock definitions for service test runners. +*/ +{ flake-inputs, lib, ... }: +{ + imports = [ + flake-inputs.sops-nix.nixosModules.sops + ../. + ../../configuration/services/backups.nix + ]; + + sops.defaultSopsFile = ../../keys/staging.yaml; + environment.etc."staging.key" = { + mode = "0400"; + source = ../../keys/hosts/staging.key; + }; + services.openssh = { + enable = true; + hostKeys = lib.mkForce [ + { + type = "rsa"; + bits = 4096; + path = "/etc/staging.key"; + } + ]; + }; +} diff --git a/modules/serviceTests/stub.nix b/modules/serviceTests/stub.nix new file mode 100644 index 0000000..d4a4b78 --- /dev/null +++ b/modules/serviceTests/stub.nix @@ -0,0 +1,20 @@ +/** + Module to make writing service-specific tests easy. +*/ +{ lib, ... }: +let + inherit (lib) mkOption types; +in +{ + options = { + serviceTests = mkOption { + type = types.attrsOf types.package; + + description = '' + NixOS tests to run. + ''; + + default = { }; + }; + }; +} From 86a8df50046864de7c02955f0eda15041d00d005 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tristan=20Dani=C3=ABl=20Maat?= Date: Thu, 20 Nov 2025 00:17:43 +0800 Subject: [PATCH 09/24] refactor(firewall): Make services responsible for opening ports --- configuration/default.nix | 40 +--------------------- configuration/services/conduit/default.nix | 30 ++++++++++++++++ configuration/services/foundryvtt.nix | 5 +++ configuration/services/gitea.nix | 5 +++ configuration/services/immich.nix | 5 +++ configuration/services/metrics/grafana.nix | 5 +++ configuration/services/nextcloud.nix | 5 +++ configuration/services/starbound.nix | 2 ++ configuration/services/webserver.nix | 5 +++ 9 files changed, 63 insertions(+), 39 deletions(-) diff --git a/configuration/default.nix b/configuration/default.nix index 9881db2..1be5e12 100644 --- a/configuration/default.nix +++ b/configuration/default.nix @@ -1,9 +1,4 @@ -{ - config, - modulesPath, - flake-inputs, - ... -}: +{ modulesPath, flake-inputs, ... }: { imports = [ flake-inputs.disko.nixosModules.disko @@ -49,39 +44,6 @@ networking = { usePredictableInterfaceNames = false; useDHCP = false; - - firewall = { - allowedTCPPorts = [ - # http - 80 - 443 - # ssh - 2222 - # matrix - 8448 - # starbound - 21025 - - config.services.coturn.listening-port - config.services.coturn.tls-listening-port - config.services.coturn.alt-listening-port - config.services.coturn.alt-tls-listening-port - ]; - - allowedUDPPorts = [ - config.services.coturn.listening-port - config.services.coturn.tls-listening-port - config.services.coturn.alt-listening-port - config.services.coturn.alt-tls-listening-port - ]; - - allowedUDPPortRanges = [ - { - from = config.services.coturn.min-port; - to = config.services.coturn.max-port; - } - ]; - }; }; systemd.network.enable = true; diff --git a/configuration/services/conduit/default.nix b/configuration/services/conduit/default.nix index 6e01e81..b6f8f27 100644 --- a/configuration/services/conduit/default.nix +++ b/configuration/services/conduit/default.nix @@ -17,6 +17,36 @@ in ./matrix-hookshot.nix ]; + networking.firewall = { + allowedTCPPorts = [ + # These are for "normal" clients + 80 + 443 + + # Federation happens on 8448 + 8448 + + config.services.coturn.listening-port + config.services.coturn.tls-listening-port + config.services.coturn.alt-listening-port + config.services.coturn.alt-tls-listening-port + ]; + + allowedUDPPorts = [ + config.services.coturn.listening-port + config.services.coturn.tls-listening-port + config.services.coturn.alt-listening-port + config.services.coturn.alt-tls-listening-port + ]; + + allowedUDPPortRanges = [ + { + from = config.services.coturn.min-port; + to = config.services.coturn.max-port; + } + ]; + }; + services = { matrix-conduit = { enable = true; diff --git a/configuration/services/foundryvtt.nix b/configuration/services/foundryvtt.nix index 6c475a3..5c8a21f 100644 --- a/configuration/services/foundryvtt.nix +++ b/configuration/services/foundryvtt.nix @@ -11,6 +11,11 @@ in { imports = [ flake-inputs.foundryvtt.nixosModules.foundryvtt ]; + networking.firewall.allowedTCPPorts = [ + 80 + 443 + ]; + services = { foundryvtt = { enable = true; diff --git a/configuration/services/gitea.nix b/configuration/services/gitea.nix index 613d30c..b4dd719 100644 --- a/configuration/services/gitea.nix +++ b/configuration/services/gitea.nix @@ -8,6 +8,11 @@ let domain = "gitea.${config.services.nginx.domain}"; in { + networking.firewall.allowedTCPPorts = [ + 80 + 443 + ]; + services = { forgejo = { enable = true; diff --git a/configuration/services/immich.nix b/configuration/services/immich.nix index 516ea3e..39673d0 100644 --- a/configuration/services/immich.nix +++ b/configuration/services/immich.nix @@ -8,6 +8,11 @@ let hostName = "immich.${config.services.nginx.domain}"; in { + networking.firewall.allowedTCPPorts = [ + 80 + 443 + ]; + services = { immich = { enable = true; diff --git a/configuration/services/metrics/grafana.nix b/configuration/services/metrics/grafana.nix index f4b6956..765a364 100644 --- a/configuration/services/metrics/grafana.nix +++ b/configuration/services/metrics/grafana.nix @@ -3,6 +3,11 @@ let domain = "metrics.${config.services.nginx.domain}"; in { + networking.firewall.allowedTCPPorts = [ + 80 + 443 + ]; + services.grafana = { enable = true; settings = { diff --git a/configuration/services/nextcloud.nix b/configuration/services/nextcloud.nix index ef2a6ac..77cfa4c 100644 --- a/configuration/services/nextcloud.nix +++ b/configuration/services/nextcloud.nix @@ -9,6 +9,11 @@ let hostName = "nextcloud.${config.services.nginx.domain}"; in { + networking.firewall.allowedTCPPorts = [ + 80 + 443 + ]; + services = { nextcloud = { inherit hostName; diff --git a/configuration/services/starbound.nix b/configuration/services/starbound.nix index a667b57..6b97471 100644 --- a/configuration/services/starbound.nix +++ b/configuration/services/starbound.nix @@ -8,6 +8,8 @@ let inherit (lib) concatStringsSep; in { + networking.firewall.allowedTCPPorts = [ 21025 ]; + # Sadly, steam-run requires some X libs environment.noXlibs = false; diff --git a/configuration/services/webserver.nix b/configuration/services/webserver.nix index 864f6c0..8f08e4f 100644 --- a/configuration/services/webserver.nix +++ b/configuration/services/webserver.nix @@ -3,6 +3,11 @@ let inherit (config.services.nginx) domain; in { + networking.firewall.allowedTCPPorts = [ + 80 + 443 + ]; + services.tlaternet-webserver = { enable = true; listen = { From eef7f004b364e230ceddf3c2b5bd5cd63f9a4e24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tristan=20Dani=C3=ABl=20Maat?= Date: Thu, 20 Nov 2025 00:22:32 +0800 Subject: [PATCH 10/24] chore: Disable hack for Minecraft optimization on VPS --- configuration/default.nix | 7 ------- 1 file changed, 7 deletions(-) diff --git a/configuration/default.nix b/configuration/default.nix index 1be5e12..1b10a15 100644 --- a/configuration/default.nix +++ b/configuration/default.nix @@ -34,13 +34,6 @@ settings.trusted-users = [ "@wheel" ]; }; - # Optimization for minecraft servers, see: - # https://bugs.mojang.com/browse/MC-183518 - boot.kernelParams = [ - "highres=off" - "nohz=off" - ]; - networking = { usePredictableInterfaceNames = false; useDHCP = false; From 54a12223ea5d258187f5fc35a3c360149a03a789 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tristan=20Dani=C3=ABl=20Maat?= Date: Thu, 20 Nov 2025 00:24:04 +0800 Subject: [PATCH 11/24] fix: Make the default package disabling actually work --- configuration/default.nix | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/configuration/default.nix b/configuration/default.nix index 1b10a15..c8faa8f 100644 --- a/configuration/default.nix +++ b/configuration/default.nix @@ -1,4 +1,9 @@ -{ modulesPath, flake-inputs, ... }: +{ + lib, + modulesPath, + flake-inputs, + ... +}: { imports = [ flake-inputs.disko.nixosModules.disko @@ -79,7 +84,7 @@ sops.defaultSopsFile = ../keys/production.yaml; # Remove some unneeded packages - environment.defaultPackages = [ ]; + environment.defaultPackages = lib.mkForce [ ]; system.stateVersion = "20.09"; } From 5e488337b451e2266c7cb995f347e10aeb706821 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tristan=20Dani=C3=ABl=20Maat?= Date: Thu, 20 Nov 2025 00:37:13 +0800 Subject: [PATCH 12/24] refactor: Move service imports into a `default.nix` --- configuration/default.nix | 18 ++---------------- configuration/services/default.nix | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 16 deletions(-) create mode 100644 configuration/services/default.nix diff --git a/configuration/default.nix b/configuration/default.nix index c8faa8f..96d0ddd 100644 --- a/configuration/default.nix +++ b/configuration/default.nix @@ -9,25 +9,11 @@ flake-inputs.disko.nixosModules.disko flake-inputs.sops-nix.nixosModules.sops flake-inputs.tlaternet-webserver.nixosModules.default - "${modulesPath}/profiles/minimal.nix" - (import ../modules) - ./services/backups.nix - ./services/battery-manager.nix - ./services/conduit - ./services/crowdsec.nix - ./services/foundryvtt.nix - ./services/gitea.nix - ./services/immich.nix - ./services/metrics - ./services/minecraft.nix - ./services/nextcloud.nix - ./services/webserver.nix - ./services/wireguard.nix - # ./services/starbound.nix -- Not currently used - ./services/postgres.nix + (import ../modules) ./nginx + ./services ]; nix = { diff --git a/configuration/services/default.nix b/configuration/services/default.nix new file mode 100644 index 0000000..adc4482 --- /dev/null +++ b/configuration/services/default.nix @@ -0,0 +1,18 @@ +{ + imports = [ + ./backups.nix + ./battery-manager.nix + ./conduit + ./crowdsec.nix + ./foundryvtt.nix + ./gitea.nix + ./immich.nix + ./metrics + ./minecraft.nix + ./nextcloud.nix + ./postgres.nix + # ./starbound.nix -- Not currently used + ./webserver.nix + ./wireguard.nix + ]; +} From d950c1fa23d7cf6e290465565532f2bed288d21d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tristan=20Dani=C3=ABl=20Maat?= Date: Thu, 20 Nov 2025 00:47:22 +0800 Subject: [PATCH 13/24] refactor: Don't do pointless indirection when importing modules --- configuration/default.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configuration/default.nix b/configuration/default.nix index 96d0ddd..464f7ed 100644 --- a/configuration/default.nix +++ b/configuration/default.nix @@ -11,7 +11,7 @@ flake-inputs.tlaternet-webserver.nixosModules.default "${modulesPath}/profiles/minimal.nix" - (import ../modules) + ../modules ./nginx ./services ]; From ba617d429b8fccbafc29712466467e7f780144ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tristan=20Dani=C3=ABl=20Maat?= Date: Thu, 20 Nov 2025 14:20:46 +0800 Subject: [PATCH 14/24] chore(flake): Bump to new sonnenshift version with better flake --- flake.lock | 83 ++++++++++++++++++++++-------------------------------- flake.nix | 9 +----- 2 files changed, 34 insertions(+), 58 deletions(-) diff --git a/flake.lock b/flake.lock index ad4903f..207a0b0 100644 --- a/flake.lock +++ b/flake.lock @@ -10,17 +10,21 @@ "sonnenshift", "crate2nix" ], - "git-hooks": "git-hooks", "nixpkgs": [ + "sonnenshift", "nixpkgs" + ], + "pre-commit-hooks": [ + "sonnenshift", + "crate2nix" ] }, "locked": { - "lastModified": 1760971495, - "narHash": "sha256-IwnNtbNVrlZIHh7h4Wz6VP0Furxg9Hh0ycighvL5cZc=", + "lastModified": 1709700175, + "narHash": "sha256-A0/6ZjLmT9qdYzKHmevnEIC7G+GiZ4UCr8v0poRPzds=", "owner": "cachix", "repo": "cachix", - "rev": "c5bfd933d1033672f51a863c47303fc0e093c2d2", + "rev": "be97b37989f11b724197b5f4c7ffd78f12c8c4bf", "type": "github" }, "original": { @@ -45,6 +49,7 @@ "flake-parts": "flake-parts", "nix-test-runner": "nix-test-runner", "nixpkgs": [ + "sonnenshift", "nixpkgs" ], "pre-commit-hooks": "pre-commit-hooks" @@ -87,6 +92,10 @@ }, "devshell": { "inputs": { + "flake-utils": [ + "deploy-rs", + "utils" + ], "nixpkgs": [ "sonnenshift", "crate2nix", @@ -94,11 +103,11 @@ ] }, "locked": { - "lastModified": 1762521437, - "narHash": "sha256-RXN+lcx4DEn3ZS+LqEJSUu/HH+dwGvy0syN7hTo/Chg=", + "lastModified": 1711099426, + "narHash": "sha256-HzpgM/wc3aqpnHJJ2oDqPBkNsqWbW0WfWUO8lKu8nGk=", "owner": "numtide", "repo": "devshell", - "rev": "07bacc9531f5f4df6657c0a02a806443685f384a", + "rev": "2d45b54ca4a183f2fdcf4b19c895b64fbf620ee8", "type": "github" }, "original": { @@ -249,43 +258,12 @@ "type": "github" } }, - "git-hooks": { - "inputs": { - "flake-compat": [ - "sonnenshift", - "crate2nix", - "cachix", - "flake-compat" - ], - "gitignore": "gitignore", - "nixpkgs": [ - "sonnenshift", - "crate2nix", - "cachix", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1762868777, - "narHash": "sha256-QqS72GvguP56oKDNUckWUPNJHjsdeuXh5RyoKz0wJ+E=", - "owner": "cachix", - "repo": "git-hooks.nix", - "rev": "c5c3147730384576196fb5da048a6e45dee10d56", - "type": "github" - }, - "original": { - "owner": "cachix", - "repo": "git-hooks.nix", - "type": "github" - } - }, "gitignore": { "inputs": { "nixpkgs": [ "sonnenshift", "crate2nix", - "cachix", - "git-hooks", + "pre-commit-hooks", "nixpkgs" ] }, @@ -342,25 +320,30 @@ "crate2nix", "flake-compat" ], - "gitignore": [ + "flake-utils": [ "sonnenshift", "crate2nix", - "cachix", - "git-hooks", - "gitignore" + "devshell", + "flake-utils" ], + "gitignore": "gitignore", "nixpkgs": [ "sonnenshift", "crate2nix", "nixpkgs" + ], + "nixpkgs-stable": [ + "sonnenshift", + "crate2nix", + "nixpkgs" ] }, "locked": { - "lastModified": 1763319842, - "narHash": "sha256-YG19IyrTdnVn0l3DvcUYm85u3PaqBt6tI6VvolcuHnA=", + "lastModified": 1712055707, + "narHash": "sha256-4XLvuSIDZJGS17xEwSrNuJLL7UjDYKGJSbK1WWX2AK8=", "owner": "cachix", "repo": "pre-commit-hooks.nix", - "rev": "7275fa67fbbb75891c16d9dee7d88e58aea2d761", + "rev": "e35aed5fda3cc79f88ed7f1795021e559582093a", "type": "github" }, "original": { @@ -478,11 +461,11 @@ ] }, "locked": { - "lastModified": 1748144928, - "narHash": "sha256-OPdi6AjrWWwZlukNwDHDWAf1CCFkHsf8JbA3rJoibXM=", + "lastModified": 1763619077, + "narHash": "sha256-dlfamaoIzFEgwgtzPJuw5Tl5SqjbWcV8CsbP2hVBeuI=", "ref": "refs/heads/main", - "rev": "429036aa8630d2325f8a9f067cc041d628279a8e", - "revCount": 24, + "rev": "64a2c8a3743ea6897ecac6692fba8aebc3389fca", + "revCount": 26, "type": "git", "url": "ssh://git@github.com/sonnenshift/battery-manager" }, diff --git a/flake.nix b/flake.nix index 386de98..334f73c 100644 --- a/flake.nix +++ b/flake.nix @@ -50,16 +50,9 @@ url = "git+ssh://git@github.com/sonnenshift/battery-manager"; inputs = { nixpkgs.follows = "nixpkgs"; - crate2nix.inputs = { - nixpkgs.follows = "nixpkgs"; flake-compat.follows = "deploy-rs/flake-compat"; - cachix.inputs.nixpkgs.follows = "nixpkgs"; - pre-commit-hooks.inputs.gitignore.follows = "sonnenshift/crate2nix/cachix/git-hooks/gitignore"; - - # Yes, they do this insanity: - # https://github.com/nix-community/crate2nix/issues/371 - crate2nix_stable.follows = "sonnenshift/crate2nix"; + devshell.inputs.flake-utils.follows = "deploy-rs/utils"; }; }; }; From 92b257e1af1f81bf00d560f80110d4de397dc5ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tristan=20Dani=C3=ABl=20Maat?= Date: Thu, 20 Nov 2025 14:39:29 +0800 Subject: [PATCH 15/24] chore(devshell): Use deploy-rs from nixpkgs for binary caching --- flake.nix | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/flake.nix b/flake.nix index 334f73c..989ae71 100644 --- a/flake.nix +++ b/flake.nix @@ -63,7 +63,6 @@ self, nixpkgs, sops-nix, - deploy-rs, ... }@inputs: let @@ -78,6 +77,25 @@ ./configuration/hardware-specific/vm.nix ]; }; + + # deploy-rs unfortunately uses an `import nixpkgs`, and its + # library functions depend on an instantiated nixpkgs, so we + # can't get around multi-nixpkgs-eval. + inherit + (import nixpkgs { + inherit system; + overlays = [ + inputs.deploy-rs.overlays.default + (_: prev: { + deploy-rs = { + inherit (nixpkgs.legacyPackages.${system}) deploy-rs; + inherit (prev.deploy-rs) lib; + }; + }) + ]; + }) + deploy-rs + ; in { ################## @@ -105,7 +123,7 @@ profiles.system = { user = "root"; - path = deploy-rs.lib.${system}.activate.nixos self.nixosConfigurations.hetzner-1; + path = deploy-rs.lib.activate.nixos self.nixosConfigurations.hetzner-1; }; sshUser = "tlater"; @@ -159,7 +177,7 @@ packages = nixpkgs.lib.attrValues { inherit (sops-nix.packages.${system}) sops-import-keys-hook sops-init-gpg-key; - inherit (deploy-rs.packages.${system}) default; + inherit (deploy-rs) deploy-rs; }; }; From 016ae5ec54f762d152c2075bc8c41be453e12587 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tristan=20Dani=C3=ABl=20Maat?= Date: Thu, 20 Nov 2025 14:40:50 +0800 Subject: [PATCH 16/24] chore(flake): Switch to channel tarball for nixpkgs input --- flake.lock | 17 +++++++---------- flake.nix | 2 +- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/flake.lock b/flake.lock index 207a0b0..6c5d414 100644 --- a/flake.lock +++ b/flake.lock @@ -299,18 +299,15 @@ }, "nixpkgs": { "locked": { - "lastModified": 1760782625, - "narHash": "sha256-qeSmHF66cMiHObiBhm8IngmqDBEcqNdBSSoAjuE6tTw=", - "owner": "nixos", - "repo": "nixpkgs", - "rev": "f2ee78c4eb601be36a277e1779a7a87655419dad", - "type": "github" + "lastModified": 1763509821, + "narHash": "sha256-ibZr0ONEUA1W2WAdTzgm9/6jBE+tM20j1YW2FK4RZ/k=", + "rev": "659aa6fa27619d04de231b4cc0c938905dfa01e9", + "type": "tarball", + "url": "https://releases.nixos.org/nixos/25.05-small/nixos-25.05.812929.659aa6fa2761/nixexprs.tar.xz?lastModified=1763509821&rev=659aa6fa27619d04de231b4cc0c938905dfa01e9" }, "original": { - "owner": "nixos", - "ref": "nixos-25.05-small", - "repo": "nixpkgs", - "type": "github" + "type": "tarball", + "url": "https://channels.nixos.org/nixos-25.05-small/nixexprs.tar.xz" } }, "pre-commit-hooks": { diff --git a/flake.nix b/flake.nix index 989ae71..029fd9a 100644 --- a/flake.nix +++ b/flake.nix @@ -2,7 +2,7 @@ description = "tlater.net host configuration"; inputs = { - nixpkgs.url = "github:nixos/nixpkgs/nixos-25.05-small"; + nixpkgs.url = "https://channels.nixos.org/nixos-25.05-small/nixexprs.tar.xz"; ## Nix/OS utilities From 324d2d94f3cbe99951626eb4d9b2b323ccd98d25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tristan=20Dani=C3=ABl=20Maat?= Date: Thu, 20 Nov 2025 14:49:28 +0800 Subject: [PATCH 17/24] update: Update flake inputs --- flake.lock | 55 +++++++++++++++++++++++++----------------------------- 1 file changed, 25 insertions(+), 30 deletions(-) diff --git a/flake.lock b/flake.lock index 6c5d414..05a6a5b 100644 --- a/flake.lock +++ b/flake.lock @@ -77,11 +77,11 @@ "utils": "utils" }, "locked": { - "lastModified": 1756719547, - "narHash": "sha256-N9gBKUmjwRKPxAafXEk1EGadfk2qDZPBQp4vXWPHINQ=", + "lastModified": 1762286984, + "narHash": "sha256-9I2H9x5We6Pl+DBYHjR1s3UT8wgwcpAH03kn9CqtdQc=", "owner": "serokell", "repo": "deploy-rs", - "rev": "125ae9e3ecf62fb2c0fd4f2d894eb971f1ecaed2", + "rev": "9c870f63e28ec1e83305f7f6cb73c941e699f74f", "type": "github" }, "original": { @@ -123,11 +123,11 @@ ] }, "locked": { - "lastModified": 1760701190, - "narHash": "sha256-y7UhnWlER8r776JsySqsbTUh2Txf7K30smfHlqdaIQw=", + "lastModified": 1762276996, + "narHash": "sha256-TtcPgPmp2f0FAnc+DMEw4ardEgv1SGNR3/WFGH0N19M=", "owner": "nix-community", "repo": "disko", - "rev": "3a9450b26e69dcb6f8de6e2b07b3fc1c288d85f5", + "rev": "af087d076d3860760b3323f6b583f4d828c1ac17", "type": "github" }, "original": { @@ -145,11 +145,11 @@ "pyproject-nix": "pyproject-nix" }, "locked": { - "lastModified": 1763413832, - "narHash": "sha256-dkqBwDXiv8MPoFyIvOuC4bVubAP+TlVZUkVMB78TTSg=", + "lastModified": 1735160684, + "narHash": "sha256-n5CwhmqKxifuD4Sq4WuRP/h5LO6f23cGnSAuJemnd/4=", "owner": "nix-community", "repo": "dream2nix", - "rev": "5658fba3a0b6b7d5cb0460b949651f64f644a743", + "rev": "8ce6284ff58208ed8961681276f82c2f8f978ef4", "type": "github" }, "original": { @@ -245,11 +245,11 @@ ] }, "locked": { - "lastModified": 1757786467, - "narHash": "sha256-gx3THVUlpycVrUFC9vGhAtYRLI7dJtHyo67Zdq5Hadc=", + "lastModified": 1761916399, + "narHash": "sha256-wLZ8km5ftKlIDdHJrFiDQivXc5b+7DRxmBp2347H5g8=", "owner": "reckenrode", "repo": "nix-foundryvtt", - "rev": "1bbc26a28d320fb336d94e9f3cc6b92c035fab20", + "rev": "8cceb7af3dfbe465b5108db5c098b097edf85790", "type": "github" }, "original": { @@ -363,11 +363,11 @@ "slimlock": "slimlock" }, "locked": { - "lastModified": 1750754571, - "narHash": "sha256-qquerCyD6WhVccAV0qI7NFGwSN7rXslhogPrTaFvWto=", + "lastModified": 1728546539, + "narHash": "sha256-Sws7w0tlnjD+Bjck1nv29NjC5DbL6nH5auL9Ex9Iz2A=", "owner": "thomashoneyman", "repo": "purescript-overlay", - "rev": "81513816356c43c6ef604abdf5da41834306a2d5", + "rev": "4ad4c15d07bd899d7346b331f377606631eb0ee4", "type": "github" }, "original": { @@ -377,23 +377,18 @@ } }, "pyproject-nix": { - "inputs": { - "nixpkgs": [ - "tlaternet-webserver", - "dream2nix", - "nixpkgs" - ] - }, + "flake": false, "locked": { - "lastModified": 1762427963, - "narHash": "sha256-CkPlAbIQ87wmjy5qHibfzk4DmMGBNqFer+lLfXjpP5M=", - "owner": "pyproject-nix", + "lastModified": 1702448246, + "narHash": "sha256-hFg5s/hoJFv7tDpiGvEvXP0UfFvFEDgTdyHIjDVHu1I=", + "owner": "davhau", "repo": "pyproject.nix", - "rev": "4540ea004e04fcd12dd2738d51383d10f956f7b9", + "rev": "5a06a2697b228c04dd2f35659b4b659ca74f7aeb", "type": "github" }, "original": { - "owner": "pyproject-nix", + "owner": "davhau", + "ref": "dream2nix", "repo": "pyproject.nix", "type": "github" } @@ -478,11 +473,11 @@ ] }, "locked": { - "lastModified": 1760393368, - "narHash": "sha256-8mN3kqyqa2PKY0wwZ2UmMEYMcxvNTwLaOrrDsw6Qi4E=", + "lastModified": 1763607916, + "narHash": "sha256-VefBA1JWRXM929mBAFohFUtQJLUnEwZ2vmYUNkFnSjE=", "owner": "Mic92", "repo": "sops-nix", - "rev": "ab8d56e85b8be14cff9d93735951e30c3e86a437", + "rev": "877bb495a6f8faf0d89fc10bd142c4b7ed2bcc0b", "type": "github" }, "original": { From aeba7301b02ec6874701875b05e872949e7d1a7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tristan=20Dani=C3=ABl=20Maat?= Date: Sat, 22 Nov 2025 19:13:03 +0800 Subject: [PATCH 18/24] chore(battery-manager): Update sonnen password --- keys/production.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/keys/production.yaml b/keys/production.yaml index a686ed3..9497ad6 100644 --- a/keys/production.yaml +++ b/keys/production.yaml @@ -3,7 +3,7 @@ porkbun: secret-api-key: ENC[AES256_GCM,data:zV5PTKf45Zab8uW8mbuXmPNzciq6tV9OF0wUND7YnRk/DjZneYWItAsNBVoM+iHA+XsUPDoeKo6hoJiGkH/cCQ8WvuM=,iv:yr1M5DlgI8k6BgzNz3HRnqspHOrQuf2PmoZS1HGp0v8=,tag:JkNNziMMfKFZV2hnx5lXRg==,type:str] battery-manager: email: ENC[AES256_GCM,data:rYLUACXR/n+bLBmZ,iv:sUBEkh2+7qGjHZ5R23e/hoCiyTA7GTL4bJvXmxjZ5Sw=,tag:fdPMllaQQfRgX0WZKIre4g==,type:str] - password: ENC[AES256_GCM,data:7cokZa6Q6ahSeiFPz+cV,iv:vz405P0IcG9FsAQXlY7mi78GuushQUKJm2irG6buGzc=,tag:JLHG2jTkJDGbinAq9dXRsQ==,type:str] + password: ENC[AES256_GCM,data:h12XeBosdVAJ9QwEYSyC,iv:m6A6EhL9BF4+c7P++A7qH2gXtRnxzAk3nCUvPH8E7e0=,tag:krZyGPuCSDLNTj+QQxN8vg==,type:str] forgejo: metrics-token: ENC[AES256_GCM,data:WVbD5JloJlHNjeEwe1uEd4Haj6L3ilj1Pnux6yrelUQP18ZPAh90aDO1OIZHaPJR7tTeyATr8BIzZL1zkNhCuA==,iv:eTYXN3hymIN3bTX1YxNGkAYE0KVDbdz2ds8UQAHlALE=,tag:A61loGdu0pfsiez96u2Qsg==,type:str] grafana: @@ -28,8 +28,8 @@ turn: env: ENC[AES256_GCM,data:kt5nhVo9pb/ZbPUEcqSYXxN9YMgQKnFb5VRfFFS/qoIaJ73uD2fuJKqcxAyVRrdLqnSAWSQBgTgunBzdP7xqLAK2qt8DYAQWHkIe9uxFbSXZpdmw,iv:9lq6SFwTFN4GGm6gPiJpUMasMdnHVF6XLGYrsyG3kjU=,tag:428Qf9DOiiHt/Wjb188b8g==,type:str] secret: ENC[AES256_GCM,data:si7ee6Xfhdgdyzbp6aQpF7pz3TmTBb7iQ82lRPVXNDg9JfHI+lbmgAsSnRLX5qMCA6P9R045sSMosqidL8QwRg==,iv:SrhpZKK8D45yxCEfDb9P3TwtA14+qEI+wcRqcN/a6pw=,tag:PiwV+mOL9xHJgJft6sc61g==,type:str] sops: - lastmodified: "2025-11-19T07:44:21Z" - mac: ENC[AES256_GCM,data:u95fSyrfZGan7Zwm/Vv23mVVXSqZhZOLQHFwSclOeIvnXXgv1b+sYlxlGZekQ8Bg24+5G/fUCFIrhfFtCNuB/R5Ynj6t6QhYYD8o3kTvUKa4nf3501gLAIzqbyK4PYpJx4Olv1OkGHHdr9UpP1m6cIhaAZtbBfqW+WL26HjE/CY=,iv:gk5tlrbON64+2y+5PHrhwSPh7B9bvYckRgrepHTiQDA=,tag:FopFqhMgCmoKun1QmjtC0w==,type:str] + lastmodified: "2025-11-22T11:12:58Z" + mac: ENC[AES256_GCM,data:KWc1yNxHQa7MKV/3iXaGOz+RQnHoF4cPumhdoY9zMzWsF1UV0FcR9xCF8KB40elHd4/7uz0stKiSiHhLvtSBW6KjMj0N5JSFknXKxGJWf8XA6ZJrvHhWamXX/ZVbdpvgwh6LzluzogjlCWatWYcr6svDoSEm0gL+Aynudyrx75I=,iv:gPXZpMs0TOvY6cpQJ4MprGlS8jleuO+vWwmJ0F6G+3s=,tag:BfhXRv+mANrBVASc8Edu8A==,type:str] pgp: - created_at: "2025-10-03T21:38:48Z" enc: |- From 59fdb372220e47ef3d979a3406423d01d50b086a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tristan=20Dani=C3=ABl=20Maat?= Date: Mon, 24 Nov 2025 03:29:18 +0800 Subject: [PATCH 19/24] feat(webserver): Vendor and reimplement main pages in leptos --- .dir-locals.el | 7 + configuration/default.nix | 1 - configuration/services/webserver.nix | 64 +- flake.lock | 167 +- flake.nix | 19 +- keys/staging.yaml | 6 +- pkgs/default.nix | 7 +- pkgs/packages/webserver/.gitignore | 1 + pkgs/packages/webserver/Cargo.lock | 3453 +++++++++++++++++ pkgs/packages/webserver/Cargo.toml | 68 + pkgs/packages/webserver/config.toml | 3 + pkgs/packages/webserver/leptosfmt.toml | 5 + pkgs/packages/webserver/package.nix | 295 ++ pkgs/packages/webserver/public/icon.svg | 78 + pkgs/packages/webserver/src/app.rs | 56 + pkgs/packages/webserver/src/app/homepage.rs | 70 + pkgs/packages/webserver/src/app/mail.rs | 217 ++ pkgs/packages/webserver/src/components.rs | 45 + pkgs/packages/webserver/src/lib.rs | 73 + pkgs/packages/webserver/src/main.rs | 54 + .../webserver/style/custom-bulma.scss | 57 + pkgs/packages/webserver/style/fonts.scss | 48 + pkgs/packages/webserver/style/main.scss | 7 + pkgs/packages/webserver/style/typed.scss | 149 + pkgs/packages/webserver/update.nu | 88 + 25 files changed, 4862 insertions(+), 176 deletions(-) create mode 100644 .dir-locals.el create mode 100644 pkgs/packages/webserver/.gitignore create mode 100644 pkgs/packages/webserver/Cargo.lock create mode 100644 pkgs/packages/webserver/Cargo.toml create mode 100644 pkgs/packages/webserver/config.toml create mode 100644 pkgs/packages/webserver/leptosfmt.toml create mode 100644 pkgs/packages/webserver/package.nix create mode 100644 pkgs/packages/webserver/public/icon.svg create mode 100644 pkgs/packages/webserver/src/app.rs create mode 100644 pkgs/packages/webserver/src/app/homepage.rs create mode 100644 pkgs/packages/webserver/src/app/mail.rs create mode 100644 pkgs/packages/webserver/src/components.rs create mode 100644 pkgs/packages/webserver/src/lib.rs create mode 100644 pkgs/packages/webserver/src/main.rs create mode 100644 pkgs/packages/webserver/style/custom-bulma.scss create mode 100644 pkgs/packages/webserver/style/fonts.scss create mode 100644 pkgs/packages/webserver/style/main.scss create mode 100644 pkgs/packages/webserver/style/typed.scss create mode 100644 pkgs/packages/webserver/update.nu diff --git a/.dir-locals.el b/.dir-locals.el new file mode 100644 index 0000000..af235d5 --- /dev/null +++ b/.dir-locals.el @@ -0,0 +1,7 @@ +((rust-mode + . ((eglot-workspace-configuration + . (:rust-analyzer + (:cargo (:features "all") + :check (:command "clippy") + :rustfmt (:overrideCommand ["leptosfmt" "--stdin" "--rustfmt"]) + :linkedProjects ["./pkgs/packages/webserver/Cargo.toml"])))))) diff --git a/configuration/default.nix b/configuration/default.nix index 464f7ed..456d12d 100644 --- a/configuration/default.nix +++ b/configuration/default.nix @@ -8,7 +8,6 @@ imports = [ flake-inputs.disko.nixosModules.disko flake-inputs.sops-nix.nixosModules.sops - flake-inputs.tlaternet-webserver.nixosModules.default "${modulesPath}/profiles/minimal.nix" ../modules diff --git a/configuration/services/webserver.nix b/configuration/services/webserver.nix index 8f08e4f..88ce2b6 100644 --- a/configuration/services/webserver.nix +++ b/configuration/services/webserver.nix @@ -1,4 +1,10 @@ -{ config, ... }: +{ + pkgs, + config, + lib, + flake-inputs, + ... +}: let inherit (config.services.nginx) domain; in @@ -8,11 +14,50 @@ in 443 ]; - services.tlaternet-webserver = { - enable = true; - listen = { - addr = "127.0.0.1"; - port = 8000; + systemd.services.tlaternet-webserver = { + description = "tlater.net webserver"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + + script = '' + ${lib.getExe flake-inputs.self.packages.${pkgs.system}.webserver} + ''; + + environment = { + TLATERNET_NTFY_INSTANCE = "https://tlater.net"; + LEPTOS_SITE_ADDR = "127.0.0.1:8000"; + }; + + serviceConfig = { + Type = "exec"; + LoadCredential = "ntfy-topic:/run/secrets/tlaternet/ntfy-topic"; + + DynamicUser = true; + ProtectHome = true; # Override the default (read-only) + PrivateDevices = true; + PrivateIPC = true; + PrivateUsers = true; + ProtectHostname = true; + ProtectClock = true; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + ProtectControlGroups = true; + RestrictAddressFamilies = [ + "AF_UNIX" + "AF_INET" + "AF_INET6" + ]; + RestrictNamespaces = true; + LockPersonality = true; + MemoryDenyWriteExecute = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SystemCallArchitectures = "native"; + SystemCallFilter = [ + "@system-service" + "~@privileged @resources @setuid @keyring" + ]; }; }; @@ -28,6 +73,11 @@ in useACMEHost = "tlater.net"; enableHSTS = true; - locations."/".proxyPass = "http://${addr}:${toString port}"; + locations."/".proxyPass = + "http://${config.systemd.services.tlaternet-webserver.environment.LEPTOS_SITE_ADDR}"; }; + + sops.secrets = { + "tlaternet/ntfy-topic" = { }; + }; } diff --git a/flake.lock b/flake.lock index 05a6a5b..3c65247 100644 --- a/flake.lock +++ b/flake.lock @@ -136,50 +136,6 @@ "type": "github" } }, - "dream2nix": { - "inputs": { - "nixpkgs": [ - "nixpkgs" - ], - "purescript-overlay": "purescript-overlay", - "pyproject-nix": "pyproject-nix" - }, - "locked": { - "lastModified": 1735160684, - "narHash": "sha256-n5CwhmqKxifuD4Sq4WuRP/h5LO6f23cGnSAuJemnd/4=", - "owner": "nix-community", - "repo": "dream2nix", - "rev": "8ce6284ff58208ed8961681276f82c2f8f978ef4", - "type": "github" - }, - "original": { - "owner": "nix-community", - "repo": "dream2nix", - "type": "github" - } - }, - "fenix": { - "inputs": { - "nixpkgs": [ - "tlaternet-webserver", - "nixpkgs" - ], - "rust-analyzer-src": "rust-analyzer-src" - }, - "locked": { - "lastModified": 1737181903, - "narHash": "sha256-lvp77MhGzSN+ICd0MugppCjQR6cmlM2iAC5cjy2ZsaA=", - "owner": "nix-community", - "repo": "fenix", - "rev": "ac79bb490b8c1af4bbc587b84c76f9527d6b14f7", - "type": "github" - }, - "original": { - "owner": "nix-community", - "repo": "fenix", - "type": "github" - } - }, "flake-compat": { "flake": false, "locked": { @@ -310,6 +266,19 @@ "url": "https://channels.nixos.org/nixos-25.05-small/nixexprs.tar.xz" } }, + "nixpkgs-unstable": { + "locked": { + "lastModified": 1763835633, + "narHash": "sha256-nzRnw0UkYQpDm0o20AKvG/5oHCXy5qEGOsFAVhB5NmA=", + "rev": "050e09e091117c3d7328c7b2b7b577492c43c134", + "type": "tarball", + "url": "https://releases.nixos.org/nixos/unstable/nixos-25.11pre900642.050e09e09111/nixexprs.tar.xz?lastModified=1763835633&rev=050e09e091117c3d7328c7b2b7b577492c43c134" + }, + "original": { + "type": "tarball", + "url": "https://channels.nixos.org/nixos-unstable/nixexprs.tar.xz" + } + }, "pre-commit-hooks": { "inputs": { "flake-compat": [ @@ -349,50 +318,6 @@ "type": "github" } }, - "purescript-overlay": { - "inputs": { - "flake-compat": [ - "deploy-rs", - "flake-compat" - ], - "nixpkgs": [ - "tlaternet-webserver", - "dream2nix", - "nixpkgs" - ], - "slimlock": "slimlock" - }, - "locked": { - "lastModified": 1728546539, - "narHash": "sha256-Sws7w0tlnjD+Bjck1nv29NjC5DbL6nH5auL9Ex9Iz2A=", - "owner": "thomashoneyman", - "repo": "purescript-overlay", - "rev": "4ad4c15d07bd899d7346b331f377606631eb0ee4", - "type": "github" - }, - "original": { - "owner": "thomashoneyman", - "repo": "purescript-overlay", - "type": "github" - } - }, - "pyproject-nix": { - "flake": false, - "locked": { - "lastModified": 1702448246, - "narHash": "sha256-hFg5s/hoJFv7tDpiGvEvXP0UfFvFEDgTdyHIjDVHu1I=", - "owner": "davhau", - "repo": "pyproject.nix", - "rev": "5a06a2697b228c04dd2f35659b4b659ca74f7aeb", - "type": "github" - }, - "original": { - "owner": "davhau", - "ref": "dream2nix", - "repo": "pyproject.nix", - "type": "github" - } - }, "root": { "inputs": { "deploy-rs": "deploy-rs", @@ -400,49 +325,9 @@ "flint": "flint", "foundryvtt": "foundryvtt", "nixpkgs": "nixpkgs", + "nixpkgs-unstable": "nixpkgs-unstable", "sonnenshift": "sonnenshift", - "sops-nix": "sops-nix", - "tlaternet-webserver": "tlaternet-webserver" - } - }, - "rust-analyzer-src": { - "flake": false, - "locked": { - "lastModified": 1737140097, - "narHash": "sha256-m4SN8DeKzsP10EQFS7+2zgGfCrMhTfTt1H0QRNesD08=", - "owner": "rust-lang", - "repo": "rust-analyzer", - "rev": "f61bfa4d7feb84d07538d361fe77d34a29e3b375", - "type": "github" - }, - "original": { - "owner": "rust-lang", - "ref": "nightly", - "repo": "rust-analyzer", - "type": "github" - } - }, - "slimlock": { - "inputs": { - "nixpkgs": [ - "tlaternet-webserver", - "dream2nix", - "purescript-overlay", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1688756706, - "narHash": "sha256-xzkkMv3neJJJ89zo3o2ojp7nFeaZc2G0fYwNXNJRFlo=", - "owner": "thomashoneyman", - "repo": "slimlock", - "rev": "cf72723f59e2340d24881fd7bf61cb113b4c407c", - "type": "github" - }, - "original": { - "owner": "thomashoneyman", - "repo": "slimlock", - "type": "github" + "sops-nix": "sops-nix" } }, "sonnenshift": { @@ -501,28 +386,6 @@ "type": "github" } }, - "tlaternet-webserver": { - "inputs": { - "dream2nix": "dream2nix", - "fenix": "fenix", - "nixpkgs": [ - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1737271785, - "narHash": "sha256-yVdaaawYK1/q9V5btfGpxVCQBdyQx1WcFHYO0yX5bP8=", - "ref": "refs/heads/master", - "rev": "5d3d84836101ec9b9867a5f754c9ee1b9d4dc538", - "revCount": 76, - "type": "git", - "url": "https://gitea.tlater.net/tlaternet/tlaternet.git" - }, - "original": { - "type": "git", - "url": "https://gitea.tlater.net/tlaternet/tlaternet.git" - } - }, "utils": { "inputs": { "systems": "systems" diff --git a/flake.nix b/flake.nix index 029fd9a..951eef5 100644 --- a/flake.nix +++ b/flake.nix @@ -3,6 +3,7 @@ inputs = { nixpkgs.url = "https://channels.nixos.org/nixos-25.05-small/nixexprs.tar.xz"; + nixpkgs-unstable.url = "https://channels.nixos.org/nixos-unstable/nixexprs.tar.xz"; ## Nix/OS utilities @@ -30,17 +31,6 @@ ## Services - tlaternet-webserver = { - url = "git+https://gitea.tlater.net/tlaternet/tlaternet.git"; - inputs = { - nixpkgs.follows = "nixpkgs"; - dream2nix.inputs = { - nixpkgs.follows = "nixpkgs"; - purescript-overlay.inputs.flake-compat.follows = "deploy-rs/flake-compat"; - }; - }; - }; - foundryvtt = { url = "github:reckenrode/nix-foundryvtt"; inputs.nixpkgs.follows = "nixpkgs"; @@ -148,7 +138,10 @@ packages.${system} = { default = vm.config.system.build.vm; } - // import ./pkgs { pkgs = nixpkgs.legacyPackages.${system}; }; + // import ./pkgs { + pkgs = nixpkgs.legacyPackages.${system}; + flake-inputs = inputs; + }; ################### # Utility scripts # @@ -184,6 +177,8 @@ minecraft = nixpkgs.legacyPackages.${system}.mkShell { packages = nixpkgs.lib.attrValues { inherit (nixpkgs.legacyPackages.${system}) packwiz; }; }; + + webserver = self.packages.${system}.webserver.devShell; }; }; } diff --git a/keys/staging.yaml b/keys/staging.yaml index dc9059b..cd69d03 100644 --- a/keys/staging.yaml +++ b/keys/staging.yaml @@ -1,3 +1,5 @@ +tlaternet: + ntfy-topic: ENC[AES256_GCM,data:T0wEsXso0uJD4KDe8qv8d24+vaouDrpDFxpSROn5kiYnqa1w6BSy3VrFdqFy17s7mzlAsZtN7gfDZDFjIo1S3Q==,iv:evWlOBgdnVdprzrTy5m+rcRo0OkFEIX+lCgfwLyfw4I=,tag:iq4B6VLWy0nm80ezqJ+rgw==,type:str] porkbun: api-key: ENC[AES256_GCM,data:A5J1sqwq6hs=,iv:77Mar3IX7mq7z7x6s9sSeGNVYc1Wv78HptJElEC7z3Q=,tag:eM/EF9TxKu+zcbJ1SYXiuA==,type:str] secret-api-key: ENC[AES256_GCM,data:8Xv+jWYaWMI=,iv:li4tdY0pch5lksftMmfMVS729caAwfaacoztaQ49az0=,tag:KhfElBGzVH4ByFPfuQsdhw==,type:str] @@ -28,8 +30,8 @@ turn: env: ENC[AES256_GCM,data:xjIz/AY109lyiL5N01p5T3HcYco/rM5CJSRTtg==,iv:16bW6OpyOK/QL0QPGQp/Baa9xyT8E3ZsYkwqmjuofk0=,tag:J5re3uKxIykw3YunvQWBgg==,type:str] secret: ENC[AES256_GCM,data:eQ7dAocoZtg=,iv:fgzjTPv30WqTKlLy+yMn5MsKQgjhPnwlGFFwYEg3gWs=,tag:1ze33U1NBkgMX/9SiaBNQg==,type:str] sops: - lastmodified: "2025-11-19T16:42:43Z" - mac: ENC[AES256_GCM,data:4YivckDS+jBX3Bkon0bTAm3SXya4v2ieZyqeBXjBUYZeCmelIng7bn2dP7791O6RK6RvSXAGhiykWgGRW/boG3QM8VLxDMSRTKovJo5k6oxtFJC8OLDJoh1EC5BQLznJDKl4So6FgYPEtdQ6rx+Q6Ah7JSMtQilxRoe/hYapT90=,iv:9BGtS585gVbvH6l96/YYZiY1DrwB565vPaNNtFC9vbk=,tag:HsZuDMqPFHTMPxQsD36LNQ==,type:str] + lastmodified: "2025-11-27T19:00:56Z" + mac: ENC[AES256_GCM,data:Q8ATaYVuToQaToVgT5JNP6NGipLnu8JifBivD5jG0h/Xb82ObkMHYejy7tcir600+XgC2UJgfRBgEtYbhjK+SQcyvyBpmPWyv1cKfcidwnfDqRMbc8/1LLl/3mK1losv7/51aZDqvu7GuIvOwXh6IyDQldSuyyPf+wcmCCENiLY=,iv:zDGmnEL2659W7JPRG9dlLjtvDtCgz+iXjJt4EPx/OCM=,tag:3X69zX1sZqrZujB7En8jqA==,type:str] pgp: - created_at: "2025-10-03T21:38:26Z" enc: |- diff --git a/pkgs/default.nix b/pkgs/default.nix index 31335a6..1ce8cd2 100644 --- a/pkgs/default.nix +++ b/pkgs/default.nix @@ -1,5 +1,8 @@ -{ pkgs }: +{ pkgs, flake-inputs }: +let + inherit (flake-inputs.nixpkgs-unstable.legacyPackages.${pkgs.system}) ast-grep; +in pkgs.lib.packagesFromDirectoryRecursive { - inherit (pkgs) callPackage; + callPackage = pkgs.lib.callPackageWith (pkgs // { inherit ast-grep; }); directory = ./packages; } diff --git a/pkgs/packages/webserver/.gitignore b/pkgs/packages/webserver/.gitignore new file mode 100644 index 0000000..2f7896d --- /dev/null +++ b/pkgs/packages/webserver/.gitignore @@ -0,0 +1 @@ +target/ diff --git a/pkgs/packages/webserver/Cargo.lock b/pkgs/packages/webserver/Cargo.lock new file mode 100644 index 0000000..a6db6ec --- /dev/null +++ b/pkgs/packages/webserver/Cargo.lock @@ -0,0 +1,3453 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "any_spawner" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1384d3fe1eecb464229fcf6eebb72306591c56bf27b373561489458a7c73027d" +dependencies = [ + "futures", + "thiserror 2.0.17", + "tokio", + "wasm-bindgen-futures", +] + +[[package]] +name = "anyhow" +version = "1.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" + +[[package]] +name = "async-lock" +version = "3.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fd03604047cee9b6ce9de9f70c6cd540a0520c813cbd49bae61f33ab80ed1dc" +dependencies = [ + "event-listener", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "atomic" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89cbf775b137e9b968e67227ef7f775587cde3fd31b0d8599dbd0f598a48340" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "attribute-derive" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05832cdddc8f2650cc2cc187cc2e952b8c133a48eb055f35211f61ee81502d77" +dependencies = [ + "attribute-derive-macro", + "derive-where", + "manyhow", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "attribute-derive-macro" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a7cdbbd4bd005c5d3e2e9c885e6fa575db4f4a3572335b974d8db853b6beb61" +dependencies = [ + "collection_literals", + "interpolator", + "manyhow", + "proc-macro-utils", + "proc-macro2", + "quote", + "quote-use", + "syn", +] + +[[package]] +name = "axum" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b098575ebe77cb6d14fc7f32749631a6e44edbef6b796f89b020e99ba20d425" +dependencies = [ + "axum-core", + "axum-macros", + "base64", + "bytes", + "form_urlencoded", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "itoa", + "matchit", + "memchr", + "mime", + "multer", + "percent-encoding", + "pin-project-lite", + "serde_core", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sha1", + "sync_wrapper", + "tokio", + "tokio-tungstenite", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-core" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59446ce19cd142f8833f856eb31f3eb097812d1479ab224f54d72428ca21ea22" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "sync_wrapper", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-macros" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "604fde5e028fea851ce1d8570bbdc034bec850d157f7569d10f347d06808c05c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bitflags" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" + +[[package]] +name = "bytemuck" +version = "1.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4" + +[[package]] +name = "bytes" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" + +[[package]] +name = "camino" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "276a59bf2b2c967788139340c9f0c5b12d7fd6630315c15c217e559de85d2609" + +[[package]] +name = "cc" +version = "1.2.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd405d82c84ff7f35739f175f67d8b9fb7687a0e84ccdc78bd3568839827cf07" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "codee" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30b61b7a19443f478176473b0c35acf7f9f7e2752c781dea55820343826f366c" +dependencies = [ + "serde", + "serde_json", + "thiserror 2.0.17", +] + +[[package]] +name = "collection_literals" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2550f75b8cfac212855f6b1885455df8eaee8fe8e246b647d69146142e016084" + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "config" +version = "0.15.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b30fa8254caad766fc03cb0ccae691e14bf3bd72bfff27f72802ce729551b3d6" +dependencies = [ + "convert_case 0.6.0", + "pathdiff", + "serde_core", + "toml 0.9.8", + "winnow", +] + +[[package]] +name = "console_error_panic_hook" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +dependencies = [ + "cfg-if", + "wasm-bindgen", +] + +[[package]] +name = "const-str" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "451d0640545a0553814b4c646eb549343561618838e9b42495f466131fe3ad49" + +[[package]] +name = "const_format" +version = "0.2.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7faa7469a93a566e9ccc1c73fe783b4a65c274c5ace346038dca9c39fe0030ad" +dependencies = [ + "const_format_proc_macros", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "const_str_slice_concat" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f67855af358fcb20fac58f9d714c94e2b228fe5694c1c9b4ead4a366343eda1b" + +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "convert_case" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baaaa0ecca5b51987b9423ccdc971514dd8b0bb7b4060b983d3664dad3f1f89f" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "dashmap" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", +] + +[[package]] +name = "data-encoding" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" + +[[package]] +name = "derive-where" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef941ded77d15ca19b40374869ac6000af1c9f2a4c0f3d4c70926287e6364a8f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "drain_filter_polyfill" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "669a445ee724c5c69b1b06fe0b63e70a1c84bc9bb7d9696cd4f4e3ec45050408" + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "either_of" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "216d23e0ec69759a17f05e1c553f3a6870e5ec73420fbb07807a6f34d5d1d5a4" +dependencies = [ + "paste", + "pin-project-lite", +] + +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "erased" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1731451909bde27714eacba19c2566362a7f35224f52b153d3f42cf60f72472" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "event-listener" +version = "5.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" +dependencies = [ + "event-listener", + "pin-project-lite", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "figment" +version = "0.10.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cb01cd46b0cf372153850f4c6c272d9cbea2da513e07538405148f95bd789f3" +dependencies = [ + "atomic", + "pear", + "serde", + "toml 0.8.23", + "uncased", + "version_check", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", + "num_cpus", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi", + "wasip2", + "wasm-bindgen", +] + +[[package]] +name = "gloo-net" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06f627b1a58ca3d42b45d6104bf1e1a03799df472df00988b6ba21accc10580" +dependencies = [ + "futures-channel", + "futures-core", + "futures-sink", + "gloo-utils", + "http", + "js-sys", + "pin-project", + "serde", + "serde_json", + "thiserror 1.0.69", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "gloo-utils" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa" +dependencies = [ + "js-sys", + "serde", + "serde_json", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "guardian" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17e2ac29387b1aa07a1e448f7bb4f35b500787971e965b02842b900afa5c8f6f" + +[[package]] +name = "h2" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + +[[package]] +name = "html-escape" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d1ad449764d627e22bfd7cd5e8868264fc9236e07c752972b4080cd351cb476" +dependencies = [ + "utf8-width", +] + +[[package]] +name = "http" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "http-range-header" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9171a2ea8a68358193d15dd5d70c1c10a2afc3e7e4c5bc92bc9f025cebd7359c" + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hydration_context" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8714ae4adeaa846d838f380fbd72f049197de629948f91bf045329e0cf0a283" +dependencies = [ + "futures", + "js-sys", + "once_cell", + "or_poisoned", + "pin-project-lite", + "serde", + "throw_error", + "wasm-bindgen", +] + +[[package]] +name = "hyper" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "pin-utils", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", + "webpki-roots", +] + +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52e9a2a24dc5c6821e71a7030e1e14b7b632acac55c40e9d2e082c621261bb56" +dependencies = [ + "base64", + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "http", + "http-body", + "hyper", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2", + "system-configuration", + "tokio", + "tower-service", + "tracing", + "windows-registry", +] + +[[package]] +name = "icu_collections" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" + +[[package]] +name = "icu_properties" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e93fcd3157766c0c8da2f8cff6ce651a31f0810eaa1c51ec363ef790bbb5fb99" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02845b3647bb045f1100ecd6480ff52f34c35f82d9880e029d329c21d1054899" + +[[package]] +name = "icu_provider" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "indexmap" +version = "2.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" +dependencies = [ + "equivalent", + "hashbrown 0.16.1", +] + +[[package]] +name = "inlinable_string" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb" + +[[package]] +name = "interpolator" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71dd52191aae121e8611f1e8dc3e324dd0dd1dee1e6dd91d10ee07a3cfb4d9d8" + +[[package]] +name = "inventory" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc61209c082fbeb19919bee74b176221b27223e27b65d781eb91af24eb1fb46e" +dependencies = [ + "rustversion", +] + +[[package]] +name = "ipnet" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" + +[[package]] +name = "iri-string" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f867b9d1d896b67beb18518eda36fdb77a32ea590de864f1325b294a6d14397" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "leptos" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c202a7897aa73c90ac4ce73713512d871f75e79847558d9e88f778659b164dc" +dependencies = [ + "any_spawner", + "base64", + "cfg-if", + "either_of", + "futures", + "getrandom 0.3.4", + "hydration_context", + "leptos_config", + "leptos_dom", + "leptos_hot_reload", + "leptos_macro", + "leptos_server", + "oco_ref", + "or_poisoned", + "paste", + "rand", + "reactive_graph", + "rustc-hash", + "rustc_version", + "send_wrapper", + "serde", + "serde_qs", + "server_fn", + "slotmap", + "tachys", + "thiserror 2.0.17", + "throw_error", + "typed-builder", + "typed-builder-macro", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "leptos_axum" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a84872a7df15ec047f8b27b27e35354752a8cc62ab8b789e3dc9d4ed5613bcea" +dependencies = [ + "any_spawner", + "axum", + "dashmap", + "futures", + "hydration_context", + "leptos", + "leptos_integration_utils", + "leptos_macro", + "leptos_meta", + "leptos_router", + "parking_lot", + "server_fn", + "tachys", + "tokio", + "tower", + "tower-http", +] + +[[package]] +name = "leptos_config" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74eec2103dfa808f4b13c149dfbd3842f13a5948489fda3de31cc565fb28dbec" +dependencies = [ + "config", + "regex", + "serde", + "thiserror 2.0.17", + "typed-builder", +] + +[[package]] +name = "leptos_dom" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25b845379c33884f0dead9abb5aa3d258d7bd507789fc2527a5972f82c0757c7" +dependencies = [ + "js-sys", + "or_poisoned", + "reactive_graph", + "send_wrapper", + "tachys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "leptos_hot_reload" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fae732329192df886803f076515d73c883166a4c8cbc5532584d0d1e43539300" +dependencies = [ + "anyhow", + "camino", + "indexmap", + "parking_lot", + "proc-macro2", + "quote", + "rstml", + "serde", + "syn", + "walkdir", +] + +[[package]] +name = "leptos_integration_utils" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51059fee81537ba299323e2b58c9d721f66fd8a2d42df710a020efbdfb8efae2" +dependencies = [ + "futures", + "hydration_context", + "leptos", + "leptos_config", + "leptos_meta", + "leptos_router", + "reactive_graph", +] + +[[package]] +name = "leptos_macro" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e32ae8783d4b64838167e026ef773dbc53399e9e6658e9c2f65e0ce67a5ccec" +dependencies = [ + "attribute-derive", + "cfg-if", + "convert_case 0.8.0", + "html-escape", + "itertools", + "leptos_hot_reload", + "prettyplease", + "proc-macro-error2", + "proc-macro2", + "quote", + "rstml", + "rustc_version", + "server_fn_macro", + "syn", + "uuid", +] + +[[package]] +name = "leptos_meta" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef0521aeb2e73cbae8e6278287da485020b971e0b2cd471c5de5eb8cbcaa8802" +dependencies = [ + "futures", + "indexmap", + "leptos", + "or_poisoned", + "send_wrapper", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "leptos_router" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1b6df1b107c301bd39c114768a1196a8dfcbdae869364b3f0247e3f554b503a" +dependencies = [ + "any_spawner", + "either_of", + "futures", + "gloo-net", + "js-sys", + "leptos", + "leptos_router_macro", + "or_poisoned", + "percent-encoding", + "reactive_graph", + "rustc_version", + "send_wrapper", + "tachys", + "thiserror 2.0.17", + "url", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "leptos_router_macro" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d35d4bb7922e74546fac3e852b0f3252e9996e04fca12c359aba1e5958cfda8" +dependencies = [ + "proc-macro-error2", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "leptos_server" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26851048e161998b8f9fb3261a833ec64df2a2669ea95f360c54b7f47a1e07cb" +dependencies = [ + "any_spawner", + "base64", + "codee", + "futures", + "hydration_context", + "or_poisoned", + "reactive_graph", + "send_wrapper", + "serde", + "serde_json", + "server_fn", + "tachys", +] + +[[package]] +name = "libc" +version = "0.2.177" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" + +[[package]] +name = "linear-map" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfae20f6b19ad527b550c223fddc3077a547fc70cda94b9b566575423fd303ee" + +[[package]] +name = "linux-raw-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" + +[[package]] +name = "litemap" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" + +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + +[[package]] +name = "manyhow" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b33efb3ca6d3b07393750d4030418d594ab1139cee518f0dc88db70fec873587" +dependencies = [ + "manyhow-macros", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "manyhow-macros" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46fce34d199b78b6e6073abf984c9cf5fd3e9330145a93ee0738a7443e371495" +dependencies = [ + "proc-macro-utils", + "proc-macro2", + "quote", +] + +[[package]] +name = "markdown_view_leptos" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f75ce57ac1fcfcb0e31a39dd92f2c7f529c9840670b47d2516b4c037fd00689" +dependencies = [ + "proc-macro2", + "pulldown-cmark", + "quote", + "reqwest", + "syn", +] + +[[package]] +name = "matchit" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" + +[[package]] +name = "memchr" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "mio" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "multer" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83e87776546dc87511aa5ee218730c92b666d7264ab6ed41f9d215af9cd5224b" +dependencies = [ + "bytes", + "encoding_rs", + "futures-util", + "http", + "httparse", + "memchr", + "mime", + "spin", + "version_check", +] + +[[package]] +name = "native-tls" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "next_tuple" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60993920e071b0c9b66f14e2b32740a4e27ffc82854dcd72035887f336a09a28" + +[[package]] +name = "num_cpus" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "oco_ref" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64b94982fe39a861561cf67ff17a7849f2cedadbbad960a797634032b7abb998" +dependencies = [ + "serde", + "thiserror 1.0.69", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "openssl" +version = "0.10.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" + +[[package]] +name = "openssl-sys" +version = "0.9.111" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "or_poisoned" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c04f5d74368e4d0dfe06c45c8627c81bd7c317d52762d118fb9b3076f6420fd" + +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pathdiff" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" + +[[package]] +name = "pear" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdeeaa00ce488657faba8ebf44ab9361f9365a97bd39ffb8a60663f57ff4b467" +dependencies = [ + "inlinable_string", + "pear_codegen", + "yansi", +] + +[[package]] +name = "pear_codegen" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bab5b985dc082b345f812b7df84e1bef27e7207b39e448439ba8bd69c93f147" +dependencies = [ + "proc-macro2", + "proc-macro2-diagnostics", + "quote", + "syn", +] + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pin-project" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "potential_utf" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +dependencies = [ + "zerovec", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "proc-macro-utils" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eeaf08a13de400bc215877b5bdc088f241b12eb42f0a548d3390dc1c56bb7071" +dependencies = [ + "proc-macro2", + "quote", + "smallvec", +] + +[[package]] +name = "proc-macro2" +version = "1.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proc-macro2-diagnostics" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "version_check", + "yansi", +] + +[[package]] +name = "pulldown-cmark" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e8bbe1a966bd2f362681a44f6edce3c2310ac21e4d5067a6e7ec396297a6ea0" +dependencies = [ + "bitflags", + "memchr", + "pulldown-cmark-escape", + "unicase", +] + +[[package]] +name = "pulldown-cmark-escape" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "007d8adb5ddab6f8e3f491ac63566a7d5002cc7ed73901f72057943fa71ae1ae" + +[[package]] +name = "quinn" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2", + "thiserror 2.0.17", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" +dependencies = [ + "bytes", + "getrandom 0.3.4", + "lru-slab", + "rand", + "ring", + "rustc-hash", + "rustls", + "rustls-pki-types", + "slab", + "thiserror 2.0.17", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.60.2", +] + +[[package]] +name = "quote" +version = "1.0.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "quote-use" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9619db1197b497a36178cfc736dc96b271fe918875fbf1344c436a7e93d0321e" +dependencies = [ + "quote", + "quote-use-macros", +] + +[[package]] +name = "quote-use-macros" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82ebfb7faafadc06a7ab141a6f67bcfb24cb8beb158c6fe933f2f035afa99f35" +dependencies = [ + "proc-macro-utils", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.4", +] + +[[package]] +name = "reactive_graph" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c79f7561c7c246e2c2feab3b0b63502b213e589d7768032bbfc5ea7b267140b9" +dependencies = [ + "any_spawner", + "async-lock", + "futures", + "guardian", + "hydration_context", + "or_poisoned", + "pin-project-lite", + "rustc-hash", + "rustc_version", + "send_wrapper", + "serde", + "slotmap", + "thiserror 2.0.17", + "web-sys", +] + +[[package]] +name = "reactive_stores" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9edd67f166cfec4ffc6abf271bada268c13dac0b0318f4c6a76a877490d0617a" +dependencies = [ + "dashmap", + "guardian", + "itertools", + "or_poisoned", + "paste", + "reactive_graph", + "reactive_stores_macro", + "rustc-hash", + "send_wrapper", +] + +[[package]] +name = "reactive_stores_macro" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2418b703f385f35481737d8f2ccd9ae3de850dd778bce09817f30338f3cb5ca2" +dependencies = [ + "convert_case 0.8.0", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" + +[[package]] +name = "reqwest" +version = "0.12.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d0946410b9f7b082a427e4ef5c8ff541a88b357bc6c637c40db3a68ac70a36f" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-tls", + "hyper-util", + "js-sys", + "log", + "mime", + "native-tls", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-native-tls", + "tokio-rustls", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.16", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rstml" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61cf4616de7499fc5164570d40ca4e1b24d231c6833a88bff0fe00725080fd56" +dependencies = [ + "derive-where", + "proc-macro2", + "proc-macro2-diagnostics", + "quote", + "syn", + "syn_derive", + "thiserror 2.0.17", +] + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls" +version = "0.23.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f" +dependencies = [ + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pki-types" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94182ad936a0c91c324cd46c6511b9510ed16af436d7b5bab34beab0afd55f7a" +dependencies = [ + "web-time", + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" + +[[package]] +name = "send_wrapper" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" +dependencies = [ + "futures-core", +] + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.145" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", + "serde_core", +] + +[[package]] +name = "serde_path_to_error" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" +dependencies = [ + "itoa", + "serde", + "serde_core", +] + +[[package]] +name = "serde_qs" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3faaf9e727533a19351a43cc5a8de957372163c7d35cc48c90b75cdda13c352" +dependencies = [ + "percent-encoding", + "serde", + "thiserror 2.0.17", +] + +[[package]] +name = "serde_spanned" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_spanned" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e24345aa0fe688594e73770a5f6d1b216508b4f93484c0026d521acd30134392" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "server_fn" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c27fbd25ecc066481e383e2ed62ab2480e708aa3fe46cba36e95f58e61dfd04" +dependencies = [ + "axum", + "base64", + "bytes", + "const-str", + "const_format", + "dashmap", + "futures", + "gloo-net", + "http", + "http-body-util", + "hyper", + "inventory", + "js-sys", + "pin-project-lite", + "rustc_version", + "rustversion", + "send_wrapper", + "serde", + "serde_json", + "serde_qs", + "server_fn_macro_default", + "thiserror 2.0.17", + "throw_error", + "tokio", + "tower", + "tower-layer", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "xxhash-rust", +] + +[[package]] +name = "server_fn_macro" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "950b8cfc9ff5f39ca879c5a7c5e640de2695a199e18e424c3289d0964cabe642" +dependencies = [ + "const_format", + "convert_case 0.8.0", + "proc-macro2", + "quote", + "rustc_version", + "syn", + "xxhash-rust", +] + +[[package]] +name = "server_fn_macro_default" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63eb08f80db903d3c42f64e60ebb3875e0305be502bdc064ec0a0eab42207f00" +dependencies = [ + "server_fn_macro", + "syn", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "slab" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" + +[[package]] +name = "slotmap" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a" +dependencies = [ + "version_check", +] + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "socket2" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" +dependencies = [ + "libc", + "windows-sys 0.60.2", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.110" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn_derive" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb066a04799e45f5d582e8fc6ec8e6d6896040d00898eb4e6a835196815b219" +dependencies = [ + "proc-macro-error2", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "system-configuration" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +dependencies = [ + "bitflags", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tachys" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d706b2ab0fa7e14b878d638faee3dafed08a2ab9ff7f116ad2445cce04bfaaa" +dependencies = [ + "any_spawner", + "async-trait", + "const_str_slice_concat", + "drain_filter_polyfill", + "either_of", + "erased", + "futures", + "html-escape", + "indexmap", + "itertools", + "js-sys", + "linear-map", + "next_tuple", + "oco_ref", + "or_poisoned", + "parking_lot", + "paste", + "reactive_graph", + "reactive_stores", + "rustc-hash", + "rustc_version", + "send_wrapper", + "slotmap", + "throw_error", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "tempfile" +version = "3.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" +dependencies = [ + "fastrand", + "getrandom 0.3.4", + "once_cell", + "rustix", + "windows-sys 0.61.2", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +dependencies = [ + "thiserror-impl 2.0.17", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "throw_error" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41e42a6afdde94f3e656fae18f837cb9bbe500a5ac5de325b09f3ec05b9c28e3" +dependencies = [ + "pin-project-lite", +] + +[[package]] +name = "tinystr" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tlaternet-webserver" +version = "0.2.0" +dependencies = [ + "axum", + "console_error_panic_hook", + "figment", + "leptos", + "leptos_axum", + "leptos_meta", + "leptos_router", + "markdown_view_leptos", + "reqwest", + "serde", + "thiserror 2.0.17", + "tokio", + "url", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "tokio" +version = "1.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" +dependencies = [ + "bytes", + "libc", + "mio", + "pin-project-lite", + "socket2", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25a406cddcc431a75d3d9afc6a7c0f7428d4891dd973e4d54c56b46127bf857" +dependencies = [ + "futures-util", + "log", + "tokio", + "tungstenite", +] + +[[package]] +name = "tokio-util" +version = "0.7.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" +dependencies = [ + "serde", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", + "toml_edit", +] + +[[package]] +name = "toml" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0dc8b1fb61449e27716ec0e1bdf0f6b8f3e8f6b05391e8497b8b6d7804ea6d8" +dependencies = [ + "serde_core", + "serde_spanned 1.0.3", + "toml_datetime 0.7.3", + "toml_parser", + "winnow", +] + +[[package]] +name = "toml_datetime" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_datetime" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.22.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +dependencies = [ + "indexmap", + "serde", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", + "toml_write", + "winnow", +] + +[[package]] +name = "toml_parser" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e" +dependencies = [ + "winnow", +] + +[[package]] +name = "toml_write" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" + +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-http" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" +dependencies = [ + "bitflags", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-body-util", + "http-range-header", + "httpdate", + "iri-string", + "mime", + "mime_guess", + "percent-encoding", + "pin-project-lite", + "tokio", + "tokio-util", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "log", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "tungstenite" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442" +dependencies = [ + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "rand", + "sha1", + "thiserror 2.0.17", + "utf-8", +] + +[[package]] +name = "typed-builder" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fef81aec2ca29576f9f6ae8755108640d0a86dd3161b2e8bca6cfa554e98f77d" +dependencies = [ + "typed-builder-macro", +] + +[[package]] +name = "typed-builder-macro" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ecb9ecf7799210407c14a8cfdfe0173365780968dc57973ed082211958e0b18" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + +[[package]] +name = "uncased" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1b88fcfe09e89d3866a5c11019378088af2d24c3fbd4f0543f96b479ec90697" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicase" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" + +[[package]] +name = "unicode-ident" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" + +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "utf8-width" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86bd8d4e895da8537e5315b8254664e6b769c4ff3db18321b297a1e7004392e3" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "uuid" +version = "1.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" +dependencies = [ + "getrandom 0.3.4", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.1+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" +dependencies = [ + "cfg-if", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-streams" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "web-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-roots" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2878ef029c47c6e8cf779119f20fcf52bde7ad42a731b2a304bc221df17571e" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-registry" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" +dependencies = [ + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + +[[package]] +name = "winnow" +version = "0.7.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" +dependencies = [ + "memchr", +] + +[[package]] +name = "wit-bindgen" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" + +[[package]] +name = "writeable" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" + +[[package]] +name = "xxhash-rust" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdd20c5420375476fbd4394763288da7eb0cc0b8c11deed431a91562af7335d3" + +[[package]] +name = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" + +[[package]] +name = "yoke" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43fa6694ed34d6e57407afbccdeecfa268c470a7d2a5b0cf49ce9fcc345afb90" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c640b22cd9817fae95be82f0d2f90b11f7605f6c319d16705c459b27ac2cbc26" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" + +[[package]] +name = "zerotrie" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/pkgs/packages/webserver/Cargo.toml b/pkgs/packages/webserver/Cargo.toml new file mode 100644 index 0000000..6b53991 --- /dev/null +++ b/pkgs/packages/webserver/Cargo.toml @@ -0,0 +1,68 @@ +[package] +name = "tlaternet-webserver" +version = "0.2.0" +edition = "2024" + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +axum = { version = "0.8.7", features = ["macros"], optional = true } +console_error_panic_hook = { version = "0.1.7", optional = true } +figment = { version = "0.10.19", features = ["toml", "env"] } +leptos = "0.8.3" +leptos_axum = { version = "0.8.3", optional = true } +leptos_meta = "0.8.3" +leptos_router = "0.8.3" +markdown_view_leptos = "0.1.3" +reqwest = "0.12.24" +serde = { version = "1.0.228", features = ["derive"] } +thiserror = "2.0.17" +tokio = { version = "1.48.0", features = ["rt-multi-thread"], optional = true } +url = "2.5.7" +wasm-bindgen = { version = "=0.2.100", optional = true } +web-sys = "^0.3.77" + +[features] +hydrate = [ + "leptos/hydrate", + "dep:console_error_panic_hook", + "dep:wasm-bindgen", +] +ssr = [ + "dep:axum", + "dep:tokio", + "dep:leptos_axum", + "leptos/ssr", + "leptos_meta/ssr", + "leptos_router/ssr", +] + +[profile.wasm-release] +inherits = "release" +opt-level = 'z' +lto = true +codegen-units = 1 +panic = "abort" + +[package.metadata.leptos] +output-name = "tlaternet-webserver" +site-root = "target/site" +site-pkg-dir = "pkg" +style-file = "style/main.scss" +site-addr = "127.0.0.1:3000" +reload-port = 3001 +browserquery = "defaults" +env = "DEV" +bin-features = ["ssr"] +bin-default-features = false +lib-features = ["hydrate"] +lib-default-features = false +lib-profile-release = "wasm-release" +watch-additional-files = ["config.toml"] + +# [Optional] Command to use when running end2end tests. It will run in the end2end dir. +# [Windows] for non-WSL use "npx.cmd playwright test" +# This binary name can be checked in Powershell with Get-Command npx +end2end-cmd = "npx playwright test" +end2end-dir = "end2end" diff --git a/pkgs/packages/webserver/config.toml b/pkgs/packages/webserver/config.toml new file mode 100644 index 0000000..b346745 --- /dev/null +++ b/pkgs/packages/webserver/config.toml @@ -0,0 +1,3 @@ +# Can be specified with systemd creds instead +credentials_directory = "dev-creds" +ntfy_instance = "https://ntfy.sh" diff --git a/pkgs/packages/webserver/leptosfmt.toml b/pkgs/packages/webserver/leptosfmt.toml new file mode 100644 index 0000000..f6350a7 --- /dev/null +++ b/pkgs/packages/webserver/leptosfmt.toml @@ -0,0 +1,5 @@ +max_width = 100 +tab_spaces = 2 +attr_value_brace_style = "WhenRequired" +macro_names = [ "leptos::view", "view" ] +closing_tag_style = "SelfClosing" diff --git a/pkgs/packages/webserver/package.nix b/pkgs/packages/webserver/package.nix new file mode 100644 index 0000000..00ebf58 --- /dev/null +++ b/pkgs/packages/webserver/package.nix @@ -0,0 +1,295 @@ +{ + lib, + stdenvNoCC, + fetchFromGitHub, + fetchurl, + symlinkJoin, + makeBinaryWrapper, + + pkg-config, + openssl, + cargo-leptos, + dart-sass, + rustPlatform, + wasm-bindgen-cli_0_2_100, + binaryen, + inkscape, + + mkShell, + clangStdenv, + rust-analyzer, + rustc, + rustfmt, + leptosfmt, + cargo, + clippy, + + writers, + ast-grep, + nix-prefetch-github, +}: +let + cargoMetadata = lib.pipe ./Cargo.toml [ + builtins.readFile + builtins.fromTOML + ]; + + sass-dependencies = { + bulma = stdenvNoCC.mkDerivation (drv: { + pname = "bulma"; + version = "1.0.4"; + + src = fetchFromGitHub { + owner = "jgthms"; + repo = "bulma"; + rev = drv.version; + hash = "sha256-hlejqBI6ayzhm15IymrzhTevkl3xffMfdTasZ2CmAas="; + }; + + installPhase = '' + mkdir -p $out/node_modules/bulma/ + cp -r ./sass $out/node_modules/bulma/ + ''; + }); + + fontsource-scss = stdenvNoCC.mkDerivation { + pname = "fontsource-scss"; + version = "0.2.2"; + + src = fetchurl { + url = "https://registry.npmjs.org/@fontsource-utils/scss/-/scss-0.2.2.tgz"; + hash = "sha256-2BkCBhh01kZfMHhjHMMLDtUeesi7Uy7eMoeM1BAqX38="; + }; + + installPhase = '' + mkdir -p $out/node_modules/@fontsource-utils/scss/ + cp -r . $out/node_modules/@fontsource-utils/scss/ + ''; + }; + + fontsource-nunito = stdenvNoCC.mkDerivation { + pname = "fontsource-nunito"; + version = "5.2.7"; + + src = fetchurl { + url = "https://registry.npmjs.org/@fontsource-variable/nunito/-/nunito-5.2.7.tgz"; + hash = "sha256-xSt1sDpVL/hVYzffKTgN/t7uLI3JadDWtTfWow2jiPM="; + }; + + outputs = [ + "out" + "assets" + ]; + + installPhase = '' + mkdir -p $out/node_modules/@fontsource-variable/nunito/ + cp -r . $out/node_modules/@fontsource-variable/nunito/ + + mkdir -p $assets/@fontsource-variable/ + cp -r files/ $assets/@fontsource-variable/nunito + ''; + }; + + fontsource-arimo = stdenvNoCC.mkDerivation { + pname = "fontsource-nunito"; + version = "5.2.8"; + + src = fetchurl { + url = "https://registry.npmjs.org/@fontsource-variable/arimo/-/arimo-5.2.8.tgz"; + hash = "sha256-jD1IGqy02j4bqMRAwbCgiIz/h97WPrTSd3eZ09nptHA="; + }; + + outputs = [ + "out" + "assets" + ]; + + installPhase = '' + mkdir -p $out/node_modules/@fontsource-variable/arimo/ + cp -r . $out/node_modules/@fontsource-variable/arimo/ + + mkdir -p $assets/@fontsource-variable/ + cp -r files/ $assets/@fontsource-variable/arimo + ''; + }; + }; + + other-dependencies = { + hack-font = stdenvNoCC.mkDerivation (drv: { + pname = "hack-font"; + version = "3.003"; + + src = fetchFromGitHub { + owner = "source-foundry"; + repo = "Hack"; + rev = "v${drv.version}"; + hash = "sha256-qGDtBvKecdfsleUBfXFezllz9Op679a030Qcj/oBs1o="; + }; + + dontBuild = true; + + installPhase = '' + mkdir -p $out + cp -r build/web/fonts $out/hack-font/ + ''; + }); + }; + + icons = stdenvNoCC.mkDerivation { + pname = "tlaternet-icons"; + version = "1.0"; + + src = ./public/icon.svg; + + dontUnpack = true; + + nativeBuildInputs = [ inkscape ]; + + buildPhase = '' + inkscape -w 48 -h 48 $src --export-filename=favicon-48.png + ''; + + installPhase = '' + mkdir -p $out + cp $src $out/icon.svg + cp favicon-48.png $out/ + ''; + }; + + assets = symlinkJoin { + name = "assets-${cargoMetadata.package.name}"; + paths = [ + sass-dependencies.fontsource-arimo.assets + sass-dependencies.fontsource-nunito.assets + other-dependencies.hack-font + icons + ]; + }; + + # Hack to allow importing sass *without* resorting to using `npm` + # and dealing with the insanity that is `package.json` files. + # + # dart-sass *in theory* supports completely arbitrary logic for + # package importing via the `pkg:` url prefix, but unfortunately + # currently the only implementation of it that is available in the + # upstream binary *requires* using a `node_modules` file in the + # repository root. See here: + # https://sass-lang.com/documentation/at-rules/use/#node-js-package-importer + # + # This wouldn't be so bad if it supported an environment variable or + # command line arg to specify what the repo root should be, but it + # doesn't; so instead we use the load-path to specify a package + # import root. + # + # As a consequence, we cannot use the `pkg:` prefix, so package + # imports are indistinguishable from relative path imports. This + # isn't the end of the world, but: + # + # TODO(tlater): See if we can talk to upstream about an + # implementation better suited for use with nix, perhaps in + # dart-sass (add an env variable?) or in cargo-leptos (add a config + # option that can also be set with an env variable, and use the sass + # protocol instead of the raw exe?). + dart-sass-with-packages = + let + packages = symlinkJoin { + name = "sass-packages"; + paths = lib.attrValues sass-dependencies; + stripPrefix = "/node_modules"; + }; + in + symlinkJoin { + inherit (dart-sass) version; + pname = "dart-sass-with-packages"; + + paths = [ dart-sass ]; + nativeBuildInputs = [ makeBinaryWrapper ]; + + postBuild = '' + wrapProgram $out/bin/sass \ + --add-flag --load-path=${packages} + ''; + }; +in +rustPlatform.buildRustPackage (drv: { + inherit (cargoMetadata.package) version; + pname = cargoMetadata.package.name; + cargoLock.lockFile = drv.src + /Cargo.lock; + + src = lib.fileset.toSource { + root = ./.; + fileset = lib.fileset.fromSource (lib.sources.cleanSource ./.); + }; + + nativeBuildInputs = [ + cargo-leptos + rustc.llvmPackages.lld + dart-sass-with-packages + makeBinaryWrapper + pkg-config + wasm-bindgen-cli_0_2_100 + binaryen + ]; + + buildInputs = [ openssl ]; + + LEPTOS_ASSETS_DIR = assets.outPath; + + buildPhase = '' + runHook preBuild + cargo leptos build --release + runHook postBuild + ''; + + installPhase = '' + runHook preInstall + + mkdir -p $out/bin $out/share + cp target/release/tlaternet-webserver $out/bin + cp -r target/site $out/share + wrapProgram $out/bin/tlaternet-webserver \ + --set LEPTOS_SITE_ROOT $out/share/site \ + --set LEPTOS_ASSETS_DIR ${assets.outPath} + + runHook postInstall + ''; + + meta.mainProgram = "tlaternet-webserver"; + + passthru = { + dependencies = sass-dependencies; + + devShell = mkShell.override { stdenv = clangStdenv; } { + packages = [ + pkg-config + openssl + cargo-leptos + dart-sass-with-packages + # lld is exposed as ld by the clangStdenv, adding it + # explicitly with bintools makes it work + rustc.llvmPackages.lld + + rust-analyzer + rustc + rustfmt + leptosfmt + cargo + clippy + ]; + + LEPTOS_ASSETS_DIR = assets.outPath; + }; + + updateScript = writers.writeNuBin "update-${cargoMetadata.package.name}" { + makeWrapperArgs = [ + "--prefix" + "PATH" + ":" + (lib.makeBinPath [ + ast-grep + nix-prefetch-github + ]) + ]; + } ./update.nu; + }; +}) diff --git a/pkgs/packages/webserver/public/icon.svg b/pkgs/packages/webserver/public/icon.svg new file mode 100644 index 0000000..84ef6b3 --- /dev/null +++ b/pkgs/packages/webserver/public/icon.svg @@ -0,0 +1,78 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + diff --git a/pkgs/packages/webserver/src/app.rs b/pkgs/packages/webserver/src/app.rs new file mode 100644 index 0000000..913c640 --- /dev/null +++ b/pkgs/packages/webserver/src/app.rs @@ -0,0 +1,56 @@ +use leptos::prelude::*; +use leptos_meta::{provide_meta_context, MetaTags, Stylesheet}; +use leptos_router::{ + components::{Route, Router, Routes}, + StaticSegment, +}; + +mod homepage; +mod mail; + +use crate::components::Navbar; +use homepage::HomePage; +use mail::Mail; + +pub fn shell(options: LeptosOptions) -> impl IntoView { + view! { + + + + + + + + + + + + + + + + + + } +} + +#[component] +pub fn App() -> impl IntoView { + provide_meta_context(); + + view! { + + + + + // content for this welcome page + +
+ + + + +
+
+ } +} diff --git a/pkgs/packages/webserver/src/app/homepage.rs b/pkgs/packages/webserver/src/app/homepage.rs new file mode 100644 index 0000000..d12ec59 --- /dev/null +++ b/pkgs/packages/webserver/src/app/homepage.rs @@ -0,0 +1,70 @@ +use leptos::prelude::*; +use leptos_meta::{Meta, Title}; +use markdown_view_leptos::markdown_view; + +#[component] +pub fn HomePage() -> impl IntoView { + view! { + + + + <section class="section"> + <div class="container"> + <h1 class="title has-text-weight-normal is-family-monospace"> + "$ "<span id="typed-welcome" /> + </h1> + + <hr /> + + <div class="columns"> + <div class="column content"> + {markdown_view!( + r#" +### About Me + +Looks like you found my website. I suppose introductions are in order. + +My name's Tristan, I'm an avid Dutch-South African software +engineer. You probably either met me at an open source conference, a +hackathon, a badminton session or at a roleplaying table. +"# + )} + </div> + <div class="column content"> + {markdown_view!( + r#"### My Work + +I'm interested in a variety of things in the open source +world. Perhaps thanks to my pursuit of the perfect Linux desktop, this +has revolved a lot around reproducible build and deployment systems +for the last few years, initially starting with +[BuildStream](https://buildstream.build/) back in ~2017. I gave a +couple of talks on it at build meetups in the UK in subsequent years, +though sadly most evidence of that appears to have disappeared. + +Since then this has culminated in a strong fondness for +[NixOS](https://nixos.org/) and Nix, as its active community makes +private use cases much more feasible. As such, I have a vested +interest in making this community as large as possible - I post a lot +on the NixOS [discourse](https://discourse.nixos.org/) trying to help +newcomers out where I can. + +I also just enjoy Programming, my core languages for personal work are +currently probably Rust and Python, although I have a very varied +background. This is in part due to my former work as a consultant, +which required new languages every few months. I have experience from +JavaScript over Elm to Kotlin, but eventually I hope I might only need +to write Rust ;) + +If you're interested in seeing these things for yourself, visit my +[Gitlab](https://gitlab.com/tlater) and +[GitHub](https://github.com/tlater) pages. +"# + )} + + </div> + </div> + </div> + </section> + } +} diff --git a/pkgs/packages/webserver/src/app/mail.rs b/pkgs/packages/webserver/src/app/mail.rs new file mode 100644 index 0000000..b2b3159 --- /dev/null +++ b/pkgs/packages/webserver/src/app/mail.rs @@ -0,0 +1,217 @@ +use leptos::{html::Form, logging}; +use leptos_meta::{Meta, Title}; +use leptos::{prelude::*, server_fn::codec::JsonEncoding}; +use markdown_view_leptos::markdown_view; +use serde::{Deserialize, Serialize}; +use thiserror::Error; + +#[server] +async fn submit_mail(mail: String, subject: String, message: String) -> Result<String, MailError> { + use crate::AppState; + const NTFY_TOPIC_CREDENTIAL_NAME: &str = "ntfy-topic"; + + let mail = format!("From: {}\nSubject: {}\n{}", mail, subject, message); + logging::log!("{}", mail); + + let state = use_context::<AppState>().unwrap(); + let ntfy_topic = std::fs::read_to_string( + state + .config + .credentials_directory + .join(NTFY_TOPIC_CREDENTIAL_NAME), + )?; + let ntfy_url = state.config.ntfy_instance.join(ntfy_topic.trim())?; + + state + .http_client + .post(ntfy_url) + .body(mail) + .send() + .await + .map_err(|err| err.without_url())? + .error_for_status() + .map_err(|err| err.without_url())?; + + leptos_axum::redirect("/mail"); + Ok("Mail successfully sent!".to_owned()) +} + +#[component] +fn MailForm() -> impl IntoView { + let form: NodeRef<Form> = NodeRef::new(); + let submit_mail = ServerAction::<SubmitMail>::new(); + let pending = submit_mail.pending(); + let flash_message = submit_mail.value(); + + Effect::new(move || + if flash_message.get().is_some_and(|m| m.is_ok()) { + if let Some(form) = form.get() { + form.reset(); + } else { + logging::warn!("Failed to reset form"); + } + } + ); + + view! { + <Show when=move || { flash_message.get().is_some() }> + <div + class="notification is-light" + class=("is-success", move || flash_message.get().is_some_and(|m| m.is_ok())) + class=("is-danger", move || flash_message.get().is_some_and(|m| m.is_err())) + > + <button + class="delete" + aria-label="Close" + on:click=move |_| { + *flash_message.write() = None; + } + /> + <span role="alert"> + {move || match flash_message.get() { + None => "".to_owned(), + Some(Ok(message)) => message, + Some(Err(error)) => format!("{}", error), + }} + </span> + </div> + </Show> + + <ActionForm node_ref=form action=submit_mail> + <fieldset disabled=move || pending.get()> + <div class="field"> + <label class="label" for="mail"> + Email address + </label> + <div class="control"> + <input + id="mail" + class="input" + type="email" + placeholder="Your address" + name="mail" + required + /> + </div> + </div> + + <div class="field"> + <label class="label" for="subject"> + Subject + </label> + <div class="control"> + <input + id="subject" + class="input" + type="text" + placeholder="E.g. There's a typo on your home page!" + name="subject" + autocomplete="off" + required + /> + </div> + </div> + + <div class="field"> + <label class="label" for="message"> + Message + </label> + <textarea + id="message" + class="textarea" + type="text" + rows="6" + name="message" + autocomplete="off" + required + /> + </div> + + <div class="control"> + <div class="field"> + <button + type="submit" + class="button is-link" + class=("is-loading", move || pending.get()) + > + Send + </button> + </div> + </div> + </fieldset> + </ActionForm> + } +} + +#[component] +pub fn Mail() -> impl IntoView { + view! { + <Meta name="description" content="tlater.net mail submission" /> + <Title text="Mail submission" /> + + <section class="section"> + <div class="container"> + <h1 class="title has-text-weight-normal">Contact Me</h1> + + <div class="columns"> + <div class="column"> + <MailForm /> + </div> + + <div class="column content"> + {markdown_view!( + r#" +Any messages you enter here are directly forwarded to me. I aim to +respond within a day. + +Don't be upset about the form, I want to avoid the spam publishing +your email address brings with it... And minimize the amount of mail +that doesn't reach me, this form is an exception in all my spam +filters, you see ;) +"# + )} + </div> + </div> + </div> + </section> + } +} + +#[derive(Error, Debug, Clone, Serialize, Deserialize)] +pub enum MailError { + #[error("This form appears to currently be broken :(")] + Permanent, + #[error("Mail service appears to be down; please try again later")] + Temporary, + #[error("Server error: {0}")] + ServerFnError(ServerFnErrorErr), +} + +impl From<url::ParseError> for MailError { + fn from(error: url::ParseError) -> Self { + logging::error!("Invalid ntfy URL: {error}"); + Self::Permanent + } +} + +impl From<std::io::Error> for MailError { + fn from(error: std::io::Error) -> Self { + logging::error!("Couldn't read ntfy topic secret: {error}"); + Self::Permanent + } +} + +impl From<reqwest::Error> for MailError { + fn from(error: reqwest::Error) -> Self { + logging::error!("Failed to connect to ntfy: {error}"); + Self::Temporary + } +} + +impl FromServerFnError for MailError { + type Encoder = JsonEncoding; + + fn from_server_fn_error(value: ServerFnErrorErr) -> Self { + Self::ServerFnError(value) + } +} diff --git a/pkgs/packages/webserver/src/components.rs b/pkgs/packages/webserver/src/components.rs new file mode 100644 index 0000000..23457ab --- /dev/null +++ b/pkgs/packages/webserver/src/components.rs @@ -0,0 +1,45 @@ +use leptos::prelude::*; + +#[component] +pub fn Navbar() -> impl IntoView { + let (active, set_active) = signal(false); + + view! { + <nav class="navbar" role="navigation" aria-label="main navigation"> + <div class="navbar-brand"> + <a class="navbar-item has-text-primary is-uppercase" href="/"> + tlater + </a> + + <a + role="button" + on:click=move |_| { set_active.update(|active: &mut bool| *active = !*active) } + class="navbar-burger" + class=("is-active", move || active.get()) + aria-label="menu" + aria-controls="main-navigation" + aria-expanded=move || if active.get() { "true" } else { "false" } + > + <span aria-hidden="true" /> + <span aria-hidden="true" /> + <span aria-hidden="true" /> + <span aria-hidden="true" /> + </a> + </div> + + <div id="main-navigation" class="navbar-menu" class=("is-active", move || active.get())> + <div class="navbar-start"> + <a class="navbar-item" href="/mail"> + "E-Mail" + </a> + <a class="navbar-item" href="https://www.gitlab.com/tlater"> + GitLab + </a> + <a class="navbar-item" href="https://www.github.com/TLATER"> + GitHub + </a> + </div> + </div> + </nav> + } +} diff --git a/pkgs/packages/webserver/src/lib.rs b/pkgs/packages/webserver/src/lib.rs new file mode 100644 index 0000000..5ae288d --- /dev/null +++ b/pkgs/packages/webserver/src/lib.rs @@ -0,0 +1,73 @@ +pub mod app; +mod components; + +#[cfg(feature = "hydrate")] +#[wasm_bindgen::prelude::wasm_bindgen] +pub fn hydrate() { + use crate::app::*; + console_error_panic_hook::set_once(); + leptos::mount::hydrate_body(App); +} + +#[cfg(feature = "ssr")] +pub use appstate::{AppState, Config}; + +#[cfg(feature = "ssr")] +mod appstate { + use axum::extract::FromRef; + use figment::{providers::Format, Figment}; + use leptos::config::LeptosOptions; + use reqwest::Client; + use serde::Deserialize; + use std::path::PathBuf; + use url::Url; + + #[derive(Deserialize, Debug, Clone)] + pub struct Config { + pub credentials_directory: PathBuf, + pub ntfy_instance: Url, + } + + impl Config { + pub fn parse() -> Self { + let config_path = std::env::var_os("TLATERNET_CONFIG") + .map(PathBuf::from) + .or_else(|| { + std::env::current_dir() + .map(|dir| dir.join("config.toml")) + .ok() + }); + + let config: Result<Config, figment::Error> = if let Some(config_path) = config_path { + Figment::new().merge(figment::providers::Toml::file(config_path)) + } else { + Figment::new() + } + .merge(figment::providers::Env::raw().only(&["CREDENTIALS_DIRECTORY"])) + .merge(figment::providers::Env::prefixed("TLATERNET_")) + .extract(); + + match config { + Ok(config) => { + if !config.credentials_directory.join("ntfy-topic").exists() { + leptos::logging::error!("Failed to find ntfy-topic credential"); + std::process::exit(1); + } + + config + } + Err(error) => { + leptos::logging::error!("Failed to parse configuration: {:?}", error); + std::process::exit(1); + } + } + } + } + + #[derive(FromRef, Debug, Clone)] + pub struct AppState { + pub config: Config, + pub http_client: Client, + pub leptos_options: LeptosOptions, + } +} diff --git a/pkgs/packages/webserver/src/main.rs b/pkgs/packages/webserver/src/main.rs new file mode 100644 index 0000000..a5aac95 --- /dev/null +++ b/pkgs/packages/webserver/src/main.rs @@ -0,0 +1,54 @@ +#[cfg(feature = "ssr")] +#[tokio::main] +async fn main() { + use axum::Router; + use leptos::logging::log; + use leptos::prelude::*; + use leptos_axum::{LeptosRoutes, generate_route_list}; + use tlaternet_webserver::app::*; + use tlaternet_webserver::{AppState, Config}; + + let config = Config::parse(); + + let (addr, leptos_options) = { + let conf = get_configuration(None).unwrap(); + let addr = conf.leptos_options.site_addr; + let leptos_options = conf.leptos_options; + (addr, leptos_options) + }; + + // Generate the list of routes in your Leptos App + let routes = generate_route_list(App); + let http_client = reqwest::Client::new(); + + let state = AppState { + config, + http_client, + leptos_options, + }; + + let app = Router::new() + .leptos_routes(&state, routes, { + let leptos_options = state.leptos_options.clone(); + move || shell(leptos_options.clone()) + }) + .fallback::<_, (_, _, axum::extract::State<AppState>, _)>( + leptos_axum::file_and_error_handler(shell), + ) + .with_state(state); + + // run our app with hyper + // `axum::Server` is a re-export of `hyper::Server` + log!("listening on http://{}", &addr); + let listener = tokio::net::TcpListener::bind(&addr).await.unwrap(); + axum::serve(listener, app.into_make_service()) + .await + .unwrap(); +} + +#[cfg(not(feature = "ssr"))] +pub fn main() { + // no client-side main function + // unless we want this to work with e.g., Trunk for pure client-side testing + // see lib.rs for hydration function instead +} diff --git a/pkgs/packages/webserver/style/custom-bulma.scss b/pkgs/packages/webserver/style/custom-bulma.scss new file mode 100644 index 0000000..0f15681 --- /dev/null +++ b/pkgs/packages/webserver/style/custom-bulma.scss @@ -0,0 +1,57 @@ +@use "bulma/sass/utilities/initial-variables" as iv with ( + $black: #0f0f0f, + $grey-darker: #11151c, + $grey-light: #dddddd, + $white: #ffffff, + + $orange: #d26937, + $yellow: #b58900, + $green: #2aa889, + $cyan: #99d1ce, + $blue: #195466, + $red: #dc322f, +); + +iv.$family-sans-serif: "Nunito", iv.$family-sans-serif; +iv.$family-monospace: "Hack", iv.$family-monospace; + +@forward "bulma/sass/utilities/functions"; +@use "bulma/sass/utilities/derived-variables" with ( + $link: iv.$cyan, + $primary: iv.$cyan, +); +@forward "bulma/sass/utilities/controls"; + +@forward "bulma/sass/form"; + +@forward "bulma/sass/base" with ( + $body-background-color: iv.$black, + $body-color: iv.$grey-light, + + $hr-background-color: iv.$grey-light, + $hr-height: 1px, +); +@forward "bulma/sass/themes"; + +@forward "bulma/sass/elements/button"; +@use "bulma/sass/elements/content" with ( + $content-heading-weight: iv.$weight-semibold, +); +@use "bulma/sass/elements/notification"; +@use "bulma/sass/elements/delete"; + +@use "bulma/sass/elements/title" with ( + $title-color: iv.$cyan, +); + +@forward "bulma/sass/grid/columns"; + +@forward "bulma/sass/helpers/typography"; +@forward "bulma/sass/helpers/color"; + +@forward "bulma/sass/layout/container"; +@forward "bulma/sass/layout/section"; + +@forward "bulma/sass/components/navbar" with ( + $navbar-burger-color: iv.$grey-light, +); diff --git a/pkgs/packages/webserver/style/fonts.scss b/pkgs/packages/webserver/style/fonts.scss new file mode 100644 index 0000000..5071ee0 --- /dev/null +++ b/pkgs/packages/webserver/style/fonts.scss @@ -0,0 +1,48 @@ +@use "@fontsource-utils/scss/src/mixins" as fontsource with ( + $display: auto +); +@use "@fontsource-variable/arimo/scss/metadata.scss" as arimo; +@use "@fontsource-variable/nunito/scss/metadata.scss" as nunito; + +@include fontsource.faces( + $metadata: nunito.$metadata, + $weights: ( + 300, + 400, + 500, + 600, + 700, + ), + $subsets: latin, + $styles: ( + normal, + italic, + ), + $family: "Nunito", + $directory: "/@fontsource-variable/nunito" +); + +@include fontsource.faces( + $metadata: arimo.$metadata, + $weights: 400, + $subsets: latin, + $styles: normal, + $family: "Arimo", + $directory: "/@fontsource-variable/arimo" +); + +// Hack *does* come with its own CSS, but it's broken and hasn't seen +// a release since https://github.com/source-foundry/Hack/issues/467 +// was resolved. + +$variants: regular normal 400, bold normal 700, italic italic 400, bolditalic italic 700; + +@each $name, $style, $weights in $variants { + @font-face { + font-family: "Hack"; + font-style: $style; + font-display: auto; + font-weight: $weights; + src: url("/hack-font/hack-#{$name}-subset.woff2") format("woff2-variations"); + } +} diff --git a/pkgs/packages/webserver/style/main.scss b/pkgs/packages/webserver/style/main.scss new file mode 100644 index 0000000..2c06900 --- /dev/null +++ b/pkgs/packages/webserver/style/main.scss @@ -0,0 +1,7 @@ +@use "fonts"; +@use "custom-bulma"; +@use "typed"; + +#typed-welcome { + @include typed.typed-text(typed-welcome, "Welcome to tlater.net!", 1.2s); +} diff --git a/pkgs/packages/webserver/style/typed.scss b/pkgs/packages/webserver/style/typed.scss new file mode 100644 index 0000000..3be437f --- /dev/null +++ b/pkgs/packages/webserver/style/typed.scss @@ -0,0 +1,149 @@ +@use "sass:math"; +@use "sass:list"; + +@mixin test { + margin: 0; +} + +@mixin typed-text($id, $text, $duration) { + &::before { + @include typed($id, $text, $duration); + } + + &::after { + @include cursor($id, 6s); + } +} + +/// Animate a blinking cursor. +@mixin cursor($id, $duration) { + $name: cursor-#{$id}; + // The number of times we need to blink is = the number of full + // seconds (500ms * 2) that fit in the total duration, rounded up, + // and doubled. + $iterations: math.ceil(math.div($duration, 1s)) * 2; + + animation: $name ease-in-out 500ms $iterations alternate; + content: " "; + + @keyframes #{$name} { + from { + content: " "; + } + + to { + content: "█"; + } + } +} + +/// Animate a piece of text as if it was being typed by a human. +@mixin typed($id, $text, $duration) { + word-break: break-all; + + // We don't want a linearly typed set of text, which makes this + // significantly more complex. + // + // CSS animations normally do not permit per-frame changes in + // duration (since the total animation time is fixed). This means we + // need to create multiple animations, and delay them so that they + // happen in the time sequence we want. + // + // We generate the raw values with _generate-animations, and then + // split up the result into the animation API. + $frames: str-length($text); + $animations: _generate-animations($id, $frames, 1.2s); + + animation-name: _unzip($animations, 1); + animation-delay: _unzip($animations, 3); + animation-fill-mode: forwards; + content: ""; + + // We need to type each character in separate animations, see above + // comment. + @each $name, $character in $animations { + @keyframes #{$name} { + from { + content: str-slice($text, 0, $character); + } + + to { + content: str-slice($text, 0, $character + 1); + } + } + } +} + +/// Unzip a nested set of lists, taking the nth value of each sublist. +@function _unzip($lists, $i) { + $out: (); + $sep: comma; + @each $sublist in $lists { + $out: list.append($out, list.nth($sublist, $i), $sep); + } + @return $out; +} + +/// Compute the sum of all numbers in a list. +@function _sum($list) { + $out: 0; + @each $val in $list { + $out: $out + $val; + } + @return $out; +} + +/// Produce a list from a shorter list by repeating it up until size +/// $length. +@function _round-robin($base, $length) { + $out: (); + $sep: list.separator($out); + @for $i from 0 through $length { + $out: list.append($out, list.nth($base, $i % list.length($base) + 1)); + } + @return $out; +} + +/// Generate the actual animation values. +/// +/// This generates a nested list as: +/// +/// (keyframe-name, index, start time) +/// +/// The duration of each frame is taken from the internal $delays in a +/// round robin fashion, to give some amount of human-like variance to +/// the duration of each frame. +/// +/// Start time is set to the time at which the frame should start to +/// achieve the desired frame-by-frame duration. +@function _generate-animations($id, $number, $total_duration) { + $out: (); + $sep: list.separator($out); + + // A set of "human-like" delays for each typed character. In + // practice, my typing seems to be about 20-70ms, but it looks a bit + // nicer to increase all typing by 20ms to make the effect more + // noticeable. + // + // Numbers generated once with a random number generator, rather + // than using `math.random()`, since they end up in CSS verbatim, + // and the build would be non-reproducible if we didn't do it this + // way. Using `math.random() wouldn't change this dynamically each + // time the page loads anyway, so we don't really lose anything by + // pre-generating these numbers. + $delays: 69ms, 83ms, 49ms, 48ms, 52ms, 59ms, 40ms, 71ms, 80ms, 67ms; + + @for $animation from 0 through $number { + $out: list.append( + $out, + ( + type-#{$id}-#{$animation}, + $animation, + _sum(_round_robin($delays, $animation)) + ), + $sep + ); + } + + @return $out; +} diff --git a/pkgs/packages/webserver/update.nu b/pkgs/packages/webserver/update.nu new file mode 100644 index 0000000..3e6e6b7 --- /dev/null +++ b/pkgs/packages/webserver/update.nu @@ -0,0 +1,88 @@ +const self = "pkgs/packages/webserver/package.nix" +let tmpdir = mktemp -d webserver-update.XXXXXXXXXX + +let dependencies = { + fontsource-scss: (prefetch-npm @fontsource-utils/scss) + fontsource-arimo: (prefetch-npm @fontsource-variable/arimo) + fontsource-nunito: (prefetch-npm @fontsource-variable/nunito) + + bulma: (prefetch-github jgthms bulma) + hack-font: (prefetch-github source-foundry Hack) +} + +cd (git rev-parse --show-toplevel) + +$dependencies | items {|name, metadata| $metadata | update-dependency $name } + +rm -r $tmpdir + +cd (dirname $self) +cargo update + +def prefetch-npm [package: string] { + let metadata = http get $'https://registry.npmjs.org/($package)' + let version = $metadata.dist-tags.latest + let url = ($metadata.versions | get $version).dist.tarball + let tarball = ($tmpdir | path join "package.tgz") + + http get $url | save -f $tarball + + let hash = nix hash file $tarball + + { + url: $url + version: $version + hash: $hash + } +} + +def prefetch-github [owner: string, repo: string] { + let metadata = http get $'https://api.github.com/repos/($owner)/($repo)/releases/latest' + let prefetch = nix-prefetch-github --rev $metadata.tag_name --json $owner $repo | from json + $prefetch | select hash | insert version ($metadata.name | str trim --left --char v) +} + +def update-dependency [dependency_name: string] { + const replace_attribute_template = " + id: update-attribute + language: nix + utils: + is-attribute: + kind: string_fragment + inside: + kind: binding + stopBy: end + has: + field: attrpath + regex: '{attribute}' + + rule: + matches: is-attribute + not: + regex: '{replacement}' + inside: + kind: binding + stopBy: end + has: + field: attrpath + regex: '{dependency_name}' + + fix: '{replacement}'" + + let template_data = ( + $in | if ($in has url) { + [{attribute: url replacement: $in.url}] + } else { [] } + ) ++ [ + {attribute: version replacement: $in.version} + {attribute: hash replacement: $in.hash} + ]; + + let ast_grep_rule = ( + $template_data + | each { $in | insert dependency_name $dependency_name | format pattern $replace_attribute_template } + | str join "\n---\n" + ) + + ast-grep scan --update-all --inline-rules $ast_grep_rule $self +} From 342b6c756a9636b39cad86cd5a7a49f1b2bc511e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tristan=20Dani=C3=ABl=20Maat?= <tm@tlater.net> Date: Sat, 29 Nov 2025 21:02:10 +0800 Subject: [PATCH 20/24] feat(ntfy-sh): Self-host ntfy-sh --- configuration/services/default.nix | 1 + configuration/services/ntfy-sh/default.nix | 189 ++++++++++++++++++ .../services/ntfy-sh/downstream-module.nix | 70 +++++++ configuration/services/webserver.nix | 22 +- keys/production.yaml | 8 +- keys/staging.yaml | 9 +- 6 files changed, 281 insertions(+), 18 deletions(-) create mode 100644 configuration/services/ntfy-sh/default.nix create mode 100644 configuration/services/ntfy-sh/downstream-module.nix diff --git a/configuration/services/default.nix b/configuration/services/default.nix index adc4482..1624653 100644 --- a/configuration/services/default.nix +++ b/configuration/services/default.nix @@ -8,6 +8,7 @@ ./gitea.nix ./immich.nix ./metrics + ./ntfy-sh ./minecraft.nix ./nextcloud.nix ./postgres.nix diff --git a/configuration/services/ntfy-sh/default.nix b/configuration/services/ntfy-sh/default.nix new file mode 100644 index 0000000..aacec91 --- /dev/null +++ b/configuration/services/ntfy-sh/default.nix @@ -0,0 +1,189 @@ +{ + pkgs, + config, + flake-inputs, + ... +}: +let + domain = "ntfy.${config.services.nginx.domain}"; +in +{ + imports = [ ./downstream-module.nix ]; + + networking.firewall.allowedTCPPorts = [ + 80 + 443 + ]; + + services.ntfy-sh = { + enable = true; + package = flake-inputs.nixpkgs-unstable.legacyPackages.${pkgs.system}.ntfy-sh; + + environmentFile = config.sops.secrets."ntfy/users".path; + + settings = { + base-url = "https://${domain}"; + listen-http = "127.0.0.1:2586"; + behind-proxy = true; + + # Paths + attachment-cache-dir = "/var/lib/ntfy-sh/attachments"; + cache-file = "/var/lib/ntfy-sh/cache-file.db"; + auth-file = "/var/lib/ntfy-sh/user.db"; + auth-default-access = "deny-all"; + auth-access = [ "*:local-*:wo" ]; + + # Don't want to host the front-end + web-root = "disable"; + }; + }; + + services.nginx.virtualHosts."ntfy.${config.services.nginx.domain}" = { + forceSSL = true; + useACMEHost = "tlater.net"; + enableHSTS = true; + + locations."/" = { + proxyWebsockets = true; + proxyPass = "http://${config.services.ntfy-sh.settings.listen-http}"; + extraConfig = '' + client_max_body_size 0; # Stream request body to backend + ''; + }; + + # Don't allow writing to topics with the local prefix, *including* + # webhook writes, since they are set to write-only access from + # anyone. + locations."/local" = { + proxyWebsockets = true; + proxyPass = "http://${config.services.ntfy-sh.settings.listen-http}"; + extraConfig = '' + client_max_body_size 0; # Stream request body to backend + + limit_except GET OPTIONS { + deny all; + } + + location ~ /trigger$ { + deny all; + } + ''; + }; + }; + + sops.secrets."ntfy/users" = { }; + + serviceTests = { + testNtfyConfig = pkgs.testers.runNixOSTest { + name = "test-ntfy-config"; + + node.specialArgs = { inherit flake-inputs; }; + nodes = { + testHost = + { lib, ... }: + { + imports = [ + ./. + ../../nginx + ../../../modules/serviceTests/mocks.nix + ]; + + services.nginx.domain = "testHost"; + + # Don't care for testing SSL here + services.nginx.virtualHosts."ntfy.testHost" = { + forceSSL = lib.mkForce false; + enableHSTS = lib.mkForce false; + }; + }; + + client = + { pkgs, ... }: + { + environment.systemPackages = [ pkgs.curl ]; + networking.hosts."192.168.1.2" = [ "ntfy.testHost" ]; + }; + }; + + testScript = '' + import json + import time + from contextlib import contextmanager + + def read_client_messages(): + client.wait_for_unit("messages.service") + messages = [json.loads(line) for line in client.succeed("cat messages").split()] + client.succeed("systemctl stop messages.service") + client.succeed("rm messages") + + print(messages) + + return messages + + @contextmanager + def client_subscribe(topic: str, timeout: int = 2): + systemd_invocation = [ + "systemd-run", + "--unit messages", + "--property=Type=oneshot", + "--property=SuccessExitStatus=28", + "--remain-after-exit", + "--setenv=PATH=$PATH", + "--same-dir", + "--no-block", + "/bin/sh -c" + ] + + curl = [ + "curl", + "--silent", + "--show-error", + f"--max-time {timeout}", + "-u tlater:insecure", + f"http://ntfy.testHost/{topic}/json", + "> messages" + ] + + client.succeed(f'{" ".join(systemd_invocation)} "{" ".join(curl)}"') + + # Give some slack so the host doesn't send messages before + # we're listening + time.sleep(1) + + yield + + + start_all() + testHost.wait_for_unit("ntfy-sh.service") + client.wait_until_succeeds("curl http://ntfy.testHost") + + with subtest("subscribing and writing to local topics works"): + with client_subscribe("local-test"): + testHost.succeed("curl --fail --silent --show-error --data test http://127.0.0.1:2586/local-test") + + messages = read_client_messages() + t.assertEqual(len(messages), 2) + t.assertEqual(messages[1].get("message"), "test") + + with subtest("writing to non-local topics without auth fails"): + testHost.fail("curl --fail --silent --show-error --data test http://127.0.0.1:2586/test") + + with subtest("writing to *any* topics from outside localhost fails"): + client.fail("curl --fail --silent --show-error --data test http://ntfy.testHost/test") + client.fail("curl --fail --silent --show-error --data test http://ntfy.testHost/local-test") + # GET requests work by default because websocket shenanigans + client.fail("curl --fail --silent --show-error http://ntfy.testHost/local-test/trigger?message=test") + + with subtest("authenticated messaging works from outside localhost"): + with client_subscribe("test", 10): + client.succeed("curl -u tlater:insecure --fail --silent --show-error --data test http://ntfy.testHost/test") + client.succeed("curl -u tlater:insecure --fail --silent --show-error http://ntfy.testHost/test/trigger?message=test2") + + messages = read_client_messages() + t.assertEqual(len(messages), 3) + t.assertEqual(messages[1].get("message"), "test") + t.assertEqual(messages[2].get("message"), "test2") + ''; + }; + }; +} diff --git a/configuration/services/ntfy-sh/downstream-module.nix b/configuration/services/ntfy-sh/downstream-module.nix new file mode 100644 index 0000000..c3a9634 --- /dev/null +++ b/configuration/services/ntfy-sh/downstream-module.nix @@ -0,0 +1,70 @@ +{ + pkgs, + lib, + config, + ... +}: +let + cfg = config.services.ntfy-sh; + settingsFormat = pkgs.formats.yaml { }; + configFile = settingsFormat.generate "server.yml" cfg.settings; +in +{ + # We don't use the upstream module because it's stupid; the author + # doesn't seem to understand `DynamicUser` (or at least be unaware of + # systemd credentials). + disabledModules = [ "services/misc/ntfy-sh.nix" ]; + + options.services.ntfy-sh = { + enable = lib.mkEnableOption "[ntfy-sh](https://ntfy.sh), a push notification service"; + package = lib.mkPackageOption pkgs "ntfy-sh" { }; + environmentFile = lib.mkOption { + type = lib.types.nullOr lib.types.path; + default = null; + description = '' + Environment file; intended to be used for user provisioning. + ''; + }; + + settings = lib.mkOption { + inherit (settingsFormat) type; + default = { }; + description = '' + Configuration for ntfy.sh, supported values are [here](https://ntfy.sh/docs/config/#config-options). + ''; + }; + }; + + config.systemd.services.ntfy-sh = { + description = "Push notifications server"; + + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + + serviceConfig = { + Type = "exec"; + ExecReload = "kill --signal HUP $MAINPID"; + ExecStart = "${lib.getExe' cfg.package "ntfy"} serve -c ${configFile}"; + + EnvironmentFile = cfg.environmentFile; + + StateDirectory = "ntfy-sh"; + + DynamicUser = true; + PrivateTmp = true; + NoNewPrivileges = true; + ProtectSystem = "full"; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + ProtectControlGroups = true; + PrivateDevices = true; + RestrictSUIDSGID = true; + RestrictNamespaces = true; + RestrictRealtime = true; + MemoryDenyWriteExecute = true; + # Upstream Recommandation + LimitNOFILE = 20500; + }; + }; +} diff --git a/configuration/services/webserver.nix b/configuration/services/webserver.nix index 88ce2b6..e8daeaf 100644 --- a/configuration/services/webserver.nix +++ b/configuration/services/webserver.nix @@ -24,7 +24,7 @@ in ''; environment = { - TLATERNET_NTFY_INSTANCE = "https://tlater.net"; + TLATERNET_NTFY_INSTANCE = "http://${config.services.ntfy-sh.settings.listen-http}"; LEPTOS_SITE_ADDR = "127.0.0.1:8000"; }; @@ -62,20 +62,16 @@ in }; # Set up SSL - services.nginx.virtualHosts."${domain}" = - let - inherit (config.services.tlaternet-webserver.listen) addr port; - in - { - serverAliases = [ "www.${domain}" ]; + services.nginx.virtualHosts."${domain}" = { + serverAliases = [ "www.${domain}" ]; - forceSSL = true; - useACMEHost = "tlater.net"; - enableHSTS = true; + forceSSL = true; + useACMEHost = "tlater.net"; + enableHSTS = true; - locations."/".proxyPass = - "http://${config.systemd.services.tlaternet-webserver.environment.LEPTOS_SITE_ADDR}"; - }; + locations."/".proxyPass = + "http://${config.systemd.services.tlaternet-webserver.environment.LEPTOS_SITE_ADDR}"; + }; sops.secrets = { "tlaternet/ntfy-topic" = { }; diff --git a/keys/production.yaml b/keys/production.yaml index 9497ad6..ccbee64 100644 --- a/keys/production.yaml +++ b/keys/production.yaml @@ -1,3 +1,7 @@ +tlaternet: + ntfy-topic: ENC[AES256_GCM,data:1mlcrb4uOkwyB5pwTbIv,iv:Ba6k9HV4ze9cftbSE8ns2+j2ebi4cqmHa5UwM1YR6W8=,tag:Ksf840uCOQdoVasORjUdcQ==,type:str] +ntfy: + users: ENC[AES256_GCM,data:7vZx2jIXsEMT0LhPKis7oiaq8YGgGnW3M6L9Aq0qlaNzpF31sAAZHMYPMJDG0Z09gvQ60kRVxgggHYx8TlaD/ZxLhFVfKgUcQaiRRxyAFIKFVqVNRQaiykQcFzw=,iv:HefHN4rkVKg43Ery0QW2Mg74tIwsM0OvsluXFIWVCVM=,tag:WM8uW4bV9nHuwWYaH+mmUw==,type:str] porkbun: api-key: ENC[AES256_GCM,data:p3lqvGc8m2U/12rBPjoNR7hxQyD52CyEen/V8q59k5CSJZSqzZS8M5vEXFBsUMjz2lrmKM4pgtz4wa2fWK6Ty4LJCaI=,iv:OQC3FpwTtPmqHvDbA41mWF7LGYwC/jD2ZMBsE8ktNOg=,tag:kq5hUR7TBgczuGcXpsdu2A==,type:str] secret-api-key: ENC[AES256_GCM,data:zV5PTKf45Zab8uW8mbuXmPNzciq6tV9OF0wUND7YnRk/DjZneYWItAsNBVoM+iHA+XsUPDoeKo6hoJiGkH/cCQ8WvuM=,iv:yr1M5DlgI8k6BgzNz3HRnqspHOrQuf2PmoZS1HGp0v8=,tag:JkNNziMMfKFZV2hnx5lXRg==,type:str] @@ -28,8 +32,8 @@ turn: env: ENC[AES256_GCM,data:kt5nhVo9pb/ZbPUEcqSYXxN9YMgQKnFb5VRfFFS/qoIaJ73uD2fuJKqcxAyVRrdLqnSAWSQBgTgunBzdP7xqLAK2qt8DYAQWHkIe9uxFbSXZpdmw,iv:9lq6SFwTFN4GGm6gPiJpUMasMdnHVF6XLGYrsyG3kjU=,tag:428Qf9DOiiHt/Wjb188b8g==,type:str] secret: ENC[AES256_GCM,data:si7ee6Xfhdgdyzbp6aQpF7pz3TmTBb7iQ82lRPVXNDg9JfHI+lbmgAsSnRLX5qMCA6P9R045sSMosqidL8QwRg==,iv:SrhpZKK8D45yxCEfDb9P3TwtA14+qEI+wcRqcN/a6pw=,tag:PiwV+mOL9xHJgJft6sc61g==,type:str] sops: - lastmodified: "2025-11-22T11:12:58Z" - mac: ENC[AES256_GCM,data:KWc1yNxHQa7MKV/3iXaGOz+RQnHoF4cPumhdoY9zMzWsF1UV0FcR9xCF8KB40elHd4/7uz0stKiSiHhLvtSBW6KjMj0N5JSFknXKxGJWf8XA6ZJrvHhWamXX/ZVbdpvgwh6LzluzogjlCWatWYcr6svDoSEm0gL+Aynudyrx75I=,iv:gPXZpMs0TOvY6cpQJ4MprGlS8jleuO+vWwmJ0F6G+3s=,tag:BfhXRv+mANrBVASc8Edu8A==,type:str] + lastmodified: "2025-11-29T14:52:24Z" + mac: ENC[AES256_GCM,data:RC18s48jxRFQMtbmu74P7G4uhm2yHk9TB0wN7z4g8SNE3nfkYMvHAJqPr3A3dO+T33zkTFcSRm7fhWItUahTCW3fO10u6kDvWbnyjlSuAy86Tkz2iqeW4iSOzKswDptAgb/B+juAHhEMxDnkG5vpPlIcD0SVP89NlflXftogOqw=,iv:2vN2TJvzePzBJfUeBxvGXwGmRsB5sopqyWm9uUv/rzA=,tag:C6UOWrUxVsRMFncL1y1eTQ==,type:str] pgp: - created_at: "2025-10-03T21:38:48Z" enc: |- diff --git a/keys/staging.yaml b/keys/staging.yaml index cd69d03..20ee3db 100644 --- a/keys/staging.yaml +++ b/keys/staging.yaml @@ -1,5 +1,8 @@ tlaternet: - ntfy-topic: ENC[AES256_GCM,data:T0wEsXso0uJD4KDe8qv8d24+vaouDrpDFxpSROn5kiYnqa1w6BSy3VrFdqFy17s7mzlAsZtN7gfDZDFjIo1S3Q==,iv:evWlOBgdnVdprzrTy5m+rcRo0OkFEIX+lCgfwLyfw4I=,tag:iq4B6VLWy0nm80ezqJ+rgw==,type:str] + ntfy-topic: ENC[AES256_GCM,data:BSNLP9hLQKufDf3STknQ,iv:4WeYARwDEg84fh8qMa4szssQeK2orBl72oiIRywXCi8=,tag:GtS7YAlQiuGAWaUn+JxaYg==,type:str] +ntfy: + #ENC[AES256_GCM,data:B5sUuCWTheo/Lz5kAN5/8NeiT7ohQ6IRYjD7k/c0,iv:E85yGjNcd9RGOgBKFPxRB6LqdqISNcjlvp/pqpR7VPg=,tag:1NrwtWyoVp4qDWiqAt/few==,type:comment] + users: ENC[AES256_GCM,data:zW02xRlLvqJ0/rLJMX9/LSW7CQ26nIyifOLYba4AkstQcrVMOIV1Je4QwaKoy2TPhDcVcjLg6Ce2/0qxpO7Q3rqoXqACbPAUwcxzn14KOt7tEb3IE2S//0Y/law=,iv:sV5Dvplr6A5ivrI/+Cyl6mC+Zxo8NORxfuhEZ/75JbU=,tag:NzqxoN3Hr0YO2CXiVdEo+A==,type:str] porkbun: api-key: ENC[AES256_GCM,data:A5J1sqwq6hs=,iv:77Mar3IX7mq7z7x6s9sSeGNVYc1Wv78HptJElEC7z3Q=,tag:eM/EF9TxKu+zcbJ1SYXiuA==,type:str] secret-api-key: ENC[AES256_GCM,data:8Xv+jWYaWMI=,iv:li4tdY0pch5lksftMmfMVS729caAwfaacoztaQ49az0=,tag:KhfElBGzVH4ByFPfuQsdhw==,type:str] @@ -30,8 +33,8 @@ turn: env: ENC[AES256_GCM,data:xjIz/AY109lyiL5N01p5T3HcYco/rM5CJSRTtg==,iv:16bW6OpyOK/QL0QPGQp/Baa9xyT8E3ZsYkwqmjuofk0=,tag:J5re3uKxIykw3YunvQWBgg==,type:str] secret: ENC[AES256_GCM,data:eQ7dAocoZtg=,iv:fgzjTPv30WqTKlLy+yMn5MsKQgjhPnwlGFFwYEg3gWs=,tag:1ze33U1NBkgMX/9SiaBNQg==,type:str] sops: - lastmodified: "2025-11-27T19:00:56Z" - mac: ENC[AES256_GCM,data:Q8ATaYVuToQaToVgT5JNP6NGipLnu8JifBivD5jG0h/Xb82ObkMHYejy7tcir600+XgC2UJgfRBgEtYbhjK+SQcyvyBpmPWyv1cKfcidwnfDqRMbc8/1LLl/3mK1losv7/51aZDqvu7GuIvOwXh6IyDQldSuyyPf+wcmCCENiLY=,iv:zDGmnEL2659W7JPRG9dlLjtvDtCgz+iXjJt4EPx/OCM=,tag:3X69zX1sZqrZujB7En8jqA==,type:str] + lastmodified: "2025-11-29T11:54:33Z" + mac: ENC[AES256_GCM,data:SaTvwxfARVou/ZjrWfdC8J6je8l89Zuumdz7PkmY2Tl2CQVxZmEt4AyV4bWiCtWhJmfH1Qa8m4Q+DyqimjapgYT5cUB1yxlknp233bB/+5C5k3KozU2hmh80KYgR496FtQvI74p0qw/lw00CGCR3WHNcIc0dbTiDzC90HlOpafg=,iv:vxMCAjpgyWvxk18LalmFhwOb5b2ThCDq1KTaX2OPvpM=,tag:QMA+tC4hs/FBnuVDye38Vg==,type:str] pgp: - created_at: "2025-10-03T21:38:26Z" enc: |- From 642dad3afa2536c9c42e7e96a10da5e815f6f2bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tristan=20Dani=C3=ABl=20Maat?= <tm@tlater.net> Date: Mon, 1 Dec 2025 16:22:00 +0800 Subject: [PATCH 21/24] update(treewide): Update to NixOS 25.11 --- configuration/hardware-specific/vm.nix | 8 +++++ configuration/nginx/ssl.nix | 34 +++++++++---------- configuration/services/foundryvtt.nix | 2 +- configuration/services/nextcloud.nix | 4 +-- configuration/services/ntfy-sh/default.nix | 1 - configuration/services/starbound.nix | 2 +- configuration/services/webserver.nix | 2 +- flake.lock | 32 +++++------------ flake.nix | 8 ++--- modules/crowdsec/default.nix | 2 +- .../remediations/cs-firewall-bouncer.nix | 2 +- pkgs/default.nix | 7 ++-- 12 files changed, 44 insertions(+), 60 deletions(-) diff --git a/configuration/hardware-specific/vm.nix b/configuration/hardware-specific/vm.nix index aa360aa..70c1b58 100644 --- a/configuration/hardware-specific/vm.nix +++ b/configuration/hardware-specific/vm.nix @@ -43,6 +43,14 @@ source = ../../keys/hosts/staging.key; }; + # Pretend the acme renew succeeds. + # + # TODO(tlater): Set up pebble to retrieve certs "properly" + # instead + systemd.services."acme-order-renew-tlater.net".script = '' + touch out/acme-success + ''; + virtualisation.vmVariant = { virtualisation = { memorySize = 3941; diff --git a/configuration/nginx/ssl.nix b/configuration/nginx/ssl.nix index 56bfa78..f3fac13 100644 --- a/configuration/nginx/ssl.nix +++ b/configuration/nginx/ssl.nix @@ -51,20 +51,9 @@ paths = [ "/var/lib/acme/tlater.net" ]; }; - systemd.services = { - nginx.serviceConfig.SupplementaryGroups = [ config.security.acme.certs."tlater.net".group ]; - - # Don't attempt to retrieve a certificate if the domain name - # doesn't *actually* match the cert name - # - # TODO(tlater): Set up pebble to retrieve certs "properly" - # instead - "acme-tlater.net".serviceConfig.ExecCondition = - let - confirm = ''[[ "tlater.net" = "${config.services.nginx.domain}" ]]''; - in - ''${pkgs.runtimeShell} -c '${confirm}' ''; - }; + systemd.services.nginx.serviceConfig.SupplementaryGroups = [ + config.security.acme.certs."tlater.net".group + ]; sops.secrets = { "porkbun/api-key".owner = "acme"; @@ -85,10 +74,18 @@ security.acme.certs."tlater.net".extraDomainNames = [ config.services.nginx.domain ]; - services.nginx = { - domain = "testHost"; + # Pretend the acme renew succeeds. + # + # TODO(tlater): Set up pebble to retrieve certs "properly" + # instead + systemd.services."acme-order-renew-tlater.net".script = '' + touch out/acme-success + ''; - virtualHosts."${config.services.nginx.domain}" = { + services.nginx = { + domain = "testHost.test"; + + virtualHosts."${config.services.nginx.domain}.local" = { useACMEHost = "tlater.net"; onlySSL = true; enableHSTS = true; @@ -109,6 +106,7 @@ { pkgs, ... }: { environment.systemPackages = [ pkgs.curl ]; + networking.hosts."192.168.1.2" = [ "testHost.test" ]; }; }; @@ -125,7 +123,7 @@ "--silent", "--dump-header -", "--cacert /certs/tlater.net/fullchain.pem", - "https://testHost", + "https://testHost.test", "-o /dev/null" ])) diff --git a/configuration/services/foundryvtt.nix b/configuration/services/foundryvtt.nix index 5c8a21f..d7b9d02 100644 --- a/configuration/services/foundryvtt.nix +++ b/configuration/services/foundryvtt.nix @@ -23,7 +23,7 @@ in minifyStaticFiles = true; proxySSL = true; proxyPort = 443; - package = flake-inputs.foundryvtt.packages.${pkgs.system}.foundryvtt_13; + package = flake-inputs.foundryvtt.packages.${pkgs.stdenv.hostPlatform.system}.foundryvtt_13; }; nginx.virtualHosts."${domain}" = diff --git a/configuration/services/nextcloud.nix b/configuration/services/nextcloud.nix index 77cfa4c..30f79ed 100644 --- a/configuration/services/nextcloud.nix +++ b/configuration/services/nextcloud.nix @@ -5,7 +5,7 @@ ... }: let - nextcloud = pkgs.nextcloud31; + nextcloud = pkgs.nextcloud32; hostName = "nextcloud.${config.services.nginx.domain}"; in { @@ -104,7 +104,7 @@ in }; # Ensure that this service doesn't start before postgres is ready - systemd.services.nextcloud-setup.after = [ "postgresql.service" ]; + systemd.services.nextcloud-setup.after = [ "postgresql.target" ]; sops.secrets."nextcloud/tlater" = { owner = "nextcloud"; diff --git a/configuration/services/ntfy-sh/default.nix b/configuration/services/ntfy-sh/default.nix index aacec91..39ace90 100644 --- a/configuration/services/ntfy-sh/default.nix +++ b/configuration/services/ntfy-sh/default.nix @@ -17,7 +17,6 @@ in services.ntfy-sh = { enable = true; - package = flake-inputs.nixpkgs-unstable.legacyPackages.${pkgs.system}.ntfy-sh; environmentFile = config.sops.secrets."ntfy/users".path; diff --git a/configuration/services/starbound.nix b/configuration/services/starbound.nix index 6b97471..888fc3e 100644 --- a/configuration/services/starbound.nix +++ b/configuration/services/starbound.nix @@ -19,7 +19,7 @@ in serviceConfig = { ExecStart = "${ - flake-inputs.self.packages.${pkgs.system}.starbound + flake-inputs.self.packages.${pkgs.stdenv.hostPlatform.system}.starbound }/bin/launch-starbound ${./configs/starbound.json}"; Type = "simple"; diff --git a/configuration/services/webserver.nix b/configuration/services/webserver.nix index e8daeaf..ffe7480 100644 --- a/configuration/services/webserver.nix +++ b/configuration/services/webserver.nix @@ -20,7 +20,7 @@ in after = [ "network.target" ]; script = '' - ${lib.getExe flake-inputs.self.packages.${pkgs.system}.webserver} + ${lib.getExe flake-inputs.self.packages.${pkgs.stdenv.hostPlatform.system}.webserver} ''; environment = { diff --git a/flake.lock b/flake.lock index 3c65247..9ca0388 100644 --- a/flake.lock +++ b/flake.lock @@ -255,28 +255,15 @@ }, "nixpkgs": { "locked": { - "lastModified": 1763509821, - "narHash": "sha256-ibZr0ONEUA1W2WAdTzgm9/6jBE+tM20j1YW2FK4RZ/k=", - "rev": "659aa6fa27619d04de231b4cc0c938905dfa01e9", + "lastModified": 1764522689, + "narHash": "sha256-GzkEBSHGkj8EyOxnxQvl9sx0x2S7JzH0hwCziF176T8=", + "rev": "8bb5646e0bed5dbd3ab08c7a7cc15b75ab4e1d0f", "type": "tarball", - "url": "https://releases.nixos.org/nixos/25.05-small/nixos-25.05.812929.659aa6fa2761/nixexprs.tar.xz?lastModified=1763509821&rev=659aa6fa27619d04de231b4cc0c938905dfa01e9" + "url": "https://releases.nixos.org/nixos/25.11/nixos-25.11.650.8bb5646e0bed/nixexprs.tar.xz?lastModified=1764522689&rev=8bb5646e0bed5dbd3ab08c7a7cc15b75ab4e1d0f" }, "original": { "type": "tarball", - "url": "https://channels.nixos.org/nixos-25.05-small/nixexprs.tar.xz" - } - }, - "nixpkgs-unstable": { - "locked": { - "lastModified": 1763835633, - "narHash": "sha256-nzRnw0UkYQpDm0o20AKvG/5oHCXy5qEGOsFAVhB5NmA=", - "rev": "050e09e091117c3d7328c7b2b7b577492c43c134", - "type": "tarball", - "url": "https://releases.nixos.org/nixos/unstable/nixos-25.11pre900642.050e09e09111/nixexprs.tar.xz?lastModified=1763835633&rev=050e09e091117c3d7328c7b2b7b577492c43c134" - }, - "original": { - "type": "tarball", - "url": "https://channels.nixos.org/nixos-unstable/nixexprs.tar.xz" + "url": "https://channels.nixos.org/nixos-25.11/nixexprs.tar.xz" } }, "pre-commit-hooks": { @@ -325,7 +312,6 @@ "flint": "flint", "foundryvtt": "foundryvtt", "nixpkgs": "nixpkgs", - "nixpkgs-unstable": "nixpkgs-unstable", "sonnenshift": "sonnenshift", "sops-nix": "sops-nix" } @@ -338,11 +324,11 @@ ] }, "locked": { - "lastModified": 1763619077, - "narHash": "sha256-dlfamaoIzFEgwgtzPJuw5Tl5SqjbWcV8CsbP2hVBeuI=", + "lastModified": 1764578400, + "narHash": "sha256-8V0SpIcYyjpP+nAHfYJDof7CofLTwVVDo5QLZ0epjOQ=", "ref": "refs/heads/main", - "rev": "64a2c8a3743ea6897ecac6692fba8aebc3389fca", - "revCount": 26, + "rev": "bf17617899692c9c2bfebfce87320a4174e6dc28", + "revCount": 27, "type": "git", "url": "ssh://git@github.com/sonnenshift/battery-manager" }, diff --git a/flake.nix b/flake.nix index 951eef5..10b52ec 100644 --- a/flake.nix +++ b/flake.nix @@ -2,8 +2,7 @@ description = "tlater.net host configuration"; inputs = { - nixpkgs.url = "https://channels.nixos.org/nixos-25.05-small/nixexprs.tar.xz"; - nixpkgs-unstable.url = "https://channels.nixos.org/nixos-unstable/nixexprs.tar.xz"; + nixpkgs.url = "https://channels.nixos.org/nixos-25.11/nixexprs.tar.xz"; ## Nix/OS utilities @@ -138,10 +137,7 @@ packages.${system} = { default = vm.config.system.build.vm; } - // import ./pkgs { - pkgs = nixpkgs.legacyPackages.${system}; - flake-inputs = inputs; - }; + // import ./pkgs { pkgs = nixpkgs.legacyPackages.${system}; }; ################### # Utility scripts # diff --git a/modules/crowdsec/default.nix b/modules/crowdsec/default.nix index 44e6bc5..9cb26f9 100644 --- a/modules/crowdsec/default.nix +++ b/modules/crowdsec/default.nix @@ -271,7 +271,7 @@ in # To add completions; sadly need to hand-roll this since # neither `symlinkJoin` nor `buildEnv` have collision # handling. - (pkgs.runCommandNoCCLocal "cscli" { } '' + (pkgs.runCommandLocal "cscli" { } '' mkdir -p $out ln -s ${cscli}/bin $out/bin ln -s ${cfg.package}/share $out/share diff --git a/modules/crowdsec/remediations/cs-firewall-bouncer.nix b/modules/crowdsec/remediations/cs-firewall-bouncer.nix index 42accc6..bdc6da8 100644 --- a/modules/crowdsec/remediations/cs-firewall-bouncer.nix +++ b/modules/crowdsec/remediations/cs-firewall-bouncer.nix @@ -6,7 +6,7 @@ ... }: let - inherit (flake-inputs.self.packages.${pkgs.system}) crowdsec-firewall-bouncer; + inherit (flake-inputs.self.packages.${pkgs.stdenv.hostPlatform.system}) crowdsec-firewall-bouncer; crowdsecCfg = config.security.crowdsec; cfg = crowdsecCfg.remediationComponents.firewallBouncer; diff --git a/pkgs/default.nix b/pkgs/default.nix index 1ce8cd2..31335a6 100644 --- a/pkgs/default.nix +++ b/pkgs/default.nix @@ -1,8 +1,5 @@ -{ pkgs, flake-inputs }: -let - inherit (flake-inputs.nixpkgs-unstable.legacyPackages.${pkgs.system}) ast-grep; -in +{ pkgs }: pkgs.lib.packagesFromDirectoryRecursive { - callPackage = pkgs.lib.callPackageWith (pkgs // { inherit ast-grep; }); + inherit (pkgs) callPackage; directory = ./packages; } From 4b9da36a2502fc383684809ec9d5aceec746c24f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tristan=20Dani=C3=ABl=20Maat?= <tm@tlater.net> Date: Sun, 30 Nov 2025 15:53:28 +0800 Subject: [PATCH 22/24] feat(grafana): Use ntfy for alerts instead of matrix-hookshot --- configuration/services/conduit/default.nix | 5 +- .../services/conduit/matrix-hookshot.nix | 172 ------------------ configuration/services/metrics/grafana.nix | 13 ++ .../services/metrics/victoriametrics.nix | 4 - keys/production.yaml | 7 +- keys/staging.yaml | 7 +- 6 files changed, 18 insertions(+), 190 deletions(-) delete mode 100644 configuration/services/conduit/matrix-hookshot.nix diff --git a/configuration/services/conduit/default.nix b/configuration/services/conduit/default.nix index b6f8f27..a4c91d3 100644 --- a/configuration/services/conduit/default.nix +++ b/configuration/services/conduit/default.nix @@ -12,10 +12,7 @@ let turn-realm = "turn.${config.services.nginx.domain}"; in { - imports = [ - ./heisenbridge.nix - ./matrix-hookshot.nix - ]; + imports = [ ./heisenbridge.nix ]; networking.firewall = { allowedTCPPorts = [ diff --git a/configuration/services/conduit/matrix-hookshot.nix b/configuration/services/conduit/matrix-hookshot.nix deleted file mode 100644 index c1fec82..0000000 --- a/configuration/services/conduit/matrix-hookshot.nix +++ /dev/null @@ -1,172 +0,0 @@ -{ - pkgs, - lib, - config, - ... -}: -let - matrixLib = pkgs.callPackage ./lib.nix { }; - - cfg = config.services.matrix-hookshot; - conduitCfg = config.services.matrix-conduit; - - domain = conduitCfg.settings.global.server_name; - - registration = matrixLib.writeRegistrationScript { - id = "matrix-hookshot"; - url = "http://127.0.0.1:9993"; - sender_localpart = "hookshot"; - - namespaces = { - aliases = [ ]; - rooms = [ ]; - users = [ - { - regex = "@${cfg.settings.generic.userIdPrefix}.*:${domain}"; - exclusive = true; - } - ]; - }; - - # Encryption support - # TODO(tlater): Enable when - # https://github.com/matrix-org/matrix-hookshot/issues/1060 is - # fixed - # extraSettings = { - # "de.sorunome.msc2409.push_ephemeral" = true; - # push_ephemeral = true; - # "org.matrix.msc3202" = true; - # }; - - runtimeRegistration = "${cfg.registrationFile}"; - }; -in -{ - # users = { - # users.matrix-hookshot = { - # home = "/run/matrix-hookshot"; - # group = "matrix-hookshot"; - # isSystemUser = true; - # }; - - # groups.matrix-hookshot = { }; - # }; - - systemd.services.matrix-hookshot = { - serviceConfig = { - Type = lib.mkForce "exec"; - - LoadCredential = "matrix-hookshot:/run/secrets/matrix-hookshot"; - inherit (registration) ExecStartPre; - - # Some library in matrix-hookshot wants a home directory - Environment = [ "HOME=/run/matrix-hookshot" ]; - - # User = "matrix-hookshot"; - DynamicUser = true; - StateDirectory = "matrix-hookshot"; - RuntimeDirectory = "matrix-hookshot"; - RuntimeDirectoryMode = "0700"; - - RestrictNamespaces = true; - PrivateUsers = true; - ProtectHostname = true; - ProtectClock = true; - ProtectKernelTunables = true; - ProtectKernelModules = true; - ProtectKernelLogs = true; - ProtectControlGroups = true; - RestrictAddressFamilies = [ - # "AF_UNIX" - "AF_INET" - "AF_INET6" - ]; - LockPersonality = true; - RestrictRealtime = true; - ProtectProc = "invisible"; - ProcSubset = "pid"; - UMask = 77; - }; - }; - - # services.redis.servers.matrix-hookshot = { - # enable = true; - # user = "matrix-hookshot"; - # }; - - services.matrix-hookshot = { - enable = true; - - serviceDependencies = [ "conduit.service" ]; - - registrationFile = "/run/matrix-hookshot/registration.yaml"; - - settings = { - bridge = { - inherit domain; - url = "http://localhost:${toString conduitCfg.settings.global.port}"; - mediaUrl = conduitCfg.settings.global.well_known.client; - port = 9993; - bindAddress = "127.0.0.1"; - }; - - bot.displayname = "Hookshot"; - - # cache.redisUri = "redis://${config.services.redis.servers.matrix-hookshot.unixSocket}"; - - generic = { - enabled = true; - outbound = false; - # Only allow webhooks from localhost for the moment - urlPrefix = "http://127.0.0.1:9000/webhook"; - userIdPrefix = "_webhooks_"; - allowJsTransformationFunctions = true; - }; - - # TODO(tlater): Enable when - # https://github.com/matrix-org/matrix-hookshot/issues/1060 is - # fixed - # encryption.storagePath = "/var/lib/matrix-hookshot/cryptostore"; - - permissions = [ - { - actor = "matrix.tlater.net"; - services = [ - { - service = "*"; - level = "notifications"; - } - ]; - } - { - actor = "@tlater:matrix.tlater.net"; - services = [ - { - service = "*"; - level = "admin"; - } - ]; - } - ]; - - listeners = [ - { - port = 9000; - resources = [ "webhooks" ]; - } - { - port = 9001; - resources = [ "metrics" ]; - } - ]; - - metrics.enabled = true; - }; - }; - - sops.secrets = { - # Accessed via systemd cred through /run/secrets/matrix-hookshot - "matrix-hookshot/as-token" = { }; - "matrix-hookshot/hs-token" = { }; - }; -} diff --git a/configuration/services/metrics/grafana.nix b/configuration/services/metrics/grafana.nix index 765a364..078f27c 100644 --- a/configuration/services/metrics/grafana.nix +++ b/configuration/services/metrics/grafana.nix @@ -57,6 +57,19 @@ in access = "proxy"; } ]; + + alerting.contactPoints.settings.contactPoints = [ + { + name = "ntfy"; + receivers = [ + { + uid = "ntfy"; + type = "webhook"; + settings.url = "http://${config.services.ntfy-sh.settings.listen-http}/local-alerts?template=grafana"; + } + ]; + } + ]; }; }; diff --git a/configuration/services/metrics/victoriametrics.nix b/configuration/services/metrics/victoriametrics.nix index 96e09e5..71741b5 100644 --- a/configuration/services/metrics/victoriametrics.nix +++ b/configuration/services/metrics/victoriametrics.nix @@ -89,10 +89,6 @@ in "127.0.0.1:8082" ]; - # Configured in the hookshot listeners, but it's hard to filter - # the correct values out of that config. - matrixHookshot.targets = [ "127.0.0.1:9001" ]; - victorialogs.targets = [ config.services.victorialogs.bindAddress ]; }; }; diff --git a/keys/production.yaml b/keys/production.yaml index ccbee64..6a60c40 100644 --- a/keys/production.yaml +++ b/keys/production.yaml @@ -20,9 +20,6 @@ steam: heisenbridge: as-token: ENC[AES256_GCM,data:+2yo6T18j34622H8ZWblAFB2phLw1q0k0vUQEZ5sFj7dQaRnkEiAMi0R3p17Zq0pOtGEC0RRZuPLYkcZ1oKP0w==,iv:lGwrQYp//FufpmJocrLIVyy9RK7lEEVcpAi0wmkjr34=,tag:yV06UbhAYJQz36O2XdhY+A==,type:str] hs-token: ENC[AES256_GCM,data:u52WpkQFd/J7JFoE/rfNluebyZQLOokvkVdL7+AEAvrhJhrkJli1ztkD79lbC+6tGUH4tT3T+nX9wvGKnrRUQg==,iv:as+9fVuvMg2IoE2WIKD9mHi+znhNcWRh5Zq+yr0xcDQ=,tag:mZ7fh7U0MfgI8hyq/28Bcg==,type:str] -matrix-hookshot: - as-token: ENC[AES256_GCM,data:nXTanPhDyDF7R3AllLqpM5dzljBrHwlh1KJnTGIi5PhbDY2lPj4+uXkMEwvm1u+hQjPyM7vKZPfK+0/dms6Y7A==,iv:fSakJN+yai0gfOJKFxxaxgyUtk0pNmIeqVgrdq92/24=,tag:Qc7+SUnm5/Nq5+QIScR9kQ==,type:str] - hs-token: ENC[AES256_GCM,data:Bwyj0JTTN0NNnwOs1zA8CqbtZSNcvlINeT7QVc2eJiHda92J6vQk7bSxy6KuqCN9DxlUsK13ggYjNORY2vic5w==,iv:Npnp8arYQ3Yb6CXrnKgE03hD7ZjGINPa/DwFI8D+5tA=,tag:FqNE6yI0nF4puEUw9MGAjQ==,type:str] wireguard: server-key: ENC[AES256_GCM,data:mXb7ZznJHf5CgV8rI4uzPBATMRbmd7LimgtCkQM9kAjbIaGwUBqJZBN3fXs=,iv:3Po1Orinzov9rnEm9cLzgJY1PeD+5Jl9115MriABHh8=,tag:E/2CjDO1JCvJzxCnqKcNyw==,type:str] restic: @@ -32,8 +29,8 @@ turn: env: ENC[AES256_GCM,data:kt5nhVo9pb/ZbPUEcqSYXxN9YMgQKnFb5VRfFFS/qoIaJ73uD2fuJKqcxAyVRrdLqnSAWSQBgTgunBzdP7xqLAK2qt8DYAQWHkIe9uxFbSXZpdmw,iv:9lq6SFwTFN4GGm6gPiJpUMasMdnHVF6XLGYrsyG3kjU=,tag:428Qf9DOiiHt/Wjb188b8g==,type:str] secret: ENC[AES256_GCM,data:si7ee6Xfhdgdyzbp6aQpF7pz3TmTBb7iQ82lRPVXNDg9JfHI+lbmgAsSnRLX5qMCA6P9R045sSMosqidL8QwRg==,iv:SrhpZKK8D45yxCEfDb9P3TwtA14+qEI+wcRqcN/a6pw=,tag:PiwV+mOL9xHJgJft6sc61g==,type:str] sops: - lastmodified: "2025-11-29T14:52:24Z" - mac: ENC[AES256_GCM,data:RC18s48jxRFQMtbmu74P7G4uhm2yHk9TB0wN7z4g8SNE3nfkYMvHAJqPr3A3dO+T33zkTFcSRm7fhWItUahTCW3fO10u6kDvWbnyjlSuAy86Tkz2iqeW4iSOzKswDptAgb/B+juAHhEMxDnkG5vpPlIcD0SVP89NlflXftogOqw=,iv:2vN2TJvzePzBJfUeBxvGXwGmRsB5sopqyWm9uUv/rzA=,tag:C6UOWrUxVsRMFncL1y1eTQ==,type:str] + lastmodified: "2025-12-01T11:39:17Z" + mac: ENC[AES256_GCM,data:TwhGOW/V9/IoBifzh1MSwy/ff7ONTnxEmwERD8Yl2E27WG/6dTVz0/nIlZ8KsEKLC6vB2m+sJT+14Q9KCj4Cn/bWV1PmhytktGPxLQpgF55+pZlSK1aLUPLq0hwE93b4MAeOvzoOXtCQguh1dsB2RkinabFoMeZ2xJ7Kc+jHlfA=,iv:Ri8aEA4tssGDv2UuKeza8vs94IovM9GARLIEapb9Ya0=,tag:MDgAffj7ndmMwpw7mBXNRg==,type:str] pgp: - created_at: "2025-10-03T21:38:48Z" enc: |- diff --git a/keys/staging.yaml b/keys/staging.yaml index 20ee3db..b5c8533 100644 --- a/keys/staging.yaml +++ b/keys/staging.yaml @@ -21,9 +21,6 @@ steam: heisenbridge: as-token: ENC[AES256_GCM,data:tXbOeo7nv8I=,iv:wJAKcOXX9nGIw4n38ThOoj29u7dUWhsxSQG/p79JlEw=,tag:rTVaGS2UuWcea1uBa8YX2g==,type:str] hs-token: ENC[AES256_GCM,data:VBwvwomv0Xg=,iv:q6INtJ+rg+QiXj8uBdBzQYQZUBBXp+9odxDHwvu8Jxc=,tag:XKhm8nxygAkKaiVPJ2Fcdg==,type:str] -matrix-hookshot: - as-token: ENC[AES256_GCM,data:uSUOo4f2KqA=,iv:Xb9G8Ecv6m59m51kDw2bOfq3SMJt4g9/6/EdH74R+KM=,tag:K9MSfO2c2Y4rlf0eYrmTnw==,type:str] - hs-token: ENC[AES256_GCM,data:0KsyA06InL4=,iv:zAR0Y1fk8SyodcSLBHlQ8I+BAmttz9Hkd8Q3OREFqs4=,tag:t1Et8N/3seq95DeGoUd7Sw==,type:str] wireguard: server-key: ENC[AES256_GCM,data:FvY897XdKoa/mckE8JQLCkklsnYD6Wz1wpsu5t3uhEnW3iarnDQxF9msuYU=,iv:jqGXfekM+Vs+J9b5nlZ5Skd1ZKHajoUo2Dc4tMYPm1w=,tag:EehikjI/FCU8wqtpvJRamQ==,type:str] restic: @@ -33,8 +30,8 @@ turn: env: ENC[AES256_GCM,data:xjIz/AY109lyiL5N01p5T3HcYco/rM5CJSRTtg==,iv:16bW6OpyOK/QL0QPGQp/Baa9xyT8E3ZsYkwqmjuofk0=,tag:J5re3uKxIykw3YunvQWBgg==,type:str] secret: ENC[AES256_GCM,data:eQ7dAocoZtg=,iv:fgzjTPv30WqTKlLy+yMn5MsKQgjhPnwlGFFwYEg3gWs=,tag:1ze33U1NBkgMX/9SiaBNQg==,type:str] sops: - lastmodified: "2025-11-29T11:54:33Z" - mac: ENC[AES256_GCM,data:SaTvwxfARVou/ZjrWfdC8J6je8l89Zuumdz7PkmY2Tl2CQVxZmEt4AyV4bWiCtWhJmfH1Qa8m4Q+DyqimjapgYT5cUB1yxlknp233bB/+5C5k3KozU2hmh80KYgR496FtQvI74p0qw/lw00CGCR3WHNcIc0dbTiDzC90HlOpafg=,iv:vxMCAjpgyWvxk18LalmFhwOb5b2ThCDq1KTaX2OPvpM=,tag:QMA+tC4hs/FBnuVDye38Vg==,type:str] + lastmodified: "2025-12-01T11:39:26Z" + mac: ENC[AES256_GCM,data:11VQAYk8Am0k8OO6BtU17qpuEhcJ8ylRhJWQNHVAsmi5BCFjD1zU3NkWhtSstPrBcqHMenG+9XuEzpNnbccHI2ru0qlILsQvNj5OKo96FnvYtzApYlApoAzOetCx08Lfxa4RGLN/XCUSuccjBIU2PZRWEK+z+Cm1wHUFeqc1xPc=,iv:6y9j55Cld+GoOVGWAqsEgURRna6dHA2mGZwHVA+ZOE8=,tag:bSZi3nYmYrn3nFT2+RBPUQ==,type:str] pgp: - created_at: "2025-10-03T21:38:26Z" enc: |- From 4d773ef9e23e65c36899d35dc5fb82c0e3e534fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tristan=20Dani=C3=ABl=20Maat?= <tm@tlater.net> Date: Mon, 1 Dec 2025 20:06:01 +0800 Subject: [PATCH 23/24] feat(immich): Migrate to vectorchord --- configuration/services/immich.nix | 3 +++ 1 file changed, 3 insertions(+) diff --git a/configuration/services/immich.nix b/configuration/services/immich.nix index 39673d0..1255490 100644 --- a/configuration/services/immich.nix +++ b/configuration/services/immich.nix @@ -18,6 +18,9 @@ in enable = true; settings.server.externalDomain = "https://${hostName}"; + # We're using vectorchord now + database.enableVectors = false; + environment.IMMICH_TELEMETRY_INCLUDE = "all"; }; From 35c74ed36980f7f5becd78a6395680795c574276 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tristan=20Dani=C3=ABl=20Maat?= <tm@tlater.net> Date: Mon, 1 Dec 2025 20:08:07 +0800 Subject: [PATCH 24/24] bump(flake.lock): Update other inputs --- flake.lock | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/flake.lock b/flake.lock index 9ca0388..d5305b0 100644 --- a/flake.lock +++ b/flake.lock @@ -123,11 +123,11 @@ ] }, "locked": { - "lastModified": 1762276996, - "narHash": "sha256-TtcPgPmp2f0FAnc+DMEw4ardEgv1SGNR3/WFGH0N19M=", + "lastModified": 1764350888, + "narHash": "sha256-6Rp18zavTlnlZzcoLoBTJMBahL2FycVkw2rAEs3cQvo=", "owner": "nix-community", "repo": "disko", - "rev": "af087d076d3860760b3323f6b583f4d828c1ac17", + "rev": "2055a08fd0e2fd41318279a5355eb8a161accf26", "type": "github" }, "original": { @@ -201,11 +201,11 @@ ] }, "locked": { - "lastModified": 1761916399, - "narHash": "sha256-wLZ8km5ftKlIDdHJrFiDQivXc5b+7DRxmBp2347H5g8=", + "lastModified": 1764578815, + "narHash": "sha256-WZ8+pH/cLjv3geonV3VFwtfa8IuTkPHb60a1ACQpOmc=", "owner": "reckenrode", "repo": "nix-foundryvtt", - "rev": "8cceb7af3dfbe465b5108db5c098b097edf85790", + "rev": "1b875fb942c4ef926fd7aade7db327be363f7179", "type": "github" }, "original": { @@ -344,11 +344,11 @@ ] }, "locked": { - "lastModified": 1763607916, - "narHash": "sha256-VefBA1JWRXM929mBAFohFUtQJLUnEwZ2vmYUNkFnSjE=", + "lastModified": 1764483358, + "narHash": "sha256-EyyvCzXoHrbL467YSsQBTWWg4sR96MH1sPpKoSOelB4=", "owner": "Mic92", "repo": "sops-nix", - "rev": "877bb495a6f8faf0d89fc10bd142c4b7ed2bcc0b", + "rev": "5aca6ff67264321d47856a2ed183729271107c9c", "type": "github" }, "original": {