lnbits is the header_up shape; memos and mempool are the Tailscale MagicDNS
shape. 108 lines removed, 25 added.
mempool was the one playbook already reloading Caddy correctly
(systemd: state: reloaded rather than command: systemctl reload caddy), so
its end marker differed - the role's handler does the same thing.
Verified:
- lnbits: full --check, site task ok, byte-identical to the live file
- memos, mempool: --check --diff via --limit edge shows exactly one added
line each, the standardised MagicDNS comment. Both playbooks fail earlier
in check mode on their VM play ("Extract memos binary", the same
download-does-not-happen-in-check-mode artifact as forgejo), but the edits
are confined to the hosts: edge play - memos at line 169+, play 2 starts
at 159; mempool at 617+, play 2 starts at 606.
The added comment means the next real run of memos/mempool rewrites one
comment line. Those two host files were already stale against their
playbooks before this change.
All 14 site files on all 3 hosts still byte-identical.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The plain reverse_proxy shape. All three removed a byte-identical 23-line
block (verified by md5 of the diff with the service name normalised) and
gained the same 7-line include_role call. The caddy_sites_dir self-reference
goes with it.
Verified in check mode, nothing applied to the hosts yet:
- ntfy-emergency-app: site task ok, changed=0
- vaultwarden: site task ok; the one changed task is a pre-existing
always-restarts fail2ban step, identical before the edit
- forgejo: check mode cannot run this playbook at all - get_url does not
download in check mode so the next task fails on "Source /tmp/forgejo not
found". Confirmed identical before the edit. Covered instead by the
Stage 2 dry-run, which ran the role against vipy with forgejo's real
parameters and reported ok/unchanged.
All 14 site files on all 3 hosts still byte-identical. Real runs for these
three are still outstanding.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
First service on the role. 31 lines of copy-pasted Caddy plumbing become 7.
Verified: --check before and after the edit reports the same three unrelated
tasks as changed, so the edit introduces nothing. Real run leaves all 14 site
files on all 3 hosts byte-identical, and the blog still answers HTTP 200. A
second consecutive run reports the site task ok with the handler not firing.
Side effect worth noting: the playbook no longer has a perpetually-changed
task. `command: systemctl reload caddy` always reported changed; the role's
handler only fires when the file actually moves.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Replaces the four-task Caddy vhost block currently copy-pasted into 10
playbooks. Nothing calls it yet; this commit only adds the role.
Verified by rendering all 10 sites through the template and diffing against
what the current playbooks produce: 9 of 10 byte-identical. The tenth is
datum-gateway, where the resolvers comment is standardised, rewriting one
comment line Caddy ignores.
Then dry-run against the live hosts (--check, nothing written):
- vipy: forgejo, vaultwarden, lnbits, personal-blog, ntfy-emergency-app
all report ok/unchanged against the real files
- watchtower: ntfy renders identical via caddy_site_body, blank line and
{host}{uri} placeholders intact
- spacey: headscale renders identical when given the config that is
actually running
- memos, mempool, datum-gateway report changed - the comment, as expected
All 14 site files on all 3 hosts confirmed unchanged afterwards.
Two things the build turned up:
- Ansible does not template dict *keys*, so caddy_site_basic_auth is a list
of {user, hash}. As a dict, a Jinja username passes through literally.
The assert refuses a mapping.
- `caddy validate` does accept a single site fragment - rc=0 on a good one,
rc=1 with a line number on a broken one. This was the plan's one untested
claim. A failed validate leaves the live file untouched.
The reload is now a handler, so it fires once at end of play rather than
immediately; anything needing the new config live mid-play must
flush_handlers first.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
services/caddy_playbook.yml was the one play still targeting a location
group (vps) rather than a role group. The two coincide today — vps is
exactly vipy, watchtower and spacey, the three hosts with
/etc/caddy/sites-enabled — but adding a fourth VPS that does not run
Caddy would have silently pulled it into the play.
[caddy:children] is edge + monitoring + vpn_control. Verified the play
selects the same three machines before and after.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
README, both setup guides and the forgejo-runner notes now point at
archive/uptime_kuma/ instead of describing a live service.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
lucasheld.uptime_kuma was pinned but never used - every monitor was created by
hand-rolled Python. The uptime subdomain stays because the deprecated blocks
still template it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
These five plus the ntfy notification playbook assert on the credentials, so
they now fail immediately instead of running — deliberately, before anything is
installed. The banner says so and points at archive/uptime_kuma/.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Removing the credentials would otherwise break these playbooks mid-deploy: they
template uptime_kuma_password with no assert to stop them first. 100 tasks are
now guarded by uptime_kuma_enabled (false), so deployments run normally and the
monitoring sections skip. A further 28 tasks were already self-guarding on
monitor_setup/push_url being defined; verified that a skipped task's registered
variable makes those skip cleanly rather than error.
The blocks are kept on purpose — the health-check logic is the durable part and
should be rewired to whatever replaces Uptime Kuma.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Drops uptime_kuma_username/password from infra_secrets.yml and its group_vars
copy and example, plus a dead push token in nodito_secrets.yml that nothing
referenced. They remain in git history — rotation is what actually retires them.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Captured from the live instance rather than the repo: the playbooks created 17
monitors, the server had 75. The rest existed only in the UI. Push tokens are
excluded deliberately — they are live credentials.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>