manatee: add self-hosted todo app

Wire the offline-first todo app (github: local /home/alex/code/todo) into
manatee, following the app-module convention:

- flake input `todo` (local git checkout for now; gitea URL commented) with
  nixpkgs following.
- hosts/manatee/modules/todo: mod.todo option importing the app's NixOS module;
  runs on 127.0.0.1:8091, secureCookies, token from agenix; the host owns the
  nginx vhost (todo.ppp.pm, forceSSL + useACMEHost) and homepage card.
- mod.todo.enable = true.
- certs: todo.ppp.pm DNS-01 cert via hetzner.
- home-assistant DNS updater: add `todo` to the Hetzner subdomain list.
- secrets: todo-token.age (encrypted access token) + recipients in secrets.nix.
This commit is contained in:
Alexander Heldt
2026-08-13 12:36:18 +00:00
parent d0ef7f5b7e
commit cffaec5fe8
8 changed files with 117 additions and 5 deletions
+13
View File
@@ -59,6 +59,19 @@
"--http-timeout=60"
];
};
"todo.ppp.pm" = {
dnsProvider = "hetzner";
environmentFile = config.age.secrets.hetzner-dns.path;
group = "nginx";
extraLegoFlags = [
"--dns.resolvers=1.1.1.1:53,8.8.8.8:53"
"--dns.propagation-wait=60s"
"--dns-timeout=60"
"--http-timeout=60"
];
};
};
};
+1
View File
@@ -27,6 +27,7 @@ in
disk-smart.enable = true;
puppy-tracker.enable = true;
solo-referee.enable = true;
todo.enable = true;
};
};
}
@@ -251,7 +251,7 @@ in
];
script = ''
SUBDOMAINS="ha komga romm puppy"
SUBDOMAINS="ha komga romm puppy todo"
INTERFACE="enp3s0"
CURRENT_IP=$(curl -s --fail --interface "$INTERFACE" ifconfig.me)
+58
View File
@@ -0,0 +1,58 @@
{
inputs,
lib,
config,
...
}:
let
enabled = config.mod.todo.enable;
nginxEnabled = config.mod.nginx.enable;
port = 8091;
in
{
options = {
mod.todo = {
enable = lib.mkEnableOption "Enable todo module";
};
};
imports = [
inputs.todo.nixosModules.default
];
config = lib.mkIf enabled {
mod.homepage.services = [
{
name = "Todo";
port = port;
description = "Offline-first todo";
# Login needs HTTPS (Secure cookies), so link to the public vhost.
url = "https://todo.ppp.pm";
}
];
services.todo = {
enable = true;
address = "127.0.0.1";
inherit port;
# Served publicly over HTTPS via the nginx vhost below.
secureCookies = true;
tokenFile = config.age.secrets."todo-token".path;
# sessionSecretFile is left unset: the app generates one in its state dir
# (/var/lib/todo) on first boot, mode 0600.
};
services.nginx = lib.mkIf nginxEnabled {
virtualHosts."todo.ppp.pm" = {
forceSSL = true;
useACMEHost = "todo.ppp.pm";
locations."/" = {
proxyPass = "http://127.0.0.1:${toString port}";
};
};
};
age.secrets."todo-token".file = ../../../../secrets/manatee/todo-token.age;
};
}