Campaign 12 ranked this first of eight gating candidates. It is built BEFORE the fixes it finds, because last night an off-the-shelf tool for a neighbouring class (deadcode, for C6) was made to prove itself first and found NEITHER of the two defects it was meant for. A gate nobody has watched fail has not been shown to work. scripts/wire_contract_gate.py, registered in repo_gates.py as --fast (no network, no container, so it runs in BOTH the pre-push hook and CI — the R-29 constraint). THE TEST. For every json tag reachable from a declared wire ROOT, does that literal tag occur anywhere in the receiving repo's production Go or templates? A tag occurring nowhere cannot be decoded by any struct, named OR anonymous. That last clause is why a string test is used instead of comparing struct to struct: Campaign 12's first attempt paired types by shape and false-positived badly, because the hub decodes one report through several ad-hoc anonymous structs. RESULT ON TODAY'S TREE: 210 tags checked across 3 declared wires, 51 skipped (generic / opaque / allowlisted), 40 CONVICTED. Captured verbatim in documentation/tests/wire-contract-gate-2026-08-08/ BEFORE.md, which is deliverable 1 of this session. The prompt for this session said "465 emitted tags, eight unreachable". Checked against the repo rather than quoted: R-260's wording was "at least eight DECISION-BEARING facts", not eight tags in total. The real count on the three declared wires is 40, and R-260's own census already listed more than eight. Recorded because this prompt's own rule 6 says not to quote a document as source. TWO THINGS THE CONTROL CAUGHT, both before the gate was trusted: 1. A SUBSTRING FALSE NEGATIVE. `grep -F healed_at` also matches `privsep_healed_at`, so a genuinely dropped field read as received — and R-260 named healed_at, so its absence from the output was the tell. Now a whole-token regex; healed_at is convicted. 2. dr_recipe IS NOT WHOLLY OPAQUE. The hub stores each half as json.RawMessage and re-emits nested shapes verbatim, so the LEAVES are genuinely not on this wire. But the TOP-LEVEL SECTION KEYS are decoded by hostHalfShape/appHalfShape, and those are ALLOW-LISTS: a section an emitter adds is silently dropped until named in both. That already cost `offsite_restic` (R-122). So the gate is opaque BELOW depth 1, not opaque — the sections are checked and pass. Self-test: `--selftest` plants an unreachable tag on a real root in a throwaway copy and asserts conviction. Verified: exit 1, planted tag named. Blind spots are in the module docstring AND in the gate's own output, because Campaign 12's C1 guard turned out blind to one of the three shapes it was written for: generic tag names are not checked; reachability of a NAME is not use of a VALUE; only declared ROOTS are covered, and the hub's desired-state (served as raw stored JSON, no typed emitter) and the agent local API are NOT. Allowlist entries carry a stated reason. A quiet exclusion is a dropped field with paperwork. Not pushed alone: the fixes follow in the next commit so main is never red on this check.
felhom.eu
Website, manifests, and infrastructure for Felhőm.eu — a managed home-server service for Hungarian households.
Overview
This repository contains:
- Website (
website/) — Static HTML pages served at felhom.eu - Kubernetes manifests (
manifests/) — All k3s deployments for the felhom.eu ecosystem - Assets (
website/assets/) — Logo, images, OG images
The website runs on a single-node k3s cluster alongside the rest of the Felhőm management infrastructure (Healthchecks, Umami analytics, contact mailer).
Branding
| Aspect | Value |
|---|---|
| Brand name | Felhőm.eu (with accent: ő) |
| Domain | felhom.eu (without accent — domain limitation) |
| Tagline | „Saját felhőd, saját szabályaid" |
| Controller product | Felhő Felügyelő (customer-facing name) |
| Controller code name | felhom-controller (backend/repo/container) |
| Language | Hungarian throughout all customer-facing content |
| Contact email | info@felhom.eu |
| Admin email | admin@felhom.eu |
Why "Felhőm"?
"Felhő" means "cloud" in Hungarian. The "m" suffix makes it possessive — "my cloud" (felhőm). The .eu domain is part of the brand identity and appears in the logo. The double meaning of "felhő" (tech cloud + weather cloud) is intentional and used in product naming (e.g., Felhő Felügyelő = "Cloud Supervisor/Inspector").
Website Pages
| File | URL | Purpose |
|---|---|---|
index.html |
/ |
Landing page — hero, services, app preview, backup intro, contact |
alkalmazasok.html |
/alkalmazasok |
Full application catalog (45+ apps with categories) |
technologiak.html |
/technologiak |
Technology stack explanation (Docker, Felhő Felügyelő, Proxmox, Kubernetes) |
biztonsagimentes.html |
/biztonsagimentes |
Backup strategy — 3-2-1 rule, monitoring, restore procedures |
gyik.html |
/gyik |
FAQ — structured Q&A with JSON-LD schema |
kapcsolat.html |
/kapcsolat |
Contact form + email, sends via contact-mailer API |
szolgaltatasok-nonpublic.html |
/szolgaltatasok-nonpublic |
Pricing/services page (not linked in nav, robots disallowed) |
All pages use:
- Clean URLs — nginx serves
.htmlfiles without extension (/gyik→gyik.html) - Unified CSS — each page contains the full CSS (no external stylesheet, for simplicity)
- Responsive design — mobile hamburger menu, responsive grids
- UTF-8 with BOM — all HTML files are saved as UTF-8-BOM for Hungarian character support
- Umami analytics — privacy-friendly tracking script on every page
Infrastructure
Architecture
Internet
│
▼
Cloudflare (DNS only, no proxy)
│
▼ CNAME → dooplex.hopto.org
│
Home network (dynamic IP via No-IP DynDNS)
│
▼ Port forward 80/443
│
k3s cluster (single node)
├── nginx-ingress (TLS termination via cert-manager + Let's Encrypt)
│
├── felhom-system namespace:
│ ├── felhom-webpage (nginx + git-sync sidecar)
│ ├── filebrowser (files.felhom.eu — website file management)
│ ├── contact-mailer (Go app — /api/contact endpoint)
│ ├── umami + umami-db (stats.felhom.eu — web analytics)
│ └── healthchecks (status.felhom.eu — monitoring)
│
└── cert-manager (letsencrypt-prod cluster issuer)
Kubernetes Manifests
| Manifest | Services | Subdomains |
|---|---|---|
webpage.yaml |
nginx (website), FileBrowser, git-sync | felhom.eu, www.felhom.eu, files.felhom.eu |
contact-mailer.yaml |
Go HTTP server for contact form | felhom.eu/api/* (path-based routing) |
umami.yaml |
Umami v3 + PostgreSQL | stats.felhom.eu |
healthchecks.yaml |
Healthchecks | status.felhom.eu |
Website Deployment
The website uses a git-sync sidecar pattern:
git-synccontainer polls this repository (sparse checkout:/website/only)- Syncs to a shared
emptyDirvolume nginxcontainer serves from the synced content- Changes pushed to this repo are live within minutes (no manual deployment)
FileBrowser at files.felhom.eu provides a web UI for quick edits to website files (emergency fixes, asset uploads) without needing git. It writes to a Longhorn PVC that the website nginx also reads from.
Storage
All persistent data uses Longhorn distributed storage:
filebrowser-files(1Gi, ReadWriteMany) — website filesfilebrowser-db(100Mi) — FileBrowser SQLite databaseumami-db-data(2Gi) — Umami PostgreSQL datahealthchecks-data(1Gi) — Healthchecks SQLite data
DNS Configuration (Cloudflare)
Domain: felhom.eu — Cloudflare DNS (free plan), DNS only mode (no proxy/orange cloud).
Records
| Type | Name | Content | Notes |
|---|---|---|---|
| CNAME | felhom.eu |
dooplex.hopto.org | Main website |
| CNAME | www |
dooplex.hopto.org | www redirect |
| CNAME | files |
dooplex.hopto.org | FileBrowser |
| CNAME | stats |
dooplex.hopto.org | Umami analytics |
| CNAME | status |
dooplex.hopto.org | Healthchecks |
| CNAME | ntfy |
dooplex.hopto.org | Push notifications |
| MX | felhom.eu |
route{1,2,3}.mx.cloudflare.net | Incoming email → Cloudflare Email Routing |
| MX | send |
feedback-smtp.eu-west-1.amazonses.com | Resend sending domain |
| TXT | felhom.eu |
v=spf1 include:_spf.mx.clo... |
SPF for Cloudflare |
| TXT | send |
v=spf1 include:amazonses... |
SPF for Resend |
| TXT | cf2024-1._domainkey |
DKIM for Cloudflare Email Routing | |
| TXT | resend._domainkey |
DKIM for Resend | |
| TXT | _dmarc |
v=DMARC1; p=none; |
DMARC policy |
| TXT | felhom.eu |
google-site-verification=... |
Google Search Console |
Incoming Email
Cloudflare Email Routing (free) handles all incoming mail:
info@felhom.eu→ forwarded to personal Gmailadmin@felhom.eu→ forwarded to personal Gmail- Catch-all → not configured
Outgoing Email (Transactional)
Resend (free tier) handles outgoing email via API:
- Contact form submissions → sends formatted email to
info@felhom.eu - Healthchecks alerts → sends to
admin@felhom.eu - Sending domain:
send.felhom.eu(verified with SPF, DKIM) - From address:
Felhom.eu <info@felhom.eu>
Contact Form Flow
- User fills form on
/kapcsolat - JavaScript POST to
/api/contact contact-mailer(Go, in k3s) validates + calls Resend API- Email delivered to
info@felhom.euvia Resend → Cloudflare Email Routing → Gmail
SEO
Google Search Console
- Property:
https://felhom.eu - Verified via DNS TXT record
- Sitemap submitted:
https://felhom.eu/sitemap.xml - 7 pages indexed (all public pages)
On-Page SEO
Every page includes:
<title>with Hungarian keywords + brand<meta name="description">with unique content per page<meta name="keywords">with relevant Hungarian terms<link rel="canonical">to prevent duplicate content- Open Graph tags (og:title, og:description, og:image, og:locale=hu_HU)
- Twitter Card tags (summary_large_image)
- JSON-LD structured data (LocalBusiness on index, Article on technologiak, FAQPage on gyik)
Technical SEO
robots.txt— allows all, disallows/szolgaltatasok-nonpublic, includes sitemap URLsitemap.xml— lists all 6 public pages with priority + changefreq- Clean URLs (no
.htmlextensions) - Static asset caching (7 day expiry for CSS/JS/images)
- Security headers (X-Frame-Options, X-Content-Type-Options)
Analytics
Umami v3 (self-hosted, privacy-focused):
- Dashboard:
https://stats.felhom.eu - Tracking script:
<script defer src="https://stats.felhom.eu/script.js" data-website-id="d419db57-..."> - Cookie-free, GDPR compliant — no consent banner needed
- Backend: dedicated PostgreSQL instance in k3s
Monitoring
Healthchecks (self-hosted):
- Dashboard:
https://status.felhom.eu - Monitors backup jobs, service health
- Sends email alerts via Resend when checks fail
Development Workflow
Quick content edits
- Log into FileBrowser at
https://files.felhom.eu - Edit HTML files directly
- Changes are live immediately
Standard workflow
- Clone this repo from Gitea (
gitea.dooplex.hu) - Edit files locally
- Push to
mainbranch git-syncsidecar picks up changes automatically (~1-2 min)
Adding a new page
- Create
website/newpage.html(copy structure from existing page) - Add to navigation in all pages'
<nav>section - Add to
sitemap.xmlwith appropriate priority - Push — clean URLs handle
/newpageautomatically
Related Repositories
| Repository | Purpose |
|---|---|
| app-catalog-felhom.eu | Docker Compose templates + .felhom.yml metadata for 45+ apps |
| felhom-controller | felhom-controller Go app + customer deploy scripts |
| deploy-portainer | Legacy — Portainer-based deploy scripts (deprecated) |
| homelab-manifests | k3s cluster manifests for dooplex.hu services |
| misc-scripts | Utility scripts (collect-repos.sh, etc.) |
File Encoding
All HTML files in website/ are UTF-8 with BOM (byte order mark). This ensures proper Hungarian character rendering (á, é, í, ó, ö, ő, ú, ü, ű) across all tools and platforms. The BOM is the 3-byte sequence EF BB BF at the start of each file.
When editing files, ensure your editor preserves UTF-8-BOM encoding. VS Code: check "UTF-8 with BOM" in the bottom status bar.