- /privacy/ page covering the contact form (Formspree), Umami, GA4 and
server logs; linked from the footer
- Analytics.astro: GA4 alongside Umami, both limited to the production
host so staging and local builds are not counted
- Dockerfile ARGs + CI build args from repo Actions variables
GA_MEASUREMENT_ID and UMAMI_WEBSITE_ID (non-secret)
The contact form could not be made to work by configuration. Two reasons,
both fixed here.
The endpoint never reached the browser. k8s/configmap.yaml carried
PUBLIC_CONTACT_FORM_ENDPOINT, but the Deployment has no env/envFrom pointing
at that ConfigMap, so it was dead config. Wiring it up would not have helped
either: this is a static Astro build, so import.meta.env.PUBLIC_* is inlined
when the image is built and the pod only serves pre-built HTML. Anything the
browser needs has to arrive as a build arg. Add ARG/ENV to the Dockerfile and
pass --build-arg from CI, sourced from a CONTACT_FORM_ENDPOINT Actions
secret. Rewrite the ConfigMap as documentation of what the build expects,
with an explicit warning not to add envFrom.
Submission behaviour was also wrong for visitors. A native POST would have
navigated them to the form provider's page; post in the background with
fetch() and send them to our own /thank-you/ instead. On failure the form now
stays put, re-enables the button and offers the phone and email fallback,
rather than silently losing the inquiry. Add a honeypot (_gotcha, which
Formspree treats as spam) positioned off-screen rather than display:none, so
bots that skip hidden fields still fill it.
Read data-endpoint at submit time rather than caching it at init, keeping the
attribute the single source of truth.
Tests: replace the old contact assertion, which faked a native GET
navigation, with one that mocks the endpoint and exercises the real fetch
path including the posted payload. Add a case for endpoint failure.
typecheck 0/0/0, build 0/0, seo:lint clean on 20 pages, e2e 44 passed.
The 4 remaining failures are the pre-existing stale nav assertions in
docs/launch-checklist.md section 2.1, untouched here.
CI has been failing silently since 2026-08-03. The run for 9f1cbca did
execute (Gitea action_run #6, status=failure, 13m) but died before pushing an
image, so no package version exists for that commit and ArgoCD has been
serving the 2026-07-20 build ever since.
Cause: the "Ensure docker + kustomize are available" step ran
`apt-get install -y docker.io`. The runner's job image was node:18-bullseye
and Debian 11 went EOL, so debian-security stopped serving those package
versions. `apt-get update` still succeeds, so the failure surfaces only at
install time as 404s on docker.io, containerd, systemd and libcap2.
Fetch both tools as pinned static binaries instead. These have no distro
archive dependency, so the same rot cannot recur when bookworm ages out.
Verified in the real job environment: the static client installs and reaches
the host daemon through the mounted socket.
The runner job image has separately been moved to node:22-bookworm, which
also resolves the immediate breakage, but the static-binary approach is kept
as the durable fix.
Also refresh the prerequisites header: items 3 and 4 still described the
pre-ADR-0008 registry design (k3s now pulls over public HTTPS, and the
private package's pull auth comes from External Secrets).