02-controller-module-map.md "The app page" gains the layout rules [DESIGN] — one column of one width, a section is a hairline not a card, a box means something (blue = the app is waiting for you, amber = something is risky, nothing else), the hero is not a box, and a success is not a banner. Plus the asset-tag rule: every shell's stylesheet address is derived from the BUILD, not the version, because the origin sends no validator and a cached copy can therefore only expire or be addressed differently. 09-update-architecture.md gains §0 [DESIGN]: a version number is pushed once. It belongs at the TOP of that document because everything below it compares versions — the floor moves a box by comparing a number, the button offers the highest tag, and a box re-pulls a tag only when the tag changes. Two images under one number is therefore not untidiness; it is a box running the wrong code that can never be told. R-1080 filed and closed: 0.315.0 was pushed three times from three different commits. Closed rather than left open because no box is stranded — exactly one guest ran it and it ran the registry's current digest, and the floor was 0.314.0 so nothing else could have taken it. audits/app-page-2026-10-15b/P0/ carries the measurement and its honest caveat: the stale Cloudflare object is no longer reproducible, because the first probe of the session forced its revalidation. What is shown is every precondition, the proof an aged entry existed (cf-cache-status EXPIRED), and the elimination of the only other candidate. The six stylesheet bodies are reduced to their sizes, sha256s and rule counts — one 99 KB file does not need six copies in git to make the point. .claude/rules/unprompted-work.md item 6, all four versioned copies: a version number is pushed once, stated as a FACT about what the build now enforces rather than as a new rule. STATUS: the operator's one look, through the public address. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0117kRTSgs3rJWUFGxyoWN6Y
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.