Files
felhom.eu/documentation/audits/release-2026-10-30/p3-cloudflare/PLAN.md
T

6.9 KiB

D35 / R-1228 — the website and the installer on Cloudflare (plan, 2026-10-30)

Operator ruling D35 = YES (2026-10-23): the static website and the installer are served from Cloudflare like the ISO; the hub and Gitea stay on DooPlex (R-1228's option B). This plan was written before anything on Cloudflare changed.

1. What is served today (read from the source, 2026-10-30)

Address Served by What a box, the ISO or a person fetches
felhom.eu, www.felhom.eu (CNAME → the home line, not proxied) DooPlex: nginx + git-sync of website/ at main (manifests/webpage.yaml) the 19 Hungarian and 11 English pages, sitemap.xml, robots.txt, assets/
felhom.eu/scripts/… DooPlex: a second git-sync of scripts/ at the tag installer-v1.36.1 /scripts/felhom-host-install.sh — the only /scripts/ file anything fetches: the ISO's first boot (scripts/iso/felhom-bootstrap.sh:168, FELHOM_INSTALL_URL default) and the hub's day-0 command (customer_unified.html). sha256 e18cdd17…aa1f1 = the tag's
felhom.eu/api/contact DooPlex: contact-mailer (manifests/contact-mailer.yaml, Resend) the contact form's POST (kapcsolat.html, en/contact.html) — the one dynamic part
stats.felhom.eu DooPlex: Umami the analytics script every page loads (a separate name — unchanged)
iso.felhom.eu Cloudflare R2 (proxied CNAME) the installer ISO — already off DooPlex
hub.felhom.eu, gitea.dooplex.hu DooPlex the box's claim and reports; the agent download — stay on DooPlex (D35)

Inside the installer every other address is the hub, Gitea (the agent package) and download.proxmox.com — nothing else from felhom.eu.

2. Where each goes — Cloudflare Pages, one project felhom-site

Pick: Cloudflare Pages, not R2. Fewest moving parts: Pages serves clean URLs (/kapcsolat → kapcsolat.html), the site's 404.html with status 404, directory indexes (/en/) and compression with no configuration — exactly what nginx's try_files $uri $uri.html $uri/ does today. R2 behind a custom domain serves objects by exact key only: no clean URLs, no index, no 404 page, so it would need a Worker in front — one more piece to own.

  • / ← website/ from origin/main; /scripts/ ← scripts/ from the installer tag named in manifests/webpage.yaml (the same two refs, the same files as DooPlex — 487 files, 25 MB).
  • The contact form cannot be static. It stays on DooPlex (contact-mailer, which already answers CORS for https://felhom.eu). After the switch felhom.eu/api/contact would reach Cloudflare, not DooPlex, so the form needs its own name on DooPlex — e.g. api.felhom.eu → the same ingress, and the form's formEndpoint pointing there. That is a DooPlex ingress change and a DNS name: it waits for the operator's word (the switch STOP). Without it the form shows an error on Cloudflare; the page's mailto:info@felhom.eu link still works.
  • stats.felhom.eu, files.felhom.eu, hub.felhom.eu, ntfy.felhom.eu: unchanged.

3. How a release reaches it

scripts/publish-site-cloudflare.sh (run under sec run CF_PAGES_TOKEN --): stages both trees from git, REFUSES when the staged installer is not the tag's byte for byte, when a must-have page is missing, or when the manifest names more than one tag; --dry-run stages and prints without uploading. The upload is wrangler in a pinned node container; the token reaches it as an environment variable name only.

  • A website edit: push to main (DooPlex picks it up as before) AND run the script (Cloudflare). After the switch only the script matters.
  • An installer release: cut installer-v<X>, bump BOTH --refs in manifests/webpage.yaml (as today), then run the script — it reads the tag from the manifest, so the two copies cannot differ.

4. What stays on DooPlex

The hub (hub.felhom.eu), Gitea and the registry (gitea.dooplex.hu — the agent package a new install downloads), the contact mailer, Umami, FileBrowser, ntfy. A new install still needs DooPlex for: the hub's claim and its first report (hub.felhom.eu), and the agent package from Gitea. It no longer needs DooPlex to fetch the installer.

5. The switch (only after the operator's yes) and the way back

Switch: in the Pages project add the custom domains felhom.eu and www.felhom.eu; Cloudflare replaces the two CNAMEs to the home line with proxied records to felhom-site.pages.dev. Way back (7 days, DooPlex's webpage keeps running): set the two records back to CNAME dooplex.hopto.org, not proxied (the values in dns-before.json), and remove the two custom domains from the project. DooPlex's Let's Encrypt certificate for felhom.eu is valid until its next renewal; a renewal after the switch fails (HTTP-01 reaches Cloudflare), which is the reason the way back is 7 days and not open-ended.

6. Result (2026-10-30, after the plan)

  • Test address: Pages project felhom-site + preview.felhom.eu (proxied CNAME). sitecheck.sh: 30 of 30 (every page by its clean URL, the sitemap, robots, an asset, a missing page = 404, the installer's sha256 = the tag's). Control: the same check against DooPlex's felhom.eu before the switch, 30 of 30.
  • Install proof: throwaway VM 3461 on demo-hp (scratch NVMe, never the thin pool) from the public ISO 1.30.0 (sha256 = iso.felhom.eu's). Booted with the cable unplugged, FELHOM_INSTALL_URL pointed at the test address, then plugged in: the first boot reached the hub and registered as an unclaimed appliance (pairing mode — the installer is fetched after a bind, which is a session-only operator act). The installer fetched from the test address (sha256 = the tag's) passed --preflight-only (customer Tester4, read-only GET /config). After the switch the same preflight with the installer fetched from felhom.eu passed again. The full install was not run: it needs the operator's bind and would create Tester4's tunnel and off-site account. VM 3461 destroyed; the hub's unclaimed entry (uuid d3529d2d…) is the operator's to discard.
  • The contact form: api.felhom.eu (DNS to the home line, not proxied; ingress contact-mailer-api, only /api/contact, 20 requests a minute per client; certificate issued). Preflight from https://felhom.eu 204; one test message 200, and it arrived at info@felhom.eu (read through the mailbox).
  • Switch: 10:02:35 UTC (operator yes): felhom.eu and www → felhom-site.pages.dev, proxied; the project's two custom domains active at 10:03:47 (about 70 s of 522 in between). After: 30 of 30 on both names (one 522 on the first pass, then 30 of 30 twice), server: cloudflare, the installer's sha256 = installer-v1.36.1.
  • Undo (until 2026-11-06): switch-undo.txt holds the two record ids; set both back to CNAME dooplex.hopto.org, proxied: false, and remove the two custom domains from felhom-site. DooPlex's webpage still serves both names.