diff --git a/CLAUDE.md b/CLAUDE.md index e72cde7..e81338a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -67,6 +67,12 @@ IP allocation convention: VMs `.10-.49`, containers `.50-.89`, incus `.90-.129` `flake.nix` applies patches from `/patches/` to nixpkgs before building (workaround for nix#3920). +### Service Dashboard & Monitoring + +When adding or removing a web-facing service, update both: +- **Gatus** (`common/server/gatus.nix`) — add/remove the endpoint monitor +- **Dashy** — add/remove the service entry from the dashboard config + ### Key Conventions - Uses `doas` instead of `sudo` everywhere @@ -83,3 +89,5 @@ IP allocation convention: VMs `.10-.49`, containers `.50-.89`, incus `.90-.129` ## Git Worktree Requirement When instructed to work in a git worktree (e.g., via `isolation: "worktree"` or told to use a worktree), you **MUST** do so. If you are unable to create or use a git worktree, you **MUST** stop work immediately and report the failure to the user. Do not fall back to working in the main working tree. + +When applying work from a git worktree back to the main branch, commit in the worktree first, then use `git cherry-pick` from the main working tree to bring the commit over. Do not use `git checkout` or `git apply` to copy files directly. Do **not** automatically apply worktree work to the main branch — always ask the user for approval first. diff --git a/common/server/default.nix b/common/server/default.nix index 23002d1..a4cd008 100644 --- a/common/server/default.nix +++ b/common/server/default.nix @@ -17,6 +17,6 @@ ./actualbudget.nix ./unifi.nix ./ntfy.nix - ./uptime-kuma.nix + ./gatus.nix ]; } diff --git a/common/server/gatus.nix b/common/server/gatus.nix new file mode 100644 index 0000000..9e83016 --- /dev/null +++ b/common/server/gatus.nix @@ -0,0 +1,146 @@ +{ lib, config, ... }: + +let + cfg = config.services.gatus; + port = 31103; +in +{ + options.services.gatus = { + hostname = lib.mkOption { + type = lib.types.str; + example = "status.example.com"; + }; + }; + + config = lib.mkIf cfg.enable { + services.gatus = { + environmentFile = "/run/agenix/ntfy-token"; + settings = { + storage = { + type = "sqlite"; + path = "/var/lib/gatus/data.db"; + }; + + web = { + address = "127.0.0.1"; + port = port; + }; + + alerting.ntfy = { + url = "https://ntfy.neet.dev"; + topic = "service-failures"; + priority = 4; + default-alert = { + enabled = true; + failure-threshold = 3; + success-threshold = 2; + send-on-resolved = true; + }; + token = "$NTFY_TOKEN"; + }; + + endpoints = [ + { + name = "Gitea"; + group = "services"; + url = "https://git.neet.dev"; + interval = "5m"; + conditions = [ + "[STATUS] == 200" + ]; + alerts = [{ type = "ntfy"; }]; + } + { + name = "The Lounge"; + group = "services"; + url = "https://irc.neet.dev"; + interval = "5m"; + conditions = [ + "[STATUS] == 200" + ]; + alerts = [{ type = "ntfy"; }]; + } + { + name = "ntfy"; + group = "services"; + url = "https://ntfy.neet.dev/v1/health"; + interval = "5m"; + conditions = [ + "[STATUS] == 200" + ]; + alerts = [{ type = "ntfy"; }]; + } + { + name = "Librechat"; + group = "services"; + url = "https://chat.neet.dev"; + interval = "5m"; + conditions = [ + "[STATUS] == 200" + ]; + alerts = [{ type = "ntfy"; }]; + } + { + name = "Owncast"; + group = "services"; + url = "https://live.neet.dev"; + interval = "5m"; + conditions = [ + "[STATUS] == 200" + ]; + alerts = [{ type = "ntfy"; }]; + } + { + name = "Nextcloud"; + group = "services"; + url = "https://neet.cloud"; + interval = "5m"; + conditions = [ + "[STATUS] == any(200, 302)" + ]; + alerts = [{ type = "ntfy"; }]; + } + { + name = "Element Web"; + group = "services"; + url = "https://chat.neet.space"; + interval = "5m"; + conditions = [ + "[STATUS] == 200" + ]; + alerts = [{ type = "ntfy"; }]; + } + { + name = "Mumble"; + group = "services"; + url = "tcp://voice.neet.space:23563"; + interval = "5m"; + conditions = [ + "[CONNECTED] == true" + ]; + alerts = [{ type = "ntfy"; }]; + } + { + name = "Navidrome"; + group = "services"; + url = "https://navidrome.neet.cloud"; + interval = "5m"; + conditions = [ + "[STATUS] == 200" + ]; + alerts = [{ type = "ntfy"; }]; + } + ]; + }; + }; + services.nginx.enable = true; + services.nginx.virtualHosts.${cfg.hostname} = { + enableACME = true; + forceSSL = true; + locations."/" = { + proxyPass = "http://127.0.0.1:${toString port}"; + proxyWebsockets = true; + }; + }; + }; +} diff --git a/common/server/uptime-kuma.nix b/common/server/uptime-kuma.nix deleted file mode 100644 index 856a32f..0000000 --- a/common/server/uptime-kuma.nix +++ /dev/null @@ -1,36 +0,0 @@ -{ lib, config, ... }: - -let - cfg = config.services.uptime-kuma; - port = 3001; -in -{ - options.services.uptime-kuma = { - hostname = lib.mkOption { - type = lib.types.str; - example = "status.example.com"; - }; - }; - - config = lib.mkIf cfg.enable { - services.uptime-kuma.settings = { - HOST = "127.0.0.1"; - PORT = toString port; - }; - - # backups - backup.group."uptime-kuma".paths = [ - "/var/lib/uptime-kuma" - ]; - - services.nginx.enable = true; - services.nginx.virtualHosts.${cfg.hostname} = { - enableACME = true; - forceSSL = true; - locations."/" = { - proxyPass = "http://127.0.0.1:${toString port}"; - proxyWebsockets = true; - }; - }; - }; -} diff --git a/machines/ponyo/default.nix b/machines/ponyo/default.nix index c60d127..277da33 100644 --- a/machines/ponyo/default.nix +++ b/machines/ponyo/default.nix @@ -114,6 +114,6 @@ services.ntfy-sh.hostname = "ntfy.neet.dev"; # uptime monitoring - services.uptime-kuma.enable = true; - services.uptime-kuma.hostname = "status.neet.dev"; + services.gatus.enable = true; + services.gatus.hostname = "status.neet.dev"; }