website: the dashboard shown — 4 real screenshots per language on the home page, 2 on the technology page (controller 0.303.0, demo-hp household guest, privacy-scanned); site gate 19 (pictures in the page's language) + decoy; R-906, R-907 (dashboard defects seen); 129 -> 131
gates / gates (push) Successful in 4m12s

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012qRErfCoiTkvDK9N5XHbzb
This commit is contained in:
2026-10-08 12:47:48 +02:00
parent 4b5f74e46c
commit aaac24b05c
41 changed files with 500 additions and 46 deletions
+7
View File
@@ -41,6 +41,13 @@ to the visible answers, and nothing after `</html>`.
the nav listing every language in its own name, the current one marked. **A new language = one entry in
`LANGS` in `scripts/site_gates.py` (and the globe's list) + its twin pages** — nothing else in the switch changes.
## The dashboard pictures
`website/assets/dashboard-<page>-hu.webp` / `-en.webp` show **controller 0.303.0** (taken 2026-10-08 on demo-hp's
household guest; method and privacy scan: `documentation/audits/website-dashboard-2026-10-08/`). **A controller
release that visibly changes a pictured page (Launcher, Apps, an app's page, Backup → Apps) should refresh them.**
Every caption is a claim (`captions-claims.md` there). Gate 19 keeps each page on its own language's pictures.
## The encoding fences
- **All `website/` HTML is UTF-8 *with BOM*. Preserve it.** An editor that strips the BOM is a
+65 -26
View File
@@ -1,36 +1,75 @@
# REPORT — dashboard screenshots for the website (2026-10-08): STOPPED at the baseline
# REPORT — the dashboard on the website (2026-10-08)
> `REPORT-website-dashboard.md`, not `REPORT.md`: another session works in this repo today.
> `REPORT-website-dashboard.md`, not `REPORT.md`: another session works in this repo today. This file replaces this
> morning's „stopped at the baseline" version of it (commit 4b5f74e4); the operator then approved moving 9202 to the
> fleet's controller and said the physical demo boxes are test boxes too.
| Part | What | State |
|---|---|---|
| Baseline | a scratch guest that runs the fleet's controller | **Not met — stopped here, as the brief says** |
| A | the scene + screenshots | not started |
| B | the pages + the hu/en picture gate | not started |
| C | rules, CHANGELOG, STATUS | not started (nothing changed on the site) |
| Baseline | a box on the fleet's controller (0.303.0) | 9202 moved 0.301.0 → 0.303.0 (operator's word); pictures finally from demo-hp's household guest 9201 (see below) |
| A | scene + screenshots, hu and en, privacy check | **Done** — 8 pictures published, 6 taken and kept off the site |
| B | home-page section, technology page, captions as claims, gate 19 + decoy | **Done** |
| C | rules, CHANGELOG, STATUS, rows | **Done** — two rows for dashboard defects (R-906, R-907) |
## What was measured (read only)
**Register:** before **129**, after **131**, opened **2** (R-906, R-907), closed **0**.
- **Fleet controller: 0.303.0** — `STATUS.md` header („demo-hp, demo-felhom and Tester 1 run … controller 0.303.0"),
newest entry in `felhom-controller/CHANGELOG.md`: `## v0.303.0` (2026-10-07).
- **Scratch guest 9202 runs 0.301.0** — `pct exec 9202 -- cat /etc/felhom-controller-image` →
`gitea.dooplex.hu/admin/felhom-controller:0.301.0`; `docker ps` shows `felhom-controller … 0.301.0 Up 55 minutes`.
- **No other scratch guest exists** — `pct list` on the demo-hp host: 9201 (demo-hp, the demo household — no contact),
9202 (scratch), 9401 (`upgrade-harness`, the catalog bench, stopped; it runs no controller). demo-felhom and the
Testers are fenced.
- **The site has no dashboard picture today** — `website/assets/` holds no `dashboard*` file.
- 9202's controller restarted 55 minutes before this check, and it carries paperless-ngx: **another session may be
using 9202 today** (session 1 reported „R-762 closed (9202 proven)" in d9147b02).
## Why the pictures come from 9201, not 9202
Nothing was installed, changed or removed on 9202. The only host command was `pct exec 9202 -- …` (read) and `pct list`.
On 9202 (now 0.303.0) four family apps were installed and a real backup ran, but **every page carried „+ 5 további
figyelmeztetés"**: nine `disk-not-separate` warnings (9202's „second drive" is a folder on the host disk, plus earlier
sessions' leftover folders) and the hub-off warning — true for a scratch guest, not a household's normal state. Faking it
away was not an option. demo-hp's household guest 9201 runs 0.303.0 with **no alerts** (`/api/debug/dump` →
`alerts: []`) and last night's app backups OK, so the pictures were taken there, read only. The one change: the
household language switched to English for the English pictures and back to Hungarian right after (both 302; read back
`lang="hu"`, first sidebar item „Indítópult").
## Pictures (`website/assets/`, 1440 × 900 WebP)
| Picture | hu bytes | en bytes | On |
|---|---|---|---|
| `dashboard-start` (Launcher) | 34,024 | 31,948 | home |
| `dashboard-apps` (Apps) | 59,018 | 54,156 | home, technology |
| `dashboard-app` (Paperless-ngx's page) | 83,124 | 78,992 | home |
| `dashboard-backups` (Backup → Apps) | 52,638 | 45,292 | home, technology |
Kept off the site: the backups overview (a true amber „whole-box backup on the system disk" notice on demo-hp), the System
page (operator details), the phone view (the launcher's share button runs off the edge at 390 px — R-907).
## Privacy check, per picture (`documentation/audits/website-dashboard-2026-10-08/privacy-scan.txt`)
Searched in each page's text (body, title/alt/placeholder/input values): e-mail addresses, IPv4 addresses, long hex
(tokens/keys), the host's serial, password/recovery words, any domain other than the test domain. **All 8: 0 e-mails,
0 IPs, 0 serials, no domain but `enkisfelhom.hu` (demo-hp's demo test domain) and `felhom.eu`.** One long hex per page =
the hidden CSRF field, not visible in the picture. Password-word hits on the app pages are help text, not a password.
**Positive control:** an app name known to be on the page (`Paperless-ngx` / `BookStack`) — found 8 of 8.
## Captions = claims (`captions-claims.md`)
Launcher → map E Indítópult (IMPLEMENTED); Apps → map B deploy + version (PROVEN-LIVE); an app's page → map B app
lifecycle (PROVEN-LIVE) + catalog copy + file access (IMPLEMENTED); Backup → Apps → map C nightly DB dumps (PROVEN-LIVE).
## Clean-up
9202: Vaultwarden, Jellyfin, Nextcloud, Immich removed through the product (data and backups too); Paperless-ngx
(running) and PrivateBin (stopped) were there before and are as found; screenshot folder removed, session files
shredded; 9202 stays on 0.303.0. 9201: language back to Hungarian. demo-hp host: only `pct exec`/`pct list`. Hub:
nothing. demo-felhom: one read-only `pct list` + `docker ps` (only one app there, so not used).
## Gate 19 decoy (`decoy.txt`)
Mutation: `website/en/index.html` `dashboard-start-en.webp` → `dashboard-start-hu.webp`.
Failure line: `FAIL: en/index.html: a hu dashboard picture (/assets/dashboard-start-hu.webp) on a en page — use the page's own language`.
Also in `scripts/test_gate_decoys.py` as `site/dashboard-picture-wrong-language` (with `must=` on that line).
## Rows filed (dashboard defects seen in the pictures; controller not changed)
- **R-906** (P4): a page can show „+5 more warnings" with none above it — the overflow is counted before the page filter.
- **R-907** (P4): on a phone the Launcher's share button runs off the right edge.
## Files and pushes
Named in the chat after the push (commit hash and CI by commit).
## Decision for you
**May I move scratch guest 9202 to controller 0.303.0 — the fleet's version, not past it — and then take the
screenshots?** (On 9202 the image is set by hand in `/etc/felhom-controller-image` + a restart of its bootstrap
service; `operations/nodes.md` — allowed only there.)
- **Pick: yes**, at a time when the other session is not using 9202. Cost: about two minutes of 9202 restart. The
pictures then show what households really run.
- Or: take the pictures on 0.301.0 now. Cost: the pictures show a dashboard two releases old (0.302.0 changed the
format list and 22 messages).
- **If you do nothing:** no pictures; the site stays as it is.
None. Look at the home page once (`felhom.eu/#vezerlopult`, `felhom.eu/en/#dashboard`).
+1
View File
@@ -58,6 +58,7 @@ Full designs: `documentation/audits/day-2026-10-08/`.
in the menu goes to the same page in the other language.
- **A missing-page (404) page exists now.**
- **The language link is now a globe icon**, like the dashboard's: a click shows „Magyar" and „English".
- **The website now shows the dashboard**: four real pictures on the home page (and two on the technology page), Hungarian on Hungarian pages, English on English ones. Two small dashboard defects were filed.
- **The contact form's lost program code was searched for everywhere I can reach: not found.** The running program is now saved in git, so it cannot be lost. A plan for a replacement is written. One question for you: is the February folder on your Windows computer?
**Needs you:** two choices in `REPORT-website-refresh.md`. If nothing: SparkyFitness stays listed; the contact
@@ -0,0 +1,53 @@
# Dashboard pictures for the website — evidence (2026-10-08)
## The scene, and why it moved
1. **9202 updated to the fleet's controller** (operator's word, 2026-10-08): `/etc/felhom-controller-image`
`…:0.301.0` → `…:0.303.0` (old line kept as `/etc/felhom-controller-image.bak-0.301.0` in the guest), bootstrap
restarted; `felhom-controller … 0.303.0 Up 13 seconds (healthy)`.
2. **On 9202:** Vaultwarden, Jellyfin, Nextcloud and Immich installed through the product (Immich's first pull was
refused by ghcr.io „toomanyrequests"; the second try deployed). One whole-box backup press and one Tier-2 run:
`db_dump success, count 3`; „Tier 2 run complete: 6 app(s)".
3. **The 9202 pictures were not used.** Every page carried a banner „+ 5 további figyelmeztetés" — nine
`disk-not-separate` warnings (9202's „second drive" is a folder on the host's NVMe, plus earlier sessions' leftover
app folders) and the hub-off warning. True for a scratch guest, not a household's normal state. The banner itself
showed none of the five warnings it counted on those pages: **R-906**.
4. **Pictures taken on demo-hp's household guest 9201** (operator: the physical boxes are test boxes too): controller
0.303.0, 10 standing apps, hub connected, **no alerts** (`/api/debug/dump` → `alerts: []`), last night's app backup
`success` at 04:16. Read only; the household language was switched to English for the English pictures and back to
Hungarian right after (both POSTs 302; read back `lang="hu"`). Headless Chrome ran on 9202, reaching 9201's traefik
at `192.168.0.155`; nothing ran on 9201.
## The pictures (`website/assets/`, 1440 × 900, WebP q82)
| File | Bytes |
|---|---|
| dashboard-start-hu.webp / -en | 34,024 / 31,948 |
| dashboard-apps-hu.webp / -en | 59,018 / 54,156 |
| dashboard-app-hu.webp / -en (Paperless-ngx) | 83,124 / 78,992 |
| dashboard-backups-hu.webp / -en (Backup → Apps) | 52,638 / 45,292 |
Taken but not published: the backups overview, the System page, the phone view (`captions-claims.md` says why).
The full-size PNGs stay out of the repository.
## Privacy
`privacy-scan.txt` — every published picture's page text: 0 e-mails, 0 IPv4 addresses, 0 serials, no domain except the
test domain `enkisfelhom.hu` and `felhom.eu`; the one long hex value is the hidden CSRF field (not visible). Positive
control: an app name known to be on each page, found 8 of 8. By eye: the pictures show the test domain, app names and
backup sizes/times; no person's name (the household is „Demo HP", shown only on the unpublished System page).
## Clean-up (three layers)
- **9202:** Vaultwarden, Jellyfin, Nextcloud, Immich removed through the product (stop + remove with drive data and
backups; volumes gone, stack folders back to their template files). Paperless-ngx (running) and PrivateBin
(stopped) — there before this task — left as found; the whole-box backup restarted Paperless and it came back healthy.
`/root/dash` removed; the session cookie file shredded on both sides. The `userdata/{immich,jellyfin,nextcloud}`
folders on the scratch drive hold earlier sessions' content (`kept/`, `backups/`) and were left. **9202 stays on
0.303.0** (operator-approved).
- **9201:** nothing changed except the language round trip (ends Hungarian, as found).
- **demo-hp host:** only `pct exec 9202/9201 -- …` and `pct list`. **Hub:** nothing touched.
## Decoy
`decoy.txt` — gate 19, mutation and failure line.
@@ -0,0 +1,23 @@
# Dashboard pictures — every caption is a claim (2026-10-08)
Pictures: demo-hp's household guest 9201, controller **0.303.0** (the fleet's version), read only — the only change
was the household language switched to English for the English pictures and straight back to Hungarian (both
switches answered 302; the launcher read back `lang="hu"`, first sidebar item „Indítópult"). Taken from scratch guest
9202 with headless Chrome through 9201's traefik (`192.168.0.155`, host `felhom.enkisfelhom.hu`), 1440 × 900.
| Picture | Caption (hu / en) | Capability-map row | Status |
|---|---|---|---|
| `dashboard-start-*` | „Indítópult — minden alkalmazásod egy kattintásra." / „Launcher — every app you have, one click away." | E „Indítópult (app launcher) — one-tap grid of the household's openable apps" | IMPLEMENTED |
| `dashboard-apps-*` | „Alkalmazások — telepítés pár kattintással, és látod, mi fut és mi naprakész." / „Apps — install in a few clicks, and see what runs and what is up to date." | B „Deploy an app from the catalog" (PROVEN-LIVE); B „What VERSION a box is running, and whether it is behind the catalog" (PROVEN-LIVE) | PROVEN-LIVE |
| `dashboard-app-*` | „Egy alkalmazás oldala — mire jó, hova tedd a fájlokat, és hogyan kezdj hozzá." / „An app's page — what it is for, where your files go, and how to start." | B „App lifecycle: start/stop/restart/update/logs/remove/redeploy" (PROVEN-LIVE); the page's copy is the catalog's (`10-localisation.md` §7, slice 5); the drop-zone link is map E „File access via browser" (IMPLEMENTED, R-75) | PROVEN-LIVE / IMPLEMENTED |
| `dashboard-backups-*` | „Mentések — minden éjjel minden alkalmazás adatbázisa mentésre kerül, és ellenőrizzük." / „Backups — every night each app's database is backed up and checked." | C „Nightly DB dumps (postgres/mariadb autodiscovery), atomic writes" (PROVEN-LIVE); the picture shows the night's run at 04:16 with a table count per database | PROVEN-LIVE |
Not published (taken, kept off the site): the backups **overview** (on demo-hp it truthfully shows an amber „the whole-box
backup is on the system disk" notice — not a household's normal state), the **System** page (operator details: the
template source and the monitoring address), and the **phone** view (the launcher's share button runs off the right edge
at 390 px — R-907).
Privacy: `privacy-scan.txt` (per picture: e-mail, IPv4, long hex, the host's serial, password/recovery words, any domain
other than the test domain `enkisfelhom.hu`; positive control an app name known to be on the page — found on all 8).
The only long hex value is the page's hidden CSRF field, which is not visible in the picture. The two password-word hits
are help text („a jelszó a Beállítások oldalon", „password manager"), not a password.
@@ -0,0 +1,3 @@
mutation: website/en/index.html src="/assets/dashboard-start-en.webp" -> dashboard-start-hu.webp
FAIL: en/index.html: a hu dashboard picture (/assets/dashboard-start-hu.webp) on a en page — use the page's own language
SITE GATES FAILED: 1 problem(s)
@@ -0,0 +1,59 @@
Privacy scan of the page text behind each published picture (body text + title/alt/placeholder/input values).
Allowed by design: the test domain enkisfelhom.hu (demo-hp's demo customer) and felhom.eu.
== dashboard-start-hu (positive control 'Paperless-ngx': FOUND)
e-mail: 0 []
IPv4: 0 []
long hex (token/key): 1 ['hidden CSRF field value, not visible on the page']
serial 8CN944035T: 0 []
recovery/password words: 0 []
domain other than the test domain: 1 ['Felhom.eu']
== dashboard-start-en (positive control 'Paperless-ngx': FOUND)
e-mail: 0 []
IPv4: 0 []
long hex (token/key): 1 ['hidden CSRF field value, not visible on the page']
serial 8CN944035T: 0 []
recovery/password words: 0 []
domain other than the test domain: 1 ['Felhom.eu']
== dashboard-apps-hu (positive control 'BookStack': FOUND)
e-mail: 0 []
IPv4: 0 []
long hex (token/key): 1 ['hidden CSRF field value, not visible on the page']
serial 8CN944035T: 0 []
recovery/password words: 0 []
domain other than the test domain: 2 ['Cal.com', 'Felhom.eu']
== dashboard-apps-en (positive control 'BookStack': FOUND)
e-mail: 0 []
IPv4: 0 []
long hex (token/key): 1 ['hidden CSRF field value, not visible on the page']
serial 8CN944035T: 0 []
recovery/password words: 1 ['password']
domain other than the test domain: 2 ['Cal.com', 'Felhom.eu']
== dashboard-app-hu (positive control 'Paperless-ngx': FOUND)
e-mail: 0 []
IPv4: 0 []
long hex (token/key): 1 ['hidden CSRF field value, not visible on the page']
serial 8CN944035T: 0 []
recovery/password words: 1 ['jelszó']
domain other than the test domain: 1 ['Felhom.eu']
== dashboard-app-en (positive control 'Paperless-ngx': FOUND)
e-mail: 0 []
IPv4: 0 []
long hex (token/key): 1 ['hidden CSRF field value, not visible on the page']
serial 8CN944035T: 0 []
recovery/password words: 1 ['password']
domain other than the test domain: 1 ['Felhom.eu']
== dashboard-bapps-hu (positive control 'paperless-ngx': FOUND)
e-mail: 0 []
IPv4: 0 []
long hex (token/key): 1 ['hidden CSRF field value, not visible on the page']
serial 8CN944035T: 0 []
recovery/password words: 0 []
domain other than the test domain: 1 ['Felhom.eu']
== dashboard-bapps-en (positive control 'paperless-ngx': FOUND)
e-mail: 0 []
IPv4: 0 []
long hex (token/key): 1 ['hidden CSRF field value, not visible on the page']
serial 8CN944035T: 0 []
recovery/password words: 0 []
domain other than the test domain: 1 ['Felhom.eu']
@@ -0,0 +1,8 @@
import sys, os, json
sys.path.insert(0, '/mnt/5_hdd/felhom.eu/git/felhom.eu/documentation/audits/website-refresh-2026-10-08/shots/tools'); import sw; b = sw.b
b.login()
for app, sub in [("immich", "photos")]:
ok = b.deploy(app, sub, {"HDD_PATH": "/mnt/felhom-drives/scratch_hdd"})
print(app, "deploy", ok, flush=True)
if ok:
print(app, "answers", b.wait_app(sub), flush=True)
@@ -0,0 +1,16 @@
import sys, os, re
sys.path.insert(0, '/mnt/5_hdd/felhom.eu/git/app-catalog-felhom.eu/scripts'); import box_walk as b
lang = sys.argv[1]
b.login()
sess = open(f"{b.SC}/sess{os.getpid()}.txt").read().strip()
r = b.sh(["curl", "-sk", "-H", b.HOSTHDR, "-H", f"Cookie: {sess}", f"{b.BASE}/launcher"])
tok = re.search(r'name="_csrf" value="([^"]+)"', r.stdout).group(1)
r = b.sh(["curl", "-sk", "-o", "/dev/null", "-w", "%{http_code}", "-H", b.HOSTHDR, "-H", f"Cookie: {sess}",
"-H", f"Origin: https://felhom.{b.DOMAIN}", "-H", f"Referer: https://felhom.{b.DOMAIN}/launcher",
"--data-urlencode", f"_csrf={tok}", "--data-urlencode", f"lang={lang}", "--data-urlencode", "back=/launcher",
f"{b.BASE}/settings/language"])
print("POST /settings/language", lang, "->", r.stdout)
r = b.sh(["curl", "-sk", "-H", b.HOSTHDR, "-H", f"Cookie: {sess}", f"{b.BASE}/launcher"])
print("launcher <html lang>:", re.search(r'<html lang="([^"]+)"', r.stdout).group(1) if re.search(r'<html lang="([^"]+)"', r.stdout) else "?",
"| sidebar first item:", re.search(r'#i-rocket"/></svg>([^<]+)<', r.stdout).group(1) if re.search(r'#i-rocket"/></svg>([^<]+)<', r.stdout) else "?")
fd = os.open(f"{b.SC}/session.txt", os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600); os.write(fd, sess.encode()); os.close(fd)
@@ -0,0 +1,78 @@
# -*- coding: utf-8 -*-
"""Dashboard pictures on the website (2026-10-08): a home-page section after „Mit tud a doboz ma?" and two pictures in
the technology page's dashboard card, each page in its own language. Reads the CURRENT pages; every insert asserts
its anchor once."""
import io, os, re, sys
W = "/mnt/5_hdd/felhom.eu/git/felhom.eu/website"
def rd(p): return io.open(os.path.join(W, p), encoding="utf-8-sig").read()
def wr(p, s):
with io.open(os.path.join(W, p), "w", encoding="utf-8-sig", newline="\n") as f: f.write(s)
def sub1(s, old, new):
assert s.count(old) == 1, (old[:80], s.count(old)); return s.replace(old, new)
SHOTS = { # page: (hu alt, hu caption, en alt, en caption)
"start": ("A vezérlőpult Indítópultja: az otthon telepített alkalmazások csempéi",
"<strong>Indítópult</strong> — minden alkalmazásod egy kattintásra.",
"The Dashboard's Launcher: tiles of the apps installed at home",
"<strong>Launcher</strong> — every app you have, one click away."),
"apps": ("Az Alkalmazások oldal: telepített és telepíthető alkalmazások kártyái, állapottal",
"<strong>Alkalmazások</strong> — telepítés pár kattintással, és látod, mi fut és mi naprakész.",
"The Apps page: cards of installed and installable apps, with their status",
"<strong>Apps</strong> — install in a few clicks, and see what runs and what is up to date."),
"app": ("Egy alkalmazás saját oldala: mire jó, képek, és az első lépések",
"<strong>Egy alkalmazás oldala</strong> — mire jó, hova tedd a fájlokat, és hogyan kezdj hozzá.",
"An app's own page: what it is for, pictures, and the first steps",
"<strong>An app's page</strong> — what it is for, where your files go, and how to start."),
"backups": ("A mentések oldala: minden alkalmazás adatbázisának éjszakai mentése, ellenőrizve",
"<strong>Mentések</strong> — minden éjjel minden alkalmazás adatbázisa mentésre kerül, és ellenőrizzük.",
"The backups page: each app's database backed up overnight, and checked",
"<strong>Backups</strong> — every night each app's database is backed up and checked."),
}
def fig(page, lang):
hu_alt, hu_cap, en_alt, en_cap = SHOTS[page]
alt, cap = (hu_alt, hu_cap) if lang == "hu" else (en_alt, en_cap)
src = "/assets/dashboard-%s-%s.webp" % (page, lang)
return (' <figure class="dash-shot">\n'
' <a href="%s"><img src="%s" alt="%s" width="1440" height="900" loading="lazy"></a>\n'
' <figcaption>%s</figcaption>\n'
' </figure>') % (src, src, alt, cap)
def home_section(lang):
if lang == "hu":
sid, h2, sub = "vezerlopult", "Így néz ki <span>a vezérlőpult</span>", "Valódi képek egy Felhom dobozról — kattints rájuk a teljes mérethez"
else:
sid, h2, sub = "dashboard", "What the <span>Dashboard looks like</span>", "Real pictures from a Felhom box — click one for the full size"
figs = "\n".join(fig(p, lang) for p in ("start", "apps", "app", "backups"))
return (' <section class="content-section" id="%s">\n <div class="container">\n'
' <div class="section-header">\n <h2>%s</h2>\n <p>%s</p>\n </div>\n'
' <div class="dash-grid">\n%s\n </div>\n </div>\n </section>\n\n') % (sid, h2, sub, figs)
ANCHOR_HOME = ' <section class="content-section alt-bg">\n'
for p, lang in (("index.html", "hu"), ("en/index.html", "en")):
s = rd(p)
i = s.index('id="mit-tud"' if lang == "hu" else 'id="what-it-does"')
j = s.index(ANCHOR_HOME, i)
s = s[:j] + home_section(lang) + s[j:]
wr(p, s)
print(p, "section added")
TECH_ANCHOR = {"hu": ' <div class="highlight-box">\n <p><strong>Tökéletes:</strong>',
"en": ' <div class="highlight-box">\n <p><strong>Good for:</strong>'}
for p, lang in (("technologiak.html", "hu"), ("en/technology.html", "en")):
s = rd(p)
block = (' <div class="dash-grid">\n%s\n </div>\n\n'
% "\n".join(" " + l for f in (fig("apps", lang), fig("backups", lang)) for l in f.split("\n")))
s = sub1(s, TECH_ANCHOR[lang], block + TECH_ANCHOR[lang])
wr(p, s)
print(p, "pictures added")
@@ -0,0 +1,44 @@
// Dashboard screenshots for felhom.eu (2026-10-08): demo-hp's household guest 9201 (read only; run from 9202), controller 0.303.0, through traefik with the
// real host name, headless Chrome. The session cookie comes from a 0600 file (never printed); each page's visible text
// is saved beside its picture for the privacy scan.
const puppeteer = require('puppeteer');
const fs = require('fs');
const sleep = ms => new Promise(r => setTimeout(r, ms));
const HOST = 'felhom.enkisfelhom.hu', BASE = 'https://' + HOST;
const sess = fs.readFileSync('/out/session.txt', 'utf8').trim(); // felhom_session=…
const APP = process.env.APP || 'immich';
const PAGES = (process.env.PAGES ? process.env.PAGES.split(',').map(x => x.split('=')) : [['start', '/launcher'], ['apps', '/stacks'], ['app', '/apps/' + APP], ['backups', '/backups'], ['system', '/settings']]);
async function setLang(p, lang) {
await p.goto(BASE + '/launcher', {waitUntil: 'networkidle2'});
const btn = await p.$(`button.lang-globe-item[value="${lang}"]`);
if (!btn) throw new Error('no globe item ' + lang);
await p.evaluate(b => b.closest('form').submit(), btn);
await p.waitForNavigation({waitUntil: 'networkidle2'}).catch(() => {});
const got = await p.$eval('html', e => e.lang);
console.log('language now', got);
}
async function shot(p, name) {
await sleep(1500);
await p.screenshot({path: `/out/${name}.png`});
const t = await p.evaluate(() => document.body.innerText + '\n' + Array.from(document.querySelectorAll('[title],[alt],[placeholder],input[value]')).map(e => [e.title, e.alt, e.placeholder, e.value].join(' ')).join('\n'));
fs.writeFileSync(`/out/${name}.txt`, t);
console.log('shot', name);
}
(async () => {
const b = await puppeteer.launch({args: ['--no-sandbox', '--ignore-certificate-errors', `--host-resolver-rules=MAP * ${process.env.TRAEFIK}`]});
const p = await b.newPage();
const [k, v] = sess.split('=');
await p.setCookie({name: k, value: v, domain: HOST, path: '/', secure: true, httpOnly: true});
for (const lang of (process.env.LANGS || 'hu,en').split(',')) {
await p.setViewport({width: 1440, height: 900, deviceScaleFactor: 1});
for (const [name, path] of PAGES) {
await p.goto(BASE + path, {waitUntil: 'networkidle2'});
await shot(p, `${name}-${lang}`);
}
if (process.env.NOPHONE) continue;
await p.setViewport({width: 390, height: 844, deviceScaleFactor: 2, isMobile: true, hasTouch: true});
await p.goto(BASE + '/launcher', {waitUntil: 'networkidle2'});
await shot(p, `phone-${lang}`);
}
await b.close();
})().catch(e => { console.error(e); process.exit(1); });
File diff suppressed because one or more lines are too long
+6
View File
@@ -1,3 +1,9 @@
## gates — site gate 19: dashboard pictures in the page's own language (2026-10-08)
- `site_gates.py` gate 19: every `/assets/dashboard-*-(hu|en).webp` on a page must match the page's language set.
Decoy `site/dashboard-picture-wrong-language` (the English home page given `dashboard-start-hu.webp`) convicts with
„en/index.html: a hu dashboard picture … on a en page".
## gates — the decoy suite runs one at a time (2026-10-08, fixed without a row)
- `test_gate_decoys.py` takes an exclusive `fcntl.flock` on `.git/decoy-suite.lock` before planting anything. Two runs at
+14 -1
View File
@@ -28,6 +28,7 @@ Gates (all must pass; non-zero exit on any failure):
retrieval promise beyond what the Hungarian twin makes
17. faq-ld — each FAQ page's FAQPage JSON-LD carries exactly its visible questions and answers
18. tail — nothing after </html> (a stray file-dump summary sat visible below technologiak.html)
19. dash-lang — a page shows dashboard pictures only in its own language (`dashboard-*-hu.webp` / `-en.webp`)
"""
import html as _html, io, json, os, re, sys, unicodedata
@@ -439,8 +440,20 @@ for p, s in pages.items():
if i < 0 or s[i + len("</html>"):].strip():
fail("%s: text after </html> (%r) — it renders on the page" % (p, s[i + 7:i + 87] if i >= 0 else "no </html>"))
# gate 19: dashboard pictures in the page's own language (2026-10-08). The dashboard speaks both languages and the
# site shows it in each: an English page may not carry a `dashboard-*-hu.webp`, a Hungarian page a `-en.webp`.
_DASH_RE = re.compile(r"/assets/dashboard-[a-z0-9-]+?-(hu|en)\.webp")
_dash_n = 0
for p, s in pages.items():
want = lang_of(p)
for m in _DASH_RE.finditer(s):
_dash_n += 1
if m.group(1) != want:
fail("%s: a %s dashboard picture (%s) on a %s page — use the page's own language" % (p, m.group(1), m.group(0), want))
print(" dashboard pictures: %d references, each in its page's language" % _dash_n)
if fails:
print("\nSITE GATES FAILED: %d problem(s)" % len(fails))
sys.exit(1)
print("site gates OK — BOM, emoji=0, nav/footer per language, analytics, no CDN, no legacy tokens, no <style>, "
"cache-busted assets, twins + hreflang + globe, lang, same apps, no Hungarian in English, FAQ JSON-LD, clean tail")
"cache-busted assets, twins + hreflang + globe, lang, same apps, no Hungarian in English, FAQ JSON-LD, clean tail, dashboard pictures per language")
+5 -1
View File
@@ -82,7 +82,7 @@ COVERS = {
"switch at the wrong twin, lang=\"hu\" on an English page, an app missing from one apps page, "
"ASCII-only Hungarian in English text and in a script message, a 'please', an English "
"retrieval promise the Hungarian does not make, a FAQ JSON-LD drifting from the visible "
"answer, and text after </html> — each must convict WITH its own failure line"),
"answer, text after </html>, and a Hungarian dashboard picture on an English page — each must convict WITH its own failure line"),
"script-tests": ("R-885: FIVE cases in scripts/test_script_tests_gate.py, run from here: a suite that PRINTS "
"OK and exits 1 (label without fact), a failing suite three levels deep (scope is a walk), "
"an empty scripts/ tree (checked nothing), the genuine article (must pass), the nested mark"),
@@ -540,6 +540,10 @@ decoy("site/globe-wrong-order", "site_gates.py",
decoy("site/leftover-text-switch", "site_gates.py",
replace_in(_EN("apps.html"), '<li class="lang-globe-li">', '<li><a href="/alkalmazasok" lang="hu" hreflang="hu" class="lang-switch">Magyar</a></li><li class="lang-globe-li">'),
must='en/apps.html: a leftover text language switch')
# the dashboard pictures (2026-10-08): a Hungarian picture copied onto the English home page
decoy("site/dashboard-picture-wrong-language", "site_gates.py",
replace_in(_EN("index.html"), 'src="/assets/dashboard-start-en.webp"', 'src="/assets/dashboard-start-hu.webp"'),
must="en/index.html: a hu dashboard picture (/assets/dashboard-start-hu.webp) on a en page")
decoy("site/lang-attr", "site_gates.py",
replace_in(_EN("backups.html"), '<html lang="en">', '<html lang="hu">'),
must="the English set needs lang='en'")
+1 -1
View File
@@ -6,7 +6,7 @@
<title>Az oldal nem található | Felhom.eu</title>
<meta name="robots" content="noindex, follow">
<!-- shared assets: bump ?v= together on any site.css/icons.svg change -->
<link rel="stylesheet" href="/assets/site.css?v=3">
<link rel="stylesheet" href="/assets/site.css?v=4">
<link rel="icon" type="image/png" href="/assets/logo_favicon_2.svg">
<script defer src="https://stats.felhom.eu/script.js" data-website-id="d419db57-5e1c-48df-bdb3-c29f25fb1661"></script>
</head>
+12
View File
@@ -1,3 +1,15 @@
## 2026-10-08 (afternoon) — the dashboard, shown
- **A new section after „Mit tud a doboz ma?" / „What the box does today"**: „Így néz ki a vezérlőpult" / „What the
Dashboard looks like" — four real screenshots (Launcher, Apps, an app's page, Backup → Apps), each with a one-line
caption; the technology page's dashboard card gets the Apps and Backup pictures. Hungarian pages show the Hungarian
dashboard, English pages the English one.
- Taken on demo-hp's household guest (controller 0.303.0, no warnings, last night's backups OK); 1440 × 900 WebP,
31–83 KB each; `loading="lazy"`, `width`/`height` set, each links to its full-size picture (no script).
- `site.css`: `.dash-grid` / `.dash-shot` (two per row, one below 700 px); `?v=3` → `?v=4` on all sixteen pages.
- Gate 19: a page may only show dashboard pictures in its own language (decoy seen failing first).
- Evidence, captions-to-capability rows and the privacy scan: `documentation/audits/website-dashboard-2026-10-08/`.
## 2026-10-08 — the FAQ's GDPR answer tells the truth about where copies and traffic go (R-900; operator approved the text in chat)
- `gyik.html` and `en/faq.html`, the GDPR answer, both the visible text and its structured-data copy: the line „az adataid a
+1 -1
View File
@@ -43,7 +43,7 @@
}
</script>
<!-- shared assets: bump ?v= together on any site.css/icons.svg change -->
<link rel="stylesheet" href="/assets/site.css?v=3">
<link rel="stylesheet" href="/assets/site.css?v=4">
<link rel="icon" type="image/png" href="/assets/logo_favicon_2.svg">
<script defer src="https://stats.felhom.eu/script.js" data-website-id="d419db57-5e1c-48df-bdb3-c29f25fb1661"></script>
</head>
Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

+10
View File
@@ -2056,3 +2056,13 @@ footer p { color: var(--text-2); font-size: 0.9rem; }
overflow: hidden;
}
}
/* --- Dashboard pictures (2026-10-08): real screenshots, two per row, one on a phone. A picture links to its
full-size file; no lightbox. Hairline border, 2px radius, no shadow. --- */
.dash-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 24px; margin: 8px 0 24px; }
.dash-shot { margin: 0; }
.dash-shot img { display: block; width: 100%; height: auto; border: 1px solid var(--line); border-radius: var(--radius); }
.dash-shot a:hover img { border-color: var(--blue); }
.dash-shot figcaption { margin-top: 10px; color: var(--text-2); font-size: 0.95rem; line-height: 1.5; }
.dash-shot figcaption strong { color: var(--text-1); }
@media (max-width: 700px) { .dash-grid { grid-template-columns: minmax(0, 1fr); } }
+1 -1
View File
@@ -37,7 +37,7 @@
}
</script>
<!-- shared assets: bump ?v= together on any site.css/icons.svg change -->
<link rel="stylesheet" href="/assets/site.css?v=3">
<link rel="stylesheet" href="/assets/site.css?v=4">
<link rel="icon" type="image/png" href="/assets/logo_favicon_2.svg">
<script defer src="https://stats.felhom.eu/script.js" data-website-id="d419db57-5e1c-48df-bdb3-c29f25fb1661"></script>
</head>
+1 -1
View File
@@ -43,7 +43,7 @@
}
</script>
<!-- shared assets: bump ?v= together on any site.css/icons.svg change -->
<link rel="stylesheet" href="/assets/site.css?v=3">
<link rel="stylesheet" href="/assets/site.css?v=4">
<link rel="icon" type="image/png" href="/assets/logo_favicon_2.svg">
<script defer src="https://stats.felhom.eu/script.js" data-website-id="d419db57-5e1c-48df-bdb3-c29f25fb1661"></script>
</head>
+1 -1
View File
@@ -37,7 +37,7 @@
}
</script>
<!-- shared assets: bump ?v= together on any site.css/icons.svg change -->
<link rel="stylesheet" href="/assets/site.css?v=3">
<link rel="stylesheet" href="/assets/site.css?v=4">
<link rel="icon" type="image/png" href="/assets/logo_favicon_2.svg">
<script defer src="https://stats.felhom.eu/script.js" data-website-id="d419db57-5e1c-48df-bdb3-c29f25fb1661"></script>
</head>
+1 -1
View File
@@ -26,7 +26,7 @@
<meta name="twitter:description" content="A question? Write to us: info@felhom.eu">
<meta name="twitter:image" content="https://felhom.eu/assets/og-image-en.png">
<!-- shared assets: bump ?v= together on any site.css/icons.svg change -->
<link rel="stylesheet" href="/assets/site.css?v=3">
<link rel="stylesheet" href="/assets/site.css?v=4">
<link rel="icon" type="image/png" href="/assets/logo_favicon_2.svg">
<script defer src="https://stats.felhom.eu/script.js" data-website-id="d419db57-5e1c-48df-bdb3-c29f25fb1661"></script>
</head>
+1 -1
View File
@@ -24,7 +24,7 @@
<meta name="twitter:description" content="Download, checksum, and what to check before you install.">
<meta name="twitter:image" content="https://felhom.eu/assets/og-image.png">
<!-- shared assets: bump ?v= together on any site.css/icons.svg change -->
<link rel="stylesheet" href="/assets/site.css?v=3">
<link rel="stylesheet" href="/assets/site.css?v=4">
<link rel="icon" type="image/png" href="/assets/logo_favicon_2.svg">
<script defer src="https://stats.felhom.eu/script.js" data-website-id="d419db57-5e1c-48df-bdb3-c29f25fb1661"></script>
</head>
+1 -1
View File
@@ -363,7 +363,7 @@
}
</script>
<!-- shared assets: bump ?v= together on any site.css/icons.svg change -->
<link rel="stylesheet" href="/assets/site.css?v=3">
<link rel="stylesheet" href="/assets/site.css?v=4">
<link rel="icon" type="image/png" href="/assets/logo_favicon_2.svg">
<script defer src="https://stats.felhom.eu/script.js" data-website-id="d419db57-5e1c-48df-bdb3-c29f25fb1661"></script>
</head>
+28 -1
View File
@@ -54,7 +54,7 @@
}
</script>
<!-- shared assets: bump ?v= together on any site.css/icons.svg change -->
<link rel="stylesheet" href="/assets/site.css?v=3">
<link rel="stylesheet" href="/assets/site.css?v=4">
<link rel="icon" type="image/png" href="/assets/logo_favicon_2.svg">
<script defer src="https://stats.felhom.eu/script.js" data-website-id="d419db57-5e1c-48df-bdb3-c29f25fb1661"></script>
</head>
@@ -151,6 +151,33 @@
</div>
</section>
<section class="content-section" id="dashboard">
<div class="container">
<div class="section-header">
<h2>What the <span>Dashboard looks like</span></h2>
<p>Real pictures from a Felhom box — click one for the full size</p>
</div>
<div class="dash-grid">
<figure class="dash-shot">
<a href="/assets/dashboard-start-en.webp"><img src="/assets/dashboard-start-en.webp" alt="The Dashboard's Launcher: tiles of the apps installed at home" width="1440" height="900" loading="lazy"></a>
<figcaption><strong>Launcher</strong> — every app you have, one click away.</figcaption>
</figure>
<figure class="dash-shot">
<a href="/assets/dashboard-apps-en.webp"><img src="/assets/dashboard-apps-en.webp" alt="The Apps page: cards of installed and installable apps, with their status" width="1440" height="900" loading="lazy"></a>
<figcaption><strong>Apps</strong> — install in a few clicks, and see what runs and what is up to date.</figcaption>
</figure>
<figure class="dash-shot">
<a href="/assets/dashboard-app-en.webp"><img src="/assets/dashboard-app-en.webp" alt="An app's own page: what it is for, pictures, and the first steps" width="1440" height="900" loading="lazy"></a>
<figcaption><strong>An app's page</strong> — what it is for, where your files go, and how to start.</figcaption>
</figure>
<figure class="dash-shot">
<a href="/assets/dashboard-backups-en.webp"><img src="/assets/dashboard-backups-en.webp" alt="The backups page: each app's database backed up overnight, and checked" width="1440" height="900" loading="lazy"></a>
<figcaption><strong>Backups</strong> — every night each app's database is backed up and checked.</figcaption>
</figure>
</div>
</div>
</section>
<section class="content-section alt-bg">
<div class="container">
<div class="section-header">
+12 -1
View File
@@ -38,7 +38,7 @@
}
</script>
<!-- shared assets: bump ?v= together on any site.css/icons.svg change -->
<link rel="stylesheet" href="/assets/site.css?v=3">
<link rel="stylesheet" href="/assets/site.css?v=4">
<link rel="icon" type="image/png" href="/assets/logo_favicon_2.svg">
<script defer src="https://stats.felhom.eu/script.js" data-website-id="d419db57-5e1c-48df-bdb3-c29f25fb1661"></script>
</head>
@@ -163,6 +163,17 @@
<li>System monitoring – health watched all the time, with alerts</li>
</ul>
<div class="dash-grid">
<figure class="dash-shot">
<a href="/assets/dashboard-apps-en.webp"><img src="/assets/dashboard-apps-en.webp" alt="The Apps page: cards of installed and installable apps, with their status" width="1440" height="900" loading="lazy"></a>
<figcaption><strong>Apps</strong> — install in a few clicks, and see what runs and what is up to date.</figcaption>
</figure>
<figure class="dash-shot">
<a href="/assets/dashboard-backups-en.webp"><img src="/assets/dashboard-backups-en.webp" alt="The backups page: each app's database backed up overnight, and checked" width="1440" height="900" loading="lazy"></a>
<figcaption><strong>Backups</strong> — every night each app's database is backed up and checked.</figcaption>
</figure>
</div>
<div class="highlight-box">
<p><strong>Good for:</strong> a home media server, a family cloud, personal projects, a small office.</p>
</div>
+1 -1
View File
@@ -363,7 +363,7 @@
}
</script>
<!-- shared assets: bump ?v= together on any site.css/icons.svg change -->
<link rel="stylesheet" href="/assets/site.css?v=3">
<link rel="stylesheet" href="/assets/site.css?v=4">
<link rel="icon" type="image/png" href="/assets/logo_favicon_2.svg">
<script defer src="https://stats.felhom.eu/script.js" data-website-id="d419db57-5e1c-48df-bdb3-c29f25fb1661"></script>
</head>
+28 -1
View File
@@ -54,7 +54,7 @@
}
</script>
<!-- shared assets: bump ?v= together on any site.css/icons.svg change -->
<link rel="stylesheet" href="/assets/site.css?v=3">
<link rel="stylesheet" href="/assets/site.css?v=4">
<link rel="icon" type="image/png" href="/assets/logo_favicon_2.svg">
<script defer src="https://stats.felhom.eu/script.js" data-website-id="d419db57-5e1c-48df-bdb3-c29f25fb1661"></script>
</head>
@@ -151,6 +151,33 @@
</div>
</section>
<section class="content-section" id="vezerlopult">
<div class="container">
<div class="section-header">
<h2>Így néz ki <span>a vezérlőpult</span></h2>
<p>Valódi képek egy Felhom dobozról — kattints rájuk a teljes mérethez</p>
</div>
<div class="dash-grid">
<figure class="dash-shot">
<a href="/assets/dashboard-start-hu.webp"><img src="/assets/dashboard-start-hu.webp" alt="A vezérlőpult Indítópultja: az otthon telepített alkalmazások csempéi" width="1440" height="900" loading="lazy"></a>
<figcaption><strong>Indítópult</strong> — minden alkalmazásod egy kattintásra.</figcaption>
</figure>
<figure class="dash-shot">
<a href="/assets/dashboard-apps-hu.webp"><img src="/assets/dashboard-apps-hu.webp" alt="Az Alkalmazások oldal: telepített és telepíthető alkalmazások kártyái, állapottal" width="1440" height="900" loading="lazy"></a>
<figcaption><strong>Alkalmazások</strong> — telepítés pár kattintással, és látod, mi fut és mi naprakész.</figcaption>
</figure>
<figure class="dash-shot">
<a href="/assets/dashboard-app-hu.webp"><img src="/assets/dashboard-app-hu.webp" alt="Egy alkalmazás saját oldala: mire jó, képek, és az első lépések" width="1440" height="900" loading="lazy"></a>
<figcaption><strong>Egy alkalmazás oldala</strong> — mire jó, hova tedd a fájlokat, és hogyan kezdj hozzá.</figcaption>
</figure>
<figure class="dash-shot">
<a href="/assets/dashboard-backups-hu.webp"><img src="/assets/dashboard-backups-hu.webp" alt="A mentések oldala: minden alkalmazás adatbázisának éjszakai mentése, ellenőrizve" width="1440" height="900" loading="lazy"></a>
<figcaption><strong>Mentések</strong> — minden éjjel minden alkalmazás adatbázisa mentésre kerül, és ellenőrizzük.</figcaption>
</figure>
</div>
</div>
</section>
<section class="content-section alt-bg">
<div class="container">
<div class="section-header">
+1 -1
View File
@@ -26,7 +26,7 @@
<meta name="twitter:description" content="Kérdésed van? Írj nekünk: info@felhom.eu">
<meta name="twitter:image" content="https://felhom.eu/assets/og-image.png">
<!-- shared assets: bump ?v= together on any site.css/icons.svg change -->
<link rel="stylesheet" href="/assets/site.css?v=3">
<link rel="stylesheet" href="/assets/site.css?v=4">
<link rel="icon" type="image/png" href="/assets/logo_favicon_2.svg">
<script defer src="https://stats.felhom.eu/script.js" data-website-id="d419db57-5e1c-48df-bdb3-c29f25fb1661"></script>
</head>
+1 -1
View File
@@ -24,7 +24,7 @@
<meta name="twitter:description" content="Letöltés, ellenőrző összeg, és mire figyelj a telepítés előtt.">
<meta name="twitter:image" content="https://felhom.eu/assets/og-image.png">
<!-- shared assets: bump ?v= together on any site.css/icons.svg change -->
<link rel="stylesheet" href="/assets/site.css?v=3">
<link rel="stylesheet" href="/assets/site.css?v=4">
<link rel="icon" type="image/png" href="/assets/logo_favicon_2.svg">
<script defer src="https://stats.felhom.eu/script.js" data-website-id="d419db57-5e1c-48df-bdb3-c29f25fb1661"></script>
</head>
+1 -1
View File
@@ -6,7 +6,7 @@
<meta name="description" content="Felhom.eu - Szolgáltatások és árak. Otthoni szerver telepítés, üzemeltetés, monitoring és támogatás.">
<title>Szolgáltatások - Felhom.eu</title>
<!-- shared assets: bump ?v= together on any site.css/icons.svg change -->
<link rel="stylesheet" href="/assets/site.css?v=3">
<link rel="stylesheet" href="/assets/site.css?v=4">
<link rel="icon" type="image/png" href="/assets/logo_favicon_2.svg">
<!-- analytics intentionally omitted for draft page -->
</head>
+12 -1
View File
@@ -38,7 +38,7 @@
}
</script>
<!-- shared assets: bump ?v= together on any site.css/icons.svg change -->
<link rel="stylesheet" href="/assets/site.css?v=3">
<link rel="stylesheet" href="/assets/site.css?v=4">
<link rel="icon" type="image/png" href="/assets/logo_favicon_2.svg">
<script defer src="https://stats.felhom.eu/script.js" data-website-id="d419db57-5e1c-48df-bdb3-c29f25fb1661"></script>
</head>
@@ -163,6 +163,17 @@
<li>Rendszer monitoring – folyamatos egészségfigyelés, riasztások</li>
</ul>
<div class="dash-grid">
<figure class="dash-shot">
<a href="/assets/dashboard-apps-hu.webp"><img src="/assets/dashboard-apps-hu.webp" alt="Az Alkalmazások oldal: telepített és telepíthető alkalmazások kártyái, állapottal" width="1440" height="900" loading="lazy"></a>
<figcaption><strong>Alkalmazások</strong> — telepítés pár kattintással, és látod, mi fut és mi naprakész.</figcaption>
</figure>
<figure class="dash-shot">
<a href="/assets/dashboard-backups-hu.webp"><img src="/assets/dashboard-backups-hu.webp" alt="A mentések oldala: minden alkalmazás adatbázisának éjszakai mentése, ellenőrizve" width="1440" height="900" loading="lazy"></a>
<figcaption><strong>Mentések</strong> — minden éjjel minden alkalmazás adatbázisa mentésre kerül, és ellenőrizzük.</figcaption>
</figure>
</div>
<div class="highlight-box">
<p><strong>Tökéletes:</strong> Otthoni média szerver, családi felhő, személyes projektek, kis irodai használat.</p>
</div>