Commit Graph
21 Commits
Author SHA1 Message Date
renovate-bot fd0d2ef301 chore(deps): update all non-major dependencies
ci/woodpecker/pr/ci-manifests-test Pipeline was successful
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
ci/woodpecker/pr/review-bot-test Pipeline was successful
2026-11-02 03:01:52 +00:00
k.renner 6accd1d3cd Merge pull request 'feat(authentik-gate): Traefik ForwardAuth, gate config on the Ingress' (#1429) from feat/authentik-gate-forwardauth into main
ci/woodpecker/push/build-ci-images Pipeline was successful
Reviewed-on: #1429
Reviewed-by: Review Bot <review-bot@renner.uno>
2026-10-23 07:48:22 +00:00
claude-botandClaude Opus 5.5 0b7a2b6c46 feat(authentik-gate): gate config lives on the Ingress itself
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
ci/woodpecker/pr/ci-manifests-test Pipeline was canceled
The gate is now declared on the app's own traefik Ingress: naming the
static Middleware authentik/gate (ForwardAuth against the embedded
outpost) in router.middlewares opts in, and its auth.renner.uno/*
annotations configure it. No generated route Ingress, ExternalName
Service or per-gate Middleware; the IngressClass authentik-gate is gone.

authentik-gate-sync keeps building the Authentik side (forward_single
provider on the embedded outpost, application, group policy) and the
backend CNP admitting Traefik, and sweeps the objects it generated
before. Traefik publishes the Ingress status itself.

The outpost's own paths (/outpost.goauthentik.io/) are one static
IngressRoute above the app routes. The embedded outpost answers 404 for
a host without a provider (checked live: auth/traefik with
X-Forwarded-Host no-such-gate.renner.uno), so a gate without its
provider stays closed.

docs.renner.uno points at paperless:80 behind redirect + gate (Paperless
Users) instead of the removed paperless-gate Service. Drops __pycache__
committed by mistake.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 09:40:27 +02:00
claude-botandClaude Opus 5.5 772311c574 feat(authentik-gate): Traefik ForwardAuth on the embedded outpost
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
ci/woodpecker/pr/ci-manifests-test Pipeline was successful
Each gate was a single-replica proxy outpost in series with Traefik, so
any outpost restart, drain or node loss took its app down. Gates now
run as Traefik ForwardAuth against Authentik's embedded outpost inside
authentik-server: the request path is Traefik -> app, and the only auth
dependency is authentik-server, which every login already needs.

Per gated Ingress the controller now creates a forward_single provider
attached to the embedded outpost, a ForwardAuth Middleware first in the
route's chain, a /outpost.goauthentik.io route to authentik-server, and
a CNP admitting Traefik to the backend pods. It removes the per-gate
outposts and their egress CNPs.

Identity headers: authResponseHeadersRegex makes Traefik delete every
client-sent X-authentik-* header before copying the outpost's answer
(traefik pkg/middlewares/auth/forward.go), so a gated backend only ever
sees identity Authentik issued. Traefik answers 500 if the outpost is
unreachable and drops a router whose Middleware is missing: fail closed.

The blueprint no longer declares the embedded outpost: an entry must
carry providers, and re-applying [] would detach every gate. The
controller owns its providers and authentik_host/_browser.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 09:16:30 +02:00
claude-botandClaude Opus 5.5 a3eb69ee20 docs: drop references to the deleted *.renner.uno wildcard
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
ci/woodpecker/pr/ci-manifests-test Pipeline was successful
The public Cloudflare wildcard CNAME *.renner.uno -> proxy.renner.uno and
its carve-out TXTs were removed on 2026-10-22. Public hosts now carry an
explicit CNAME to proxy.renner.uno created by external-dns from
dns.renner.uno/public; UDM records are opt-in via dns.renner.uno/internal.

Comment/doc text only; no config value changes:
- KumaMonitor, Ingress, HelmRelease and tofu comments that said hosts
  resolve via the wildcard or that external-dns-unifi "skips" public hosts.
- PrometheusRule descriptions (k8s-gateway, dns-resolution) and the
  matching promtool test strings.
- k8s-gateway cache comment: the cache tuning stays (dnsmasq refusal
  bursts, 5s lease TTLs); the wildcard-trap rationale is gone.
- hermes skill references, claude-workstation runbook, crowdsec bouncer
  defaults.

Not changed: kubernetes/apps/home-assistant/* (README rule 1 window) and
the wildcard sentence inside the CoreDNS Corefile data
(infrastructure/coredns-config), where an edit changes the ConfigMap.

Verified live before the edit: all 30 Kuma DNS monitors and all blackbox
DNS/HTTP probes green after the wildcard removal.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 22:08:07 +02:00
claude-botandClaude Opus 5.5 5bfa957f29 feat(dns): one consumer per label; public DNS as CNAME to proxy.renner.uno
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
ci/woodpecker/pr/ci-manifests-test Pipeline was successful
Step 2 of 2 of the label split (owner decision 2026-10-22). Requires
#1425 (step 1) to be applied first.

- external-dns-unifi: labelFilter dns.renner.uno/internal=true (opt-in).
  Every object it publishes today carries the label since step 1, so the
  UDM record set is unchanged.
- external-dns-cloudflare: labelFilter dns.renner.uno/public=true, default
  target proxy.renner.uno (the cloudflare-lb-sync load balancer) instead of
  the retired tunnel, no --cloudflare-proxied, policy sync. Each public
  host gets a DNS-only CNAME to proxy.renner.uno plus its TXT registry
  record; this is the prerequisite for removing the *.renner.uno wildcard.
- netbird-service-sync: reads netbird.renner.uno/public only. Every
  dns-public Ingress carries it since step 1, so the 21 proxy services are
  unchanged.
- git/gitea/gitea-mcp: add dns.renner.uno/public (public today via the
  wildcard; keep their LAN record via dns.renner.uno/internal).
- Drop comments describing the old public!=true filter.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 21:45:20 +02:00
claude-botandClaude Opus 5.5 0806bdfab5 feat(dns): add dns.renner.uno/internal and netbird.renner.uno/public labels
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
ci/woodpecker/pr/ci-manifests-test Pipeline was successful
Step 1 of 2 of the label split, owner decision 2026-10-22:
  dns.renner.uno/internal  -> UniFi (LAN) DNS via external-dns-unifi
  dns.renner.uno/public    -> Cloudflare DNS via external-dns-cloudflare
  netbird.renner.uno/public -> NetBird reverse-proxy service (netbird-service-sync)

Additive only; no consumer reads the new labels yet:
- dns.renner.uno/internal on every object external-dns-unifi publishes
  today (24 Ingresses incl. Grafana via chart values, 6 LoadBalancer
  Services = the 31 live UDM hostnames).
- netbird.renner.uno/public next to every dns.renner.uno/public label
  (20 Ingresses). netbird-service-sync already ORs both labels, so its
  desired set is unchanged.
authentik-gate-sync copies source labels to the generated *-gate Ingresses.

Step 2 flips external-dns-unifi to dns.renner.uno/internal=true,
netbird-service-sync to netbird.renner.uno/public only, and
external-dns-cloudflare to dns.renner.uno/public=true. It must merge only
after this change is applied, or external-dns-unifi (policy sync) would
delete LAN records.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 21:43:33 +02:00
k.renner 5f2c02306b Merge pull request 'fix(wordpress): move to PHP 8.5.12 for PHP2Shell' (#1422) from fix/wordpress-php85-php2shell into main
ci/woodpecker/cron/e2e Pipeline failed
ci/woodpecker/cron/security-scan Pipeline was successful
Reviewed-on: #1422
2026-10-22 10:00:11 +00:00
claude-botandClaude Opus 5.5 bbeaf622ed fix(wordpress): move to PHP 8.5.12 for PHP2Shell
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
ci/woodpecker/pr/ci-manifests-test Pipeline was successful
CVE-2026-109618 and CVE-2026-109619 (BSI BITS-H 2026-297981-1132) allow
unauthenticated RCE on every PHP before 8.5.12/8.4.27/8.3.36/8.2.35;
mod_php is the highest-risk variant. The running pod reports PHP 8.3.35
and www.renner.uno serves / to the internet without the Authentik gate.

Switch to the php8.5-apache variant of the same WordPress release,
pinned to the index digest rebuilt on 2026-10-22 after the PHP fix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 11:57:02 +02:00
claude-botandClaude Opus 5.5 e3e3b987fa feat(kuma-sync): ship the KumaStatusPage CRD, RBAC and the renner page
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
ci/woodpecker/pr/ci-manifests-test Pipeline was successful
The CRD and the ClusterRole rules land with the controller change, so
Flux applies them at merge, before the new kuma-sync image is built and
rolled out by the ImagePolicy; the new controller watches
KumaStatusPage and would not start without them.

- infrastructure/kuma-sync-crds: crd-kumastatuspages.yaml, a copy of
  config/crd with prune disabled, next to crd-kumamonitors.yaml.
- apps/kuma-sync/rbac.yaml: kumastatuspages, /status, /finalizers, as in
  config/rbac/role.yaml.
- apps/kuma-sync/statuspage-renner.yaml: the public page (slug renner,
  domain uptime.renner.uno) with the sections Services and Third-party,
  selected by the uptime.renner.uno/status-page-section label.
- The label on the 17 top-level service groups and the four third-party
  HTTP checks.

An envtest test validates the deployed page object against the CRD.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 21:15:57 +02:00
hermes-bot 58b59fbe43 feat(kuma): monitor every public service and the proxies, defined with their apps
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
ci/woodpecker/pr/ci-manifests-test Pipeline was successful
Every host the NetBird proxies serve (exactly the Ingresses labelled
dns.renner.uno/public or netbird.renner.uno/public; verified by SNI probe of
proxy-1 and proxy-2) gets a group with three kinds of check:

- DNS: cluster services have no own record, so the check asserts the
  *.renner.uno wildcard answers (CNAME == proxy.renner.uno, Cloudflare
  resolver, IPv6 first). External names (apex, proxies, third-party) assert
  their own AAAA/A record instead.
- HTTP reachability: a URL routed to the backend, redirects not followed,
  any status below 500 counts.
- Response validation: health/API JSON or a page keyword; services fully
  behind the Authentik gate assert the login page.

The proxies get their own group: proxy.renner.uno, and per instance its
DNS plus reachability and a Gatus health check pinned to that proxy (URL
host is the proxy, Host header status.renner.uno; Node derives SNI from the
Host header, so the proxy routes it like a client request).

Layout: monitors live in the kustomization of the app they monitor;
external ones (proxies, apex, third-party) in apps/monitoring. The CRD moves
to infrastructure/ so every app Kustomization can consume it; it carries
prune: disabled, so leaving kuma-sync's inventory does not delete it. The
old kuma-sync/monitors objects go; their Kuma monitors are recreated under
the new owner keys (history since this morning is lost).

App Kustomizations wait on their resources, and KumaMonitor Ready reflects
whether Uptime Kuma on the VPS is reachable. Each consuming Kustomization
therefore treats KumaMonitor as always current, so a Kuma outage cannot
stall Flux.

kuma-sync: dns monitors gain spec.dns.conditions (Kuma's "record"
variable with its string operators, ANDed), mapped both ways so drift is
detected and an emptied list clears Kuma's conditions. Until the new image
rolls out the controller drops the field and the DNS checks only test
resolvability.
2026-10-20 09:24:09 +00:00
claude-botandClaude Opus 5.5 67a1eb2ba5 feat(authentik): move every app onto the SSO login (stage 2)
Stage 2 of unifying the auth flows, after the llama and paperless trial
of stage 1 was confirmed:

- Brand authentik.srv.renner.uno: flow_authentication =
  sso-authentication (was NULL, i.e. the slug-first
  default-authentication-flow with password + MFA). Every provider
  without its own authentication_flow logs in through it, PVE and PBS
  included.
- The 7 blueprint OIDC providers (Home Assistant, NetBird,
  Audiobookshelf, Immich, Gitea, Grafana, Matrix MAS): authentication
  flow unset (brand), authorization implicit consent.
- authentik-gate-sync: default authorization flow implicit consent, via
  DEFAULT_AUTHORIZATION_FLOW on the Deployment and in the code; the
  per-gate login/authorization annotations are dropped. Paperless keeps
  paperless-authorization.

Every flow referenced here already exists, so the gate controller does
not hit the missing-flow removal seen in stage 1. Stage 3 removes the
passkey-only flows once nothing references them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 10:41:17 +02:00
claude-botandClaude Opus 5.5 01213d6ee0 feat(authentik-gate): migrate the remaining 12 gates to the authentik-gate class
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
ci/woodpecker/pr/ci-manifests-test Pipeline was successful
paperless, shelly-manager, zone-configurator, esphome, zigbee2mqtt,
alertmanager, prometheus, wiki, paperclip, navidrome, lidarr and wordpress
now gate through authentik-gate-sync: each source Ingress names its real
backend and carries the old provider's settings as annotations (group,
login-flow, authorization-flow where it was implicit consent, paperless'
skip-path).

Removed per gate: the ExternalName Service, the egress CNP in authentik,
the namespace CNP rule admitting the old outpost, and the blueprint
provider/application/binding/outpost (state: absent, outpost ->
application -> provider). Groups stay declared without users lists;
membership is managed in Authentik.

Per-gate specifics:
- paperless: the docs.renner.uno alias backends the generated
  paperless-gate Service (its middleware redirects before the backend); the
  auth.renner.uno/outpost.goauthentik.io route is removed, proxy outposts
  take their callback on the app host.
- alertmanager / prometheus: source Ingresses renamed to alertmanager /
  prometheus; the controller generates the *-gate route Ingresses.
- wordpress-admin: gains a tls block for www.renner.uno (wordpress-tls,
  certificate still owned by the public Ingress), so the gate's external
  host stays https.
- wiki, paperclip: only the gate peer/rule leaves their shared CNPs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 21:37:26 +02:00
renovate-bot c7a4adf273 chore(deps): update all non-major dependencies
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
ci/woodpecker/pr/review-bot-test Pipeline was successful
ci/woodpecker/pr/ci-manifests-test Pipeline was successful
2026-10-12 02:03:24 +00:00
renovate-bot 68c0978660 chore(deps): update all non-major dependencies
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
ci/woodpecker/pr/ci-manifests-test Pipeline was successful
ci/woodpecker/pr/pr-review Pipeline was successful
2026-10-05 02:01:26 +00:00
atlas-botandClaude Opus 5.5 21a7583523 ci: cite YAML by anchor instead of line number, and guard it (REN-183)
ci/woodpecker/pr/ci-manifests-test Pipeline was successful
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
ci/woodpecker/pr/pr-review Pipeline was successful
Comments that pointed at other YAML as `file.yaml:N` went stale on any
edit above the cited line; three drift fixes had been merged by hand
(REN-181). The cited block now carries `# anchor: <name>` and the
comment cites `path.yaml#<name>`.

- Sweep: all 32 line-number citations in YAML comments converted.
  Several were already stale at head and now point at the right block
  (see PR). Vendored files (Flux gotk-components, IMC CRD) get no
  anchor; those two citations name the object/schema path instead.
- Guard: scripts/ci/yaml-citations.sh in ci-manifests-test.yml; fails
  on a line-number citation or a path#name whose file/anchor is gone.
  Self-test in ci-scripts-test.yml.
- CONTRIBUTING.md: the convention in three lines.

Comment-only under kubernetes/: every file parses to identical objects
before and after, so Flux applies nothing and no ConfigMap data changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 08:44:54 +00:00
claude-bot c59f0050b3 refactor(secrets): one OpenBao path for the one Hetzner S3 credential
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
ci/woodpecker/pr/ci-manifests-test Pipeline was successful
ci/woodpecker/pr/pr-review Pipeline was successful
#397 asked for the opposite: give woodpecker its own path so a rotation of
paperless's credentials would stop silently changing woodpecker's. The premise
does not hold. There is one Hetzner S3 key, and rotating it at Hetzner changes
it for every consumer no matter how many paths hold a copy. A fifth copy would
have bought nothing and made the separation look real.

Seven ExternalSecrets were reading it from four paths — seed/paperless (four
namespaces), seed/authentik, seed/immich, seed/trmnl. They now all read
infrastructure/hetzner-s3, which sits beside the existing
infrastructure/velero-hetzner.

That the four paths are byte-identical was verified rather than assumed, and
without reading them: SHA-256 over the three fields of each synced Secret
returns the same fingerprint in all seven namespaces. The same check says
infrastructure/velero-hetzner carries the same access key id, in a different
shape — a single `cloud` field holding an AWS credentials file.

Velero is deliberately not folded in. Moving it means templating that INI from
these fields, which is worth doing and not worth bundling with a path swap that
touches every database backup at once.

The reasoning lives once, in the paperless manifest, with the others pointing at
it. Repeating it six times is how six copies drift.

Kept from the note this replaces, because it was right and cost someone an
afternoon: the 403 recorded in #397 was a wrong-path error, not missing
permissions. The mount is `homelab`, so the path is homelab/seed/... —
`bao kv list homelab/seed` works with the identity available here.

homelab/infrastructure/hetzner-s3 now exists, copied from the paperless path
inside OpenBao so the value never left it, and fingerprints identical to its
source. Writes turned out to be possible all along: they fail through
openbao.renner.uno with a response the client cannot parse, and succeed against
the Service. That is worth knowing before the next person concludes, as I first
did, that the identity is read-only.

Closes #397
2026-08-27 21:02:28 +02:00
claude-botandClaude Opus 5 fb3724f603 fix(wordpress): route the outpost OAuth path so the gate is a gate, not a dead end
ci/woodpecker/pr/ci-manifests-test Pipeline was successful
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
ci/woodpecker/pr/pr-review Pipeline was successful
Verified against the deployed cluster: GET /wp-admin/ correctly 302s to
https://www.renner.uno/outpost.goauthentik.io/start?rd=... — and that URL
returned WordPress's 404 page, because /outpost.goauthentik.io fell through to
the public '/' prefix router.

The effect was that /wp-admin was unreachable rather than protected. An
attacker gains nothing, but neither does the editor the gate exists for.

paperless needs the same route and solves it with a separate Ingress on
auth.renner.uno (apps/authentik/paperless-gate-ingress.yaml). Here the whole
flow stays on www.renner.uno, so the path belongs in this rule set.

These endpoints are the login flow itself and are served by the outpost, so
routing them to it is not an authentication hole.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014giKN9N6e5J2DF8QZmVh98
2026-08-16 22:22:25 +02:00
claude-botandClaude Opus 5 faf79549ac fix(wordpress): remove add_filter from wp-config, route xmlrpc through the gate
ci/woodpecker/pr/ci-manifests-test Pipeline was successful
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
ci/woodpecker/pr/pr-review Pipeline was successful
The first deploy returned 500 on every request:

  PHP Fatal error: Uncaught Error: Call to undefined function add_filter()
  in /var/www/html/wp-config.php(128) : eval()'d code

WORDPRESS_CONFIG_EXTRA is eval'd inside wp-config.php, which runs long before
wp-settings.php loads the plugin API. add_filter() does not exist yet at that
point, so the call was fatal on every request.

Removing it exposed the second, worse half: the comment justified the filter by
claiming xmlrpc.php was 'blocked at the ingress'. It was not. The public router
matches '/' as a prefix, so xmlrpc.php was served like any other path — the
claim was written without being checked.

An Ingress has no deny verb, so xmlrpc.php now routes to the Authentik gate
alongside /wp-admin and /wp-login.php: anonymous callers meet Authentik instead
of the XML-RPC endpoint. Nothing on this site uses it — no Jetpack, no mobile
app, no pingbacks worth keeping.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014giKN9N6e5J2DF8QZmVh98
2026-08-16 22:11:25 +02:00
claude-botandClaude Opus 5 aa37fb0a0a fix(wordpress): correct the network-policy claim and make the Velero selector safe
ci/woodpecker/pr/ci-manifests-test Pipeline was successful
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
ci/woodpecker/pr/pr-review Pipeline was successful
Addresses the automated review on #492.

Blocking: two comments claimed the public/admin split was kept honest by
kubernetes/apps/wordpress/network-policy.yaml. That file does not exist — the
comment was written against the paperless template and never updated once the
namespace policy proved sufficient. The deeper point in the review is the real
one: a paperless-style workload-scoped policy would be a no-op here, because
traefik must reach the pod directly for every public page. The split is
enforced by the Ingress path rules and Authentik, not at the network layer,
and the comment now says so — an auditor looking for a network control would
otherwise have gone hunting for a file that was never there.

Velero: the schedule excluded the database PVC by app.kubernetes.io/instance,
a label the mariadb-operator was only assumed to set. A wrong assumption there
fails silently and sweeps the live database volume into the snapshot. Inverted
to select the content PVC by labels this repo sets on it, the same direction
openbao-daily already uses.

The Service-name assumption flagged as a suggestion is kept: the CRD's separate
primaryService/secondaryService fields indicate the base Service carries the
resource name, and a mismatch fails loudly at pod startup. Written down as a
first-deploy check rather than left implicit.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014giKN9N6e5J2DF8QZmVh98
2026-08-16 22:01:01 +02:00
claude-botandClaude Opus 5 b784ce3d38 feat(wordpress): host www.renner.uno in the cluster behind an Authentik gate
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
ci/woodpecker/pr/ci-manifests-test Pipeline was successful
ci/woodpecker/pr/pr-review Pipeline was successful
Migrates the Hetzner VM 'wordpress-hochzeit' into the cluster. The site is not
the frozen wedding archive it looks like: it carries an unpublished, fully
composed birth announcement from 2026-05-14, so wp-admin has to keep working
for a human author rather than being replaced by a Git workflow.

Split routing on one host: '/' goes straight to WordPress so the blog stays
publicly readable, while /wp-admin and /wp-login.php route through a dedicated
wordpress-gate outpost. The inverse -- gate the host and skip_path_regex the
public paths, as paperless-gate does -- would need a negative lookahead, and
the outpost's RE2 has none.

Gating all of /wp-admin is only safe because the dead wpgiftregistry plugin is
dropped in the migration: it was the sole emitter of ajaxurl on this site, and
of 121 admin-ajax hits in the current log window not one returned 200.

Core lives in the pinned image, not on the volume, so upgrading is a tag bump
in Git. Only wp-content is persisted.

Backups are split the same way CNPG/Barman and Velero are elsewhere: the
operator takes a logical dump to Hetzner Object Storage at 02:45, Velero takes
the wp-content PVC at 08:45 and skips the database volume -- a crash-consistent
snapshot of a running database is not a backup.

Group membership for the gate is owned by Git, as with the Home Assistant,
Shelly and zone-configurator gates. s.renner is the Authentik account for the
site's WordPress Editor, whose login there is still the pre-2019 s.birzer.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014giKN9N6e5J2DF8QZmVh98
2026-08-16 21:48:49 +02:00