Logins and folders (controller 0.320.0 released): design, R-1100..R-1102 opened, STATUS, evidence, report
gates / gates (push) Successful in 7m4s
02 "Logins and folders" [DESIGN]: an infra stack is part of the box on the app page; one login answer per installed app (catalog app_info.login_kind); FileBrowser's new-password press (its CLI, server stopped); "Fájlok ↗" and the per-drive media folders, a folder with no source is text. Register 176 -> 179. STATUS: look at FileBrowser, RomM and Plex on demo-hp; say yes for the N100's press; decision D10 (R-1102). Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
@@ -0,0 +1,126 @@
|
||||
# REPORT — every app says where its login is · FileBrowser shows its password again · a „Fájlok" button (2026-10-16)
|
||||
|
||||
Written in ASD-STE100 Simplified Technical English. Task: „logins and folders" (operator brief, daytime).
|
||||
Design read first: `architecture/02-controller-module-map.md` „The app page" (now has „Logins and folders", `[DESIGN]`),
|
||||
`15-media-library.md` §2 and §11, `CLOSED-ITEMS.md` R-513 and R-75, `app-catalog-felhom.eu/FIRST-ADMIN.md`.
|
||||
Evidence: `documentation/audits/logins-folders-2026-10-16/` (P0, P1b, live, browser, tools).
|
||||
|
||||
## 0. Decisions I took (inside the brief; each reversible)
|
||||
|
||||
1. **Media folders get no backup sentence.** The brief said „the class-driven backup sentence where a class exists". The
|
||||
only class there is `excluded`, and its sentence says „this folder is temporary and not backed up". That is false for
|
||||
the family's films. The class means „not this app's data". So: no sentence (the rule `consequenceFor` already follows
|
||||
when it cannot be sure).
|
||||
2. **An infra app has no data tab.** Export, move and the per-app backup are not the household's for a part of the box.
|
||||
`/apps/filebrowser/data` answers 302 to the page.
|
||||
3. **A sixth wording inside answer „none".** MeTube and Glance have no login of their own, but the family gate asks for
|
||||
the family name. „No sign-in needed" is false there. With `family_gate`, „none" says: you sign in with your family
|
||||
name and password. No new catalog value.
|
||||
4. **The reset uses FileBrowser's own CLI, not its environment variable** (the variable re-applies at every start).
|
||||
5. **traefik, cloudflared and samba get no login panel** — nobody signs in to them.
|
||||
|
||||
## 1. Part 0 — why FileBrowser showed „Telepítés" and lost its password
|
||||
|
||||
- FileBrowser is part of the box. It has no `app.yaml`, so the box marks it „not deployed" (and „protected").
|
||||
- Every page counted `deployed OR protected` as „on the box". The new app page (v0.315.0, commit `e66d466`) counted
|
||||
`deployed` only. So it showed the not-installed view: an Install button, no tabs, and no login. The password data was
|
||||
still there, but only the settings tab shows it, and a not-installed app has no settings tab.
|
||||
- Worse, found here: the Install button led to a deploy that had **no guard** for a protected stack. A press could have
|
||||
installed the infra stack as an app. Now refused twice (the deploy API and the deploy itself).
|
||||
- **State on both boxes:** `operator` on demo-hp (since 2026-09-15) and on the N100 (since 2026-09-16). Nobody knows
|
||||
either password.
|
||||
- **Sources:** each box has one drive source (`NVME 1TB` on demo-hp, `Külső tárhely (hdd_1)` on the N100), `Beolvasás`
|
||||
and `Megőrzött adatok`. The drive source is the drive's `userdata/`, so every `media/<folder>` is reachable.
|
||||
|
||||
## 2. What a household sees now (controller 0.320.0)
|
||||
|
||||
- **FileBrowser** is part of the box: running state, „Megnyitás ↗" to `files.<domain>`, Restart only, no Install, no
|
||||
Remove, a two-line guide. Its login: user `admin` and „Megjelenítés". If the box does not know the password: the
|
||||
sentence „Ezt a jelszót te állítottad be a Fájlkezelőben; a doboz nem ismeri." and the button „Új jelszó
|
||||
beállítása" (confirm: „A régi jelszó nem fog működni.").
|
||||
- **Every installed app** has „Belépés az alkalmazásba" with exactly one answer:
|
||||
- the box made it → „Megjelenítés" (calibre-web, kimai, nextcloud… — every app with a `type: password` field);
|
||||
- the app wrote it → „Megjelenítés" (crafty-controller; FileBrowser's is this answer too);
|
||||
- the box never had one → the v0.319.0 sentence;
|
||||
- first-visit admin → „Az admin fiókot az első megnyitáskor te hozod létre…" (the default: every other app);
|
||||
- e-mail → rallly, outline; no login → bentopdf, homepage, privatebin, onlyoffice; family sign-in → glance, metube.
|
||||
- **„Fájlok ↗"** in the header: RomM → `roms`, Calibre-Web → `media/books`, Plex → the first drive's `media`. Immich →
|
||||
no button (its data is in a named volume). The data tab lists each drive's films and series for Plex, Jellyfin and
|
||||
Emby, series for Sonarr, films for Radarr, the download folder for qBittorrent and Transmission. A folder on a drive
|
||||
that is not there now is text, not a link. Under the list: „A Fájlkezelő a saját jelszavát kéri az első alkalommal…"
|
||||
with a link to the FileBrowser page.
|
||||
|
||||
## 3. Part 1b — the „new password" button
|
||||
|
||||
- **Spike (demo-hp only):** FileBrowser 1.5.6's CLI `set -u admin,<pw>` (`backend/cmd/cli.go:206-269`) sets the
|
||||
password with no old one. The server must be stopped first: the CLI opens the database with no lock timeout
|
||||
(`backend/database/storage/storage.go:26`). Measured: unknown → K1 200; K1 → K2: K2 200, K1 401, admin/admin 401; a
|
||||
test user, a test share and the settings unchanged; cleaned up. `P1b/`.
|
||||
- **Built and pressed on demo-hp through the endpoint the button calls** (`live/fb-reset-press.txt`,
|
||||
`live/fb-reset-verify.txt`): 200 in 3.4 s; the page then offers „Megjelenítés"; the reveal returns a 16-character
|
||||
password; a second press is refused (409, the box already knows it). Checked from a second channel, FileBrowser's
|
||||
own login inside the box: new 200, the old 401, admin/admin 401; users, shares and settings unchanged; state
|
||||
`generated`. The controller log names the act and never the value.
|
||||
- **The N100: NOT pressed.** It took 0.320.0 through the normal update path. It waits for the operator's yes in chat.
|
||||
|
||||
## 4. Browser check (headless Chrome through `https://felhom.enkisfelhom.hu`, `browser/`)
|
||||
|
||||
Method: `tools/cdp_logins.py` (the last session's CDP client, extended). FileBrowser, RomM, Calibre-Web and Plex pages
|
||||
and data tabs; screenshots for each. **The cold „Fájlok" click**, with every cookie cleared first: RomM, Calibre-Web
|
||||
and Plex each went to `files…/login?redirect=<the folder>`; after FileBrowser's login each landed ON the folder
|
||||
(`/files/NVME%201TB/roms`, `…/media/books`, `…/media`). No console error on any page, except the guide tab's missing
|
||||
screenshot for FileBrowser (404; the section hides itself, as for every app without screenshots). No page wider than
|
||||
the window. The FileBrowser line's link was dark blue in rc.1 — fixed in rc.2 / the release.
|
||||
|
||||
To be able to show RomM, RomM was installed fresh on demo-hp (its old data kept aside by the product's own „fresh"
|
||||
choice). It stays for the operator's look.
|
||||
|
||||
## 5. Version, commits, fixtures, rows
|
||||
|
||||
- **felhom-controller** `39bf562` → `f30a808` (the work) → `1296e7a` (the link style) → docs. Test images
|
||||
`0.320.0-rc.1`, `rc.2` (demo-hp only); release **0.320.0**, MinAgent 0.158.1.
|
||||
- **app-catalog-felhom.eu** `806d2d0` → `5630979` (`login_kind` on 8 apps, gate `login-kind`).
|
||||
- **felhom.eu** `bc9a8713` → this commit (design, register, STATUS, evidence).
|
||||
- **Fixtures replaced (25):** the 24 `app_settings_*` fixtures and `app_data_deployed` (the login panel; the FileBrowser
|
||||
line). **New (6):** `app_settings_infra_filebrowser`, `app_guide_infra_filebrowser`, `app_settings_login_email`,
|
||||
`app_settings_login_none`, `app_settings_login_family`, `app_data_files_media`. Every other fixture, the install page
|
||||
included, byte-identical.
|
||||
- **Tests, each red-proved** (the mutation and its failure: `audits/logins-folders-2026-10-16/redproof.txt`):
|
||||
`TestAppPage_InfraAppHasNoInstall`, `_EveryInstalledAppHasOneLoginAnswer`, `_FilesButtonTarget`,
|
||||
`_UnknownSourceIsTextNotLink`, `TestFileBrowserReset_SetsVerifiesAndStoresEncrypted`, `_FailedVerifyKeepsOperator`,
|
||||
`_OnlyInOperatorState`, `TestDeployStack_RefusesProtectedStack`; catalog gate `login-kind` (decoys).
|
||||
- **Register: before 176, after 179, opened 3 (R-1100, R-1101, R-1102), closed 0.**
|
||||
|
||||
**Release (`09` §3 decision 250, `.claude/rules/unprompted-work.md` item 6):** per-box floors 0.320.0 for Tester 1,
|
||||
demo-hp and the N100; each took 0.320.0 through its normal update path (the N100 and Tester 1 within minutes, demo-hp
|
||||
at its next 15-minute report). Box checks green on all three (`release/containers.txt`: nothing unhealthy, restarting,
|
||||
exited or created; `dashboards.txt`: 200 ×3; `https-only.txt`: 35 names → 301, the negative control convicted). Then
|
||||
the **global floor 0.320.0 / MinAgent 0.158.1** with CC's token (303 `floor_set`), read back from the hub DB with its
|
||||
WAL (`release/hubdb-floors-after.txt`), and the three per-box floors cleared. On the N100, read only: the FileBrowser
|
||||
page shows the „operator" sentence and the button (`live/n100-readonly-0.320.0.txt`).
|
||||
|
||||
**System poster:** no listed fact changed (no machine, role, path, tier, time, retention or key) — none of the three
|
||||
was needed. **`unproven.py --summary`:** not walked 35 of 55, unchanged.
|
||||
|
||||
## 6. Observations
|
||||
|
||||
1. One sign-in for the dashboard and FileBrowser. FILED: R-1100.
|
||||
2. FileBrowser's stored password goes stale when changed in FileBrowser, and the page says to change it. FILED: R-1101.
|
||||
3. Eight apps say „starts with a known, shared password" about a generated or typed login. FILED: R-1102 (decision D10).
|
||||
4. The install button could deploy a protected stack. NOT-A-FINDING: small, fixed in this release with a test.
|
||||
5. Plex gets the first-visit sentence, but its login is a plex.tv account. NOT-A-FINDING: what the sentence says is
|
||||
true (the box does not know it; the household makes it at the claim).
|
||||
6. The build tags every image `latest`, test images too. NOT-A-FINDING: nothing installs or offers `latest`
|
||||
(searched the hub, the agent and the scripts).
|
||||
7. The guide tab asks for screenshots FileBrowser does not have (404 in the console). NOT-A-FINDING: the section hides
|
||||
itself, the same for every app without screenshots.
|
||||
|
||||
## 7. Teardown (three layers)
|
||||
|
||||
- **Machine:** demo-hp guest 9201 — the spike's files (`/root/fbspike`: test passwords as files, the database backup)
|
||||
removed; the spike's test user and share deleted. RomM installed fresh and **kept** (for the operator's look).
|
||||
FileBrowser's password on demo-hp is now the box's own (`generated`), shown by „Megjelenítés". The N100: nothing
|
||||
changed but the normal release update.
|
||||
- **Host:** demo-hp `/root/fbspike-tmp` and the pushed password files removed. Nothing else.
|
||||
- **Hub:** the three per-box floors set and cleared; the global floor raised to 0.320.0 / MinAgent 0.158.1 (this is the
|
||||
release, not a leftover).
|
||||
@@ -6,6 +6,7 @@
|
||||
`documentation/backlog/STATUS-HISTORY.md`. Reports: `REPORT-night-2026-10-14.md`, `REPORT-immich-rollout-2026-10-14.md`, `REPORT-seo-felho-tarhely-2026-10-14.md`, `REPORT-immich-repair-2026-10-14.md`, `REPORT-power-cut-fixes-2026-10-14.md`, `REPORT-night-power-2026-10-13.md`, `REPORT-poweroff.md`.
|
||||
|
||||
## What works
|
||||
- **Every installed app says where its login is, and has a „Fájlok ↗" button to its folder** (controller 0.320.0, 2026-10-16). The file manager is shown as part of the box again, with its password; on a box that does not know that password, one press sets a new one (proven on demo-hp).
|
||||
- **Every app but three has a user guide and an FAQ, in Hungarian and English** (catalog, 2026-10-15 night): the „Útmutató” tab shows it on boxes with controller 0.315.0 or newer. Every button name in a guide was checked in the app's own files at the version we ship; a guide that could not be checked stays hidden (plant-it, home-assistant), and Emby has none (R-1092).
|
||||
- **The N100 is the lived-in box** (since 2026-10-15): used like a household's box, changed only through the product. qBittorrent, Prowlarr, Sonarr and Radarr are installed and linked by the box; films never go off-site (checked before any download). Its diary: `documentation/lived-in/n100.md`.
|
||||
- A restored app comes back exactly as safe as it was at the backup: closed to strangers until its first account is made (0.314.0, proven on the scratch box). A box on a test build now moves with the floor (Tester 1 did, by itself). The box claims Plex again (demo-hp's Plex claimed through the dashboard, 2026-10-15). A brand-new box from the public installer updated itself to the floor 3 minutes after it was linked (test box, since removed).
|
||||
@@ -48,8 +49,11 @@
|
||||
| D7 | R-990, R-989 | Rule yes / no / later on the forum ideas (`audits/forum-research-2026-10-12/FELHOM.md` §5) and the selfh.st ideas (`audits/selfhst-survey-2026-10-12/FELHOM.md` §6) | — | — | nothing changes; the research ages | — |
|
||||
| D8 | R-206 | Install DooPlex's Docker settings from git (restarts Docker), and prune the build cache weekly: yes or no? | **Yes, in a quiet window** | — | a rebuild of DooPlex still has the settings in git | — |
|
||||
| D9 | R-1062 | The N100's USB drive is also its whole-box backup drive. Split it in two anyway? | **No — keep one drive**; the lived-in box keeps its nightly whole-box backup | Split: the box loses its local whole-box backup (or add a second drive first) | it stays one drive; multi-drive tests stay on Tester 1 | — |
|
||||
| D10 | R-1102 | Eight apps tell the household „starts with a known, shared password" about a login the box generated or the household typed. Drop that line on the eight (the login panel now says where the login is)? | **Drop it** — one catalog commit | Keep it with a new „hint" form: a controller change | the eight apps keep the misleading sentence at install and on the app page | — |
|
||||
|
||||
## Waiting on you — do
|
||||
- **Nézd meg a Fájlkezelő, a RomM és a Plex oldalát a demo-hp-n** (`felhom.enkisfelhom.hu`, controller 0.320.0). A Fájlkezelő a doboz része: nincs Telepítés gomb, a jelszava a „Megjelenítés" gombbal látszik. Minden alkalmazás megmondja, hol a belépése. A „Fájlok ↗" gomb a Fájlkezelőben nyitja az app mappáját.
|
||||
- **Say yes in chat to press „Új jelszó beállítása" for the file manager on the N100.** The N100 does not know its file-manager password (set by hand in September), so the household cannot open it. The press was proven on demo-hp (3 s; the old password stops working, nothing else changes). If you do nothing: the N100's file manager stays locked, and every „Fájlok" link there ends at a login nobody can pass.
|
||||
- **Look at `roms/roms/` on the N100's drive in the file manager (R-1099).** RomM now scans `roms/<platform>/`. A game uploaded through RomM before today sits one folder deeper: if there are platform folders in `roms/roms/`, move them up into `roms/` and press „Scan” in RomM. If not: those games show under a platform called „roms”.
|
||||
- **Nézd meg a Calibre-Web oldalát a demo-hp-n** (`felhom.enkisfelhom.hu`, controller 0.319.0, minden dobozon kiadva). A fejléc, a fülsor és minden rész most külön panel (az „A” stílus). A belépési adatok egy panelben vannak, egymás mellett. A Calibre-Web jelszaváról a doboz nem tud, mert az alkalmazás előbb települt, mint hogy a sablonban jelszó lett volna — ezt most egy mondat mondja ki. Ha nem tetszik, írd meg, mit változtassak. Ha nem nézed meg: semmi nem romlik el.
|
||||
- **Discard two unclaimed boxes and delete Tester4** (hub, browser only): the build B scratch VMs registered as unclaimed boxes — registration ids 40 (`b43afa10…`) and 41 (`9c78ea30…`); both VMs are destroyed. Tester4 (the throwaway customer) is switched off the household way. If not: two dead entries stay in the Hosts list and Tester4 stays in Customers.
|
||||
|
||||
@@ -516,6 +516,41 @@ only meet by pressing. The page and the endpoint decide „stored" with ONE func
|
||||
template gained a `type: password` field — `InjectMissingFields` never fills one
|
||||
(`audits/app-page-tuning-2026-10-16/P0`).
|
||||
|
||||
#### Logins and folders (v0.320.0, task 2026-10-16 „logins-folders")
|
||||
|
||||
**[DESIGN] A protected infra stack is part of the box, and its app page says so.** FileBrowser (and traefik,
|
||||
cloudflared, samba) has no `app.yaml`, so `Deployed` is false. Every other surface counts `Deployed || Protected` as
|
||||
on the box; v0.315.0's app page counted `Deployed` alone and showed FileBrowser „not installed", with „Telepítés" and
|
||||
without its login (measured on both demo boxes, `audits/logins-folders-2026-10-16/P0`). The app page now treats a
|
||||
protected stack as installed: settings, guide and log tabs; Restart only (as the app list); the curated name
|
||||
(`inframeta.go`); no data tab and no install page (both 302 to the page). The deploy API and `DeployStack` refuse a
|
||||
protected stack — until then the Install press would have deployed the infra compose as an app.
|
||||
|
||||
**[DESIGN] Every installed app says where its login is — ONE answer.** The settings tab's „Belépés az alkalmazásba"
|
||||
panel is on every installed app with exactly one of: the box's login fields (on demand; a field with no stored value
|
||||
is the v0.319.0 sentence) · the login the app wrote, or FileBrowser's (`InitialCreds`) · a sentence chosen by the
|
||||
catalog's `app_info.login_kind` — `first_visit` (the default: „you create the admin on the first visit; the box does
|
||||
not know the password"), `email` (a sign-in link), `none` (no sign-in; behind `family_gate` the sentence says the
|
||||
family sign-in is the login). `login_kind` is an enum, not copy: the sentence is the controller's. An infra stack with
|
||||
no login of its own (traefik, cloudflared, samba) gets no panel. Code: `web/app_login.go` `loginAnswerFor`.
|
||||
|
||||
**[DESIGN] „Új jelszó beállítása" — FileBrowser's password when the box does not know it.** In R-513's `operator`
|
||||
state (both demo boxes) the panel says „Ezt a jelszót te állítottad be a Fájlkezelőben; a doboz nem ismeri." and offers
|
||||
the press. The box uses FileBrowser's own CLI (`set -u admin,<pw>`) with the server STOPPED (the CLI opens the
|
||||
database with no lock timeout), verifies new=200 and admin/admin refused, stores the value encrypted and records
|
||||
`generated`; a failed verify leaves `operator`. Not the `FILEBROWSER_ADMIN_PASSWORD` env: it re-applies on every
|
||||
start. Spike: `audits/logins-folders-2026-10-16/P1b`. FileBrowser still asks for its own login the first time; one
|
||||
sign-in for both is R-1100.
|
||||
|
||||
**[DESIGN] „Fájlok ↗" — one of the app's folders, in FileBrowser.** The header button opens the folder with the library
|
||||
role, else the first declared folder, else (a media app) the first present drive's `media` folder; built only with
|
||||
`fileBrowserLink` (PathEscape). The data tab lists each drive's media folders the app uses (Sonarr `tv`, Radarr
|
||||
`movies`, the players both, a download client its `media_library.folder`) with NO backup sentence — the bind's class
|
||||
there means „not this app's data", which is not the class-driven „temporary, not backed up". **A folder whose drive
|
||||
is not bound into FileBrowser now is text, never a link** (`drivePresent` = the same gates the FileBrowser sync
|
||||
applies). The button never promises one click: FileBrowser's router brings a cold visitor back to the folder after its
|
||||
login (measured in the headless browser, `audits/logins-folders-2026-10-16/browser/`).
|
||||
|
||||
**[DESIGN] „Cím és kulcsok" is rows, not the install form.** The settings tab does not re-render the
|
||||
install wizard's fields: the address is the „Cím" row, the storage is a „Tárhely: <név>" row, every
|
||||
other install answer and generated key is a row (keys keep their eye). The read-only hint sits under
|
||||
|
||||
@@ -0,0 +1,237 @@
|
||||
=== demo-hp
|
||||
VMID Status Lock Name
|
||||
9201 running demo-hp
|
||||
9202 running demo-hp-scratch
|
||||
9401 stopped upgrade-harness
|
||||
felhom-controller gitea.dooplex.hu/admin/felhom-controller:0.319.0
|
||||
filebrowser gtstef/filebrowser:1.5.6-stable
|
||||
SETTINGS:
|
||||
config.yaml
|
||||
docker-compose.yml
|
||||
- path: "/srv/beolvasas"
|
||||
name: "Beolvasás"
|
||||
- path: "/srv/hdd_1"
|
||||
name: "NVME 1TB"
|
||||
- path: "/srv/megorzott"
|
||||
name: "Megőrzött adatok"
|
||||
- /mnt/felhom-drives/hdd_1/userdata:/srv/hdd_1
|
||||
- /mnt/sys_drive/felhom-data/userdata/import:/srv/beolvasas
|
||||
- /mnt/felhom-drives/hdd_1/appdata/romm:/srv/megorzott/hdd_1-romm:ro
|
||||
- /mnt/felhom-drives/hdd_1/kept/romm/2026-10-16_120728:/srv/megorzott/hdd_1-romm-2026-10-16_120728:ro
|
||||
- /mnt/felhom-drives/hdd_1/kept/romm/2026-10-16_113539:/srv/megorzott/hdd_1-romm-2026-10-16_113539:ro
|
||||
=== felhom-pve
|
||||
VMID Status Lock Name
|
||||
9201 running demo-felhom
|
||||
felhom-controller gitea.dooplex.hu/admin/felhom-controller:0.319.0
|
||||
filebrowser gtstef/filebrowser:1.5.6-stable
|
||||
SETTINGS:
|
||||
config.yaml
|
||||
docker-compose.yml
|
||||
- path: "/srv/beolvasas"
|
||||
name: "Beolvasás"
|
||||
- path: "/srv/hdd_1"
|
||||
name: "Külső tárhely (hdd_1)"
|
||||
- path: "/srv/megorzott"
|
||||
name: "Megőrzött adatok"
|
||||
- /mnt/felhom-drives/hdd_1/userdata:/srv/hdd_1
|
||||
- /mnt/sys_drive/felhom-data/userdata/import:/srv/beolvasas
|
||||
- /mnt/felhom-drives/hdd_1/appdata/romm:/srv/megorzott/hdd_1-romm:ro
|
||||
- /mnt/felhom-drives/hdd_1/appdata/paperless:/srv/megorzott/hdd_1-paperless:ro
|
||||
- /mnt/felhom-drives/hdd_1/appdata/nextcloud:/srv/megorzott/hdd_1-nextcloud:ro
|
||||
- /mnt/felhom-drives/hdd_1/appdata/immich:/srv/megorzott/hdd_1-immich:ro
|
||||
=== demo-hp
|
||||
/mnt->/mnt /etc/felhom-bootstrap->/etc/felhom-bootstrap /var/lib/docker/volumes/felhom-controller-data/_data->/opt/docker/felhom-controller /opt/docker/stacks->/opt/docker/stacks /var/run/docker.sock->/var/run/docker.sock
|
||||
|
||||
/mnt/felhom-drives/hdd_1/userdata/:
|
||||
documents
|
||||
downloads
|
||||
import
|
||||
media
|
||||
roms
|
||||
roms-bios
|
||||
|
||||
/mnt/felhom-drives/hdd_1/userdata/media/:
|
||||
audiobooks
|
||||
books
|
||||
comics
|
||||
grimmory
|
||||
metube
|
||||
movies
|
||||
music
|
||||
nzbget
|
||||
photos
|
||||
podcasts
|
||||
torrents
|
||||
transmission
|
||||
tv
|
||||
usenet
|
||||
actualbudget
|
||||
adventurelog
|
||||
audiobookshelf
|
||||
bentopdf
|
||||
bookstack
|
||||
calcom
|
||||
calibre-web
|
||||
claper
|
||||
cloudflared
|
||||
code-server
|
||||
crafty-controller
|
||||
dawarich
|
||||
docmost
|
||||
emby
|
||||
filebrowser
|
||||
ghost
|
||||
gitea
|
||||
glance
|
||||
gokapi
|
||||
grafana
|
||||
gramps-web
|
||||
grimmory
|
||||
grocy
|
||||
home-assistant
|
||||
homebox
|
||||
homepage
|
||||
immich
|
||||
jellyfin
|
||||
karakeep
|
||||
kimai
|
||||
komga
|
||||
lubelogger
|
||||
mealie
|
||||
metube
|
||||
n8n
|
||||
navidrome
|
||||
nextcloud
|
||||
nzbget
|
||||
onlyoffice
|
||||
opengist
|
||||
outline
|
||||
paperless-ngx
|
||||
papra
|
||||
plant-it
|
||||
plex
|
||||
privatebin
|
||||
prowlarr
|
||||
qbittorrent
|
||||
radarr
|
||||
radicale
|
||||
rallly
|
||||
recipe-importer
|
||||
romm
|
||||
sabnzbd
|
||||
seerr
|
||||
sonarr
|
||||
sparkyfitness
|
||||
tandoor
|
||||
termix
|
||||
traefik
|
||||
transmission
|
||||
uptime-kuma
|
||||
vaultwarden
|
||||
vikunja
|
||||
wanderer
|
||||
wger
|
||||
wishlist
|
||||
zipline
|
||||
=== felhom-pve
|
||||
/mnt->/mnt /etc/felhom-bootstrap->/etc/felhom-bootstrap /var/lib/docker/volumes/felhom-controller-data/_data->/opt/docker/felhom-controller /opt/docker/stacks->/opt/docker/stacks /var/run/docker.sock->/var/run/docker.sock
|
||||
|
||||
/mnt/felhom-drives/hdd_1/userdata/:
|
||||
documents
|
||||
downloads
|
||||
import
|
||||
media
|
||||
roms
|
||||
roms-bios
|
||||
|
||||
/mnt/felhom-drives/hdd_1/userdata/media/:
|
||||
audiobooks
|
||||
books
|
||||
comics
|
||||
grimmory
|
||||
metube
|
||||
movies
|
||||
music
|
||||
nzbget
|
||||
photos
|
||||
podcasts
|
||||
torrents
|
||||
transmission
|
||||
tv
|
||||
usenet
|
||||
actualbudget
|
||||
adventurelog
|
||||
audiobookshelf
|
||||
bentopdf
|
||||
bookstack
|
||||
calcom
|
||||
calibre-web
|
||||
claper
|
||||
cloudflared
|
||||
code-server
|
||||
crafty-controller
|
||||
dawarich
|
||||
docmost
|
||||
emby
|
||||
filebrowser
|
||||
ghost
|
||||
gitea
|
||||
glance
|
||||
gokapi
|
||||
grafana
|
||||
gramps-web
|
||||
grimmory
|
||||
grocy
|
||||
home-assistant
|
||||
homebox
|
||||
homepage
|
||||
immich
|
||||
jellyfin
|
||||
karakeep
|
||||
kimai
|
||||
komga
|
||||
lubelogger
|
||||
mealie
|
||||
metube
|
||||
n8n
|
||||
navidrome
|
||||
nextcloud
|
||||
nzbget
|
||||
onlyoffice
|
||||
opengist
|
||||
outline
|
||||
paperless-ngx
|
||||
papra
|
||||
plant-it
|
||||
plex
|
||||
privatebin
|
||||
prowlarr
|
||||
qbittorrent
|
||||
radarr
|
||||
radicale
|
||||
rallly
|
||||
recipe-importer
|
||||
romm
|
||||
sabnzbd
|
||||
seerr
|
||||
sonarr
|
||||
sparkyfitness
|
||||
tandoor
|
||||
termix
|
||||
traefik
|
||||
transmission
|
||||
uptime-kuma
|
||||
vaultwarden
|
||||
vikunja
|
||||
wanderer
|
||||
wger
|
||||
wishlist
|
||||
zipline
|
||||
=== demo-hp
|
||||
FILE /var/lib/docker/volumes/felhom-controller-data/_data/data/settings.json
|
||||
"filebrowser_admin_state": "operator"
|
||||
"filebrowser_admin_decided_at": "2026-09-15T08:52:10Z"
|
||||
=== felhom-pve
|
||||
FILE /var/lib/docker/volumes/felhom-controller-data/_data/data/settings.json
|
||||
"filebrowser_admin_state": "operator"
|
||||
"filebrowser_admin_decided_at": "2026-09-16T15:37:26Z"
|
||||
@@ -0,0 +1,48 @@
|
||||
# P0 — why FileBrowser showed „Telepítés" and lost its password (2026-10-16, read only)
|
||||
|
||||
Method: source read (controller `main` 39bf562 = v0.319.0) + the live page through
|
||||
`https://felhom.enkisfelhom.hu` (curl, logged in) + names/states read with `pct exec 9201` on both boxes.
|
||||
No value of any secret was read or printed; only the state word of `filebrowser_admin_state`.
|
||||
|
||||
## 1. Why `/apps/filebrowser` renders as not installed, with an Install button
|
||||
|
||||
- FileBrowser is base infrastructure: `stacks/infra.go` `EnsureBaseStack` writes `/opt/docker/stacks/filebrowser/`
|
||||
(compose + config.yaml) and starts it. It never writes `app.yaml`.
|
||||
- `stacks/manager.go` `ScanStacks` lists every dir under the stacks dir with a compose file. FileBrowser is in the list
|
||||
with `Deployed = appCfg != nil && appCfg.Deployed` = **false** (no app.yaml) and `Protected = true`.
|
||||
Its metadata is the no-`.felhom.yml` default (`Slug = "filebrowser"`, DisplayName „Filebrowser").
|
||||
- Every other surface already counts `Deployed || Protected` as „on the box" (dashboard, launcher, app list —
|
||||
`handlers.go` ~154/315/361, `stacks.html` L30/L76). **The v0.315.0 app page (`e66d466`) uses `found.Deployed` only**
|
||||
(`web/app_page.go` `appPageHandler`): not deployed → no tab bar, the guide tab is the whole page, and the guide
|
||||
renders the Install CTA (`app_guide.html` L45, `canInstall` is true for metadata with no lifecycle).
|
||||
- The R-513 block in `appCommonData` (handlers.go ~L856) still sets `InitialCreds` for FileBrowser — but the new
|
||||
page shows `InitialCreds` only on the SETTINGS tab, which a „not deployed" app never reaches. The old
|
||||
`app_info.html` rendered `{{if .InitialCreds}}` without a Deployed gate; that is how it showed before.
|
||||
- **Commit that changed it: `e66d466` (v0.315.0, one app page with four tabs).**
|
||||
- Extra finding: `DeployStack` has NO protected-stack guard (it refuses only Deployed / not installable). The Install
|
||||
button led to a wizard whose POST would have deployed the infra stack as an app. Guarded in this task.
|
||||
|
||||
Live confirmation (demo-hp 9201, controller 0.319.0): `demo-hp-apps-filebrowser-markers.txt` — the page carries
|
||||
`data-install-cta`, „Telepítés", no `data-tab`, no `initcred`.
|
||||
|
||||
## 2. FileBrowser admin state (state word only) — `P0-state.txt`
|
||||
|
||||
| box | state | decided at |
|
||||
|---|---|---|
|
||||
| demo-hp 9201 | `operator` | 2026-09-15T08:52:10Z |
|
||||
| demo-felhom 9201 (N100, lived-in) | `operator` | 2026-09-16T15:37:26Z |
|
||||
|
||||
Both boxes: nobody knows the password (R-513 found admin/admin refused and left it).
|
||||
|
||||
## 3. FileBrowser sources (rendered `config.yaml`) — `P0-state.txt`
|
||||
|
||||
| box | drive source | import | kept |
|
||||
|---|---|---|---|
|
||||
| demo-hp | `NVME 1TB` → `/srv/hdd_1` = `/mnt/felhom-drives/hdd_1/userdata` | `Beolvasás` | `Megőrzött adatok` |
|
||||
| demo-felhom | `Külső tárhely (hdd_1)` → `/srv/hdd_1` = `/mnt/felhom-drives/hdd_1/userdata` | `Beolvasás` | `Megőrzött adatok` |
|
||||
|
||||
Each box has ONE drive. The drive source root is the drive's `userdata/`, so `media/` and every
|
||||
`media/<movies|tv|music|torrents|transmission|…>` folder is reachable as `<drive source>/media/<sub>` — measured: the
|
||||
folders exist on both boxes (`ls /mnt/felhom-drives/hdd_1/userdata/media/`). Source names come from the storage
|
||||
path's label, else the mount basename — the same rule `fbSourceNameForApp` uses, so the link builder and the
|
||||
sidebar agree by construction.
|
||||
@@ -0,0 +1,5 @@
|
||||
1 /install" class="btn btn-primary" data-install-cta="1"
|
||||
1 Telepítés
|
||||
1 Telepítés gombbal.
|
||||
1 Telepítési konfiguráció (
|
||||
1 app-hero-name">Filebrowser
|
||||
@@ -0,0 +1,33 @@
|
||||
# P1b — resetting FileBrowser's admin password WITHOUT the old one (spike, demo-hp guest 9201 only, 2026-10-16)
|
||||
|
||||
**Verdict: a safe reset exists. Built.**
|
||||
|
||||
## The mechanism, read at the pinned tag
|
||||
|
||||
`gtstef/filebrowser:1.5.6-stable` = `gtsteffaniak/filebrowser` tag `v1.5.6-stable` (`5d9b4df`).
|
||||
|
||||
- **CLI `filebrowser set -u <user>,<password> -c <config>`** — `backend/cmd/cli.go:60-67` (the flag set), `:117-126`
|
||||
(dispatch), `:206-269` `setUser`: an existing user gets `user.Password = password`, its TOTP secret cleared, and
|
||||
`store.Users.Save(user, true, false)` (hashes the password). It needs NO old password.
|
||||
- It opens the database with `storm.Open(path)` (`backend/database/storage/storage.go:26`) with no timeout: while the
|
||||
server runs, the bolt lock is held and the CLI would wait forever. So the server is STOPPED for the CLI, and the CLI
|
||||
runs in a one-shot container with the running container's volumes (`docker run --rm --volumes-from filebrowser`).
|
||||
- Not chosen: `FILEBROWSER_ADMIN_PASSWORD` / `auth.adminPassword` (`backend/common/settings/config.go:923-926`,
|
||||
applied in `backend/cmd/user.go:50-57` `validateUserInfo`) — it re-applies on EVERY start (R-513 measured the same on
|
||||
1.3.3), so it would need a compose change plus two recreates, and the value would sit in the container env.
|
||||
|
||||
## Measured (`spike-step1.txt`, `spike-step2.txt`, `spike-step3.txt`, `spike-cleanup.txt`; script `fbspike.sh`)
|
||||
|
||||
1. State `operator`, password unknown (R-513 left it 2026-09-15). A DB backup copied first.
|
||||
2. Reset to K1 with the server stopped: CLI rc 0, „successfully updated user", healthy 7 s after the stop; **K1 → 200**.
|
||||
3. A test user and a test share made as K1 (user 201, share 200). Settings hash `12a6170ac405fa72`.
|
||||
4. Reset to K2 without K1: **K2 → 200, K1 → 401, admin/admin → 401**; users `admin` + `fbspike`, the share, and
|
||||
the settings hash **unchanged**.
|
||||
5. Test user and share deleted (200/200).
|
||||
|
||||
Passwords were generated on DooPlex (16 letters and digits — never a comma, which the CLI's `user,password` form would
|
||||
split on), sent as files (0600), used from files, and never printed. The CLI carries the password on its command line
|
||||
for about one second inside the guest; this is the only exposure and it is inside the household's own box.
|
||||
|
||||
After the spike demo-hp's FileBrowser password is K2 (kept out-of-band in the session scratchpad) and the controller
|
||||
still records `operator` — the built button replaces it (Part 1b build).
|
||||
@@ -0,0 +1,64 @@
|
||||
#!/bin/sh
|
||||
# P1b spike — runs INSIDE demo-hp guest 9201. Passwords come from files in $D (0600); never printed.
|
||||
# Usage: fbspike.sh <step> steps: backup | reset <pwfile> | login <pwfile> | inventory <pwfile> | seed <pwfile> | cleanup <pwfile>
|
||||
set -u
|
||||
D=/root/fbspike
|
||||
IMG=gtstef/filebrowser:1.5.6-stable
|
||||
IP=$(docker inspect filebrowser --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' 2>/dev/null)
|
||||
B="http://$IP:80"
|
||||
hdr() { printf 'X-Password: %s\n' "$(cat "$1")" > $D/.h; chmod 600 $D/.h; }
|
||||
login_code() { hdr "$1"; curl -s -o $D/.tok -w '%{http_code}' -X POST -H @$D/.h "$B/api/auth/login?username=admin"; rm -f $D/.h; }
|
||||
tok() { login_code "$1" >/dev/null; tr -d '"' < $D/.tok; }
|
||||
case "$1" in
|
||||
backup)
|
||||
V=$(docker volume inspect filebrowser_filebrowser_data --format '{{.Mountpoint}}')
|
||||
cp -a "$V/database.db" $D/database.db.before-spike && ls -l $D/database.db.before-spike | awk '{print "backup bytes",$5}'
|
||||
;;
|
||||
reset)
|
||||
t0=$(date +%s)
|
||||
docker stop filebrowser >/dev/null && echo "stopped"
|
||||
P=$(cat "$2")
|
||||
timeout 60 docker run --rm --volumes-from filebrowser --user 1000:1000 --workdir /home/filebrowser \
|
||||
--entrypoint /home/filebrowser/filebrowser "$IMG" set -u "admin,$P" -c /home/filebrowser/config.yaml >$D/.cli 2>&1
|
||||
rc=$?; unset P
|
||||
echo "cli rc=$rc"; grep -o 'successfully [a-z]* user[^ ]*' $D/.cli; rm -f $D/.cli
|
||||
docker start filebrowser >/dev/null && echo "started"
|
||||
for i in $(seq 1 60); do s=$(docker inspect filebrowser --format '{{.State.Health.Status}}'); [ "$s" = healthy ] && break; sleep 2; done
|
||||
IP=$(docker inspect filebrowser --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'); echo "health=$s after $(( $(date +%s) - t0 ))s"
|
||||
;;
|
||||
login)
|
||||
echo "login $(basename $2): $(login_code "$2")"; rm -f $D/.tok
|
||||
;;
|
||||
inventory)
|
||||
T=$(tok "$2")
|
||||
echo "users:"; curl -s -H "Authorization: Bearer $T" "$B/api/users" | python3 -c 'import json,sys;[print(" ",u.get("id"),u.get("username"),"admin" if u.get("permissions",{}).get("admin") else "-") for u in json.load(sys.stdin)]'
|
||||
echo "shares:"; curl -s -H "Authorization: Bearer $T" "$B/api/share/list" | python3 -c 'import json,sys;d=json.load(sys.stdin);d=d if isinstance(d,list) else [];[print(" ",s.get("hash"),s.get("path"),s.get("source")) for s in d];print(" count",len(d))'
|
||||
echo "settings:"; curl -s -H "Authorization: Bearer $T" "$B/api/settings" | python3 -c 'import json,sys,hashlib;d=json.load(sys.stdin);d.get("auth",{}).pop("key",None);print(" sources",[x.get("name") for x in d.get("server",{}).get("sources",[])]);print(" sha256",hashlib.sha256(json.dumps(d,sort_keys=True).encode()).hexdigest()[:16])'
|
||||
rm -f $D/.tok
|
||||
;;
|
||||
seed)
|
||||
T=$(tok "$2")
|
||||
hdr "$2"; curl -s -o /dev/null -w 'create user %{http_code}\n' -X POST -H @$D/.h -H "Authorization: Bearer $T" -H 'Content-Type: application/json' \
|
||||
-d '{"which":[],"data":{"username":"fbspike","password":"fbspike-'"$(head -c6 /dev/urandom | od -An -tx1 | tr -d ' \n')"'","loginMethod":"password","scopes":[]}}' "$B/api/users"
|
||||
rm -f $D/.h; [ -n "${3:-}" ] && { rm -f $D/.tok; exit 0; }
|
||||
curl -s -o /dev/null -w 'create share %{http_code}\n' -X POST -H "Authorization: Bearer $T" -H 'Content-Type: application/json' \
|
||||
-d '{"path":"/media","source":"NVME 1TB","expires":"","unit":"hours","password":null}' "$B/api/share"
|
||||
rm -f $D/.tok
|
||||
;;
|
||||
probe)
|
||||
T=$(tok "$2"); echo "toklen ${#T}"
|
||||
for p in api/users api/shares api/settings "api/users?id=self"; do
|
||||
curl -s -o $D/.o -w "$p %{http_code} " -H "Authorization: Bearer $T" "$B/$p"
|
||||
head -c 160 $D/.o | sed 's/"password":"[^"]*"/"password":"REDACTED"/g'; echo
|
||||
done
|
||||
rm -f $D/.o $D/.tok
|
||||
;;
|
||||
cleanup)
|
||||
T=$(tok "$2")
|
||||
id=$(curl -s -H "Authorization: Bearer $T" "$B/api/users" | python3 -c 'import json,sys;print(next((u["id"] for u in json.load(sys.stdin) if u["username"]=="fbspike"),""))')
|
||||
hdr "$2"; [ -n "$id" ] && curl -s -o /dev/null -w "delete user %{http_code}\n" -X DELETE -H @$D/.h -H "Authorization: Bearer $T" "$B/api/users?id=$id"; rm -f $D/.h
|
||||
for h in $(curl -s -H "Authorization: Bearer $T" "$B/api/share/list" | python3 -c 'import json,sys;d=json.load(sys.stdin);d=d if isinstance(d,list) else [];[print(s["hash"]) for s in d if s.get("path","").rstrip("/")=="/media"]'); do
|
||||
curl -s -o /dev/null -w "delete share %{http_code}\n" -X DELETE -H "Authorization: Bearer $T" "$B/api/share?hash=$h"; done
|
||||
rm -f $D/.tok
|
||||
;;
|
||||
esac
|
||||
@@ -0,0 +1,10 @@
|
||||
## 2026-10-16T12:17:12Z cleanup: the test user and the test share
|
||||
delete user 200
|
||||
delete share 200
|
||||
users:
|
||||
1 admin admin
|
||||
shares:
|
||||
count 0
|
||||
settings:
|
||||
sources ['Beolvasás', 'NVME 1TB', 'Megőrzött adatok']
|
||||
sha256 12a6170ac405fa72
|
||||
@@ -0,0 +1,33 @@
|
||||
## 2026-10-16T12:14:17Z step 1: state operator, password unknown; admin/admin and K1 before
|
||||
login K1: 401
|
||||
## reset to K1 with the CLI, server stopped
|
||||
stopped
|
||||
cli rc=0
|
||||
successfully updated user:
|
||||
started
|
||||
health=healthy after 7s
|
||||
login K1: 200
|
||||
users:
|
||||
Traceback (most recent call last):
|
||||
File "<string>", line 1, in <module>
|
||||
import json,sys;[print(" ",u.get("id"),u.get("username"),"admin" if u.get("permissions",{}).get("admin") else "-") for u in json.load(sys.stdin)]
|
||||
^^^^^
|
||||
AttributeError: 'str' object has no attribute 'get'
|
||||
shares:
|
||||
Traceback (most recent call last):
|
||||
File "<string>", line 1, in <module>
|
||||
import json,sys;d=json.load(sys.stdin);d=d if isinstance(d,list) else [];[print(" ",s.get("hash"),s.get("path"),s.get("source")) for s in d];print(" count",len(d))
|
||||
~~~~~~~~~^^^^^^^^^^^
|
||||
File "/usr/lib/python3.13/json/__init__.py", line 293, in load
|
||||
return loads(fp.read(),
|
||||
cls=cls, object_hook=object_hook,
|
||||
parse_float=parse_float, parse_int=parse_int,
|
||||
parse_constant=parse_constant, object_pairs_hook=object_pairs_hook, **kw)
|
||||
File "/usr/lib/python3.13/json/__init__.py", line 346, in loads
|
||||
return _default_decoder.decode(s)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~^^^
|
||||
File "/usr/lib/python3.13/json/decoder.py", line 348, in decode
|
||||
raise JSONDecodeError("Extra data", s, end)
|
||||
json.decoder.JSONDecodeError: Extra data: line 1 column 5 (char 4)
|
||||
settings sources:
|
||||
keys ['message', 'status']
|
||||
@@ -0,0 +1,22 @@
|
||||
## 2026-10-16T12:16:26Z step 2: seed a user and a share as K1, inventory
|
||||
create user 401
|
||||
create share 200
|
||||
users:
|
||||
1 admin admin
|
||||
shares:
|
||||
aoSyMglz7IyGgR9e_WVg4Q /media/ NVME 1TB
|
||||
count 1
|
||||
settings:
|
||||
sources ['Beolvasás', 'NVME 1TB', 'Megőrzött adatok']
|
||||
sha256 12a6170ac405fa72
|
||||
## 2026-10-16T12:16:43Z step 2b: seed the user (with X-Password), inventory
|
||||
create user 201
|
||||
users:
|
||||
1 admin admin
|
||||
2 fbspike -
|
||||
shares:
|
||||
aoSyMglz7IyGgR9e_WVg4Q /media/ NVME 1TB
|
||||
count 1
|
||||
settings:
|
||||
sources ['Beolvasás', 'NVME 1TB', 'Megőrzött adatok']
|
||||
sha256 12a6170ac405fa72
|
||||
@@ -0,0 +1,19 @@
|
||||
## 2026-10-16T12:16:51Z step 3: reset to K2 without K1 (CLI, server stopped)
|
||||
stopped
|
||||
cli rc=0
|
||||
successfully updated user:
|
||||
started
|
||||
health=healthy after 7s
|
||||
login K2: 200
|
||||
login K1: 401
|
||||
admin/admin:
|
||||
login ADM: 401
|
||||
users:
|
||||
1 admin admin
|
||||
2 fbspike -
|
||||
shares:
|
||||
aoSyMglz7IyGgR9e_WVg4Q /media/ NVME 1TB
|
||||
count 1
|
||||
settings:
|
||||
sources ['Beolvasás', 'NVME 1TB', 'Megőrzött adatok']
|
||||
sha256 12a6170ac405fa72
|
||||
|
After Width: | Height: | Size: 198 KiB |
|
After Width: | Height: | Size: 98 KiB |
|
After Width: | Height: | Size: 110 KiB |
|
After Width: | Height: | Size: 79 KiB |
|
After Width: | Height: | Size: 176 KiB |
|
After Width: | Height: | Size: 80 KiB |
|
After Width: | Height: | Size: 185 KiB |
|
After Width: | Height: | Size: 183 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 67 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 70 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 46 KiB |
@@ -0,0 +1,219 @@
|
||||
after login url: /launcher
|
||||
{
|
||||
"/apps/filebrowser": {
|
||||
"path": "/apps/filebrowser",
|
||||
"title": "FileBrowser",
|
||||
"tabs": [
|
||||
"settings",
|
||||
"guide",
|
||||
"logs"
|
||||
],
|
||||
"install": false,
|
||||
"loginAnswer": [
|
||||
"initial"
|
||||
],
|
||||
"loginKind": [],
|
||||
"fbOperator": false,
|
||||
"fbReset": false,
|
||||
"reveal": true,
|
||||
"open": "https://files.enkisfelhom.hu/",
|
||||
"files": null,
|
||||
"folders": [],
|
||||
"fbNote": false,
|
||||
"scrollWidth": 1280,
|
||||
"console": []
|
||||
},
|
||||
"/apps/filebrowser/guide": {
|
||||
"path": "/apps/filebrowser/guide",
|
||||
"title": "FileBrowser",
|
||||
"tabs": [
|
||||
"settings",
|
||||
"guide",
|
||||
"logs"
|
||||
],
|
||||
"install": false,
|
||||
"loginAnswer": [],
|
||||
"loginKind": [],
|
||||
"fbOperator": false,
|
||||
"fbReset": false,
|
||||
"reveal": false,
|
||||
"open": "https://files.enkisfelhom.hu/",
|
||||
"files": null,
|
||||
"folders": [],
|
||||
"fbNote": false,
|
||||
"scrollWidth": 1280,
|
||||
"console": [
|
||||
[
|
||||
"log-error",
|
||||
"Failed to load resource: the server responded with a status of 404 ()",
|
||||
"https://felhom.enkisfelhom.hu/static/assets/filebrowser-screenshot-3.webp"
|
||||
]
|
||||
]
|
||||
},
|
||||
"/apps/romm": {
|
||||
"path": "/apps/romm",
|
||||
"title": "RomM",
|
||||
"tabs": [
|
||||
"settings",
|
||||
"data",
|
||||
"guide",
|
||||
"logs"
|
||||
],
|
||||
"install": false,
|
||||
"loginAnswer": [
|
||||
"first_visit"
|
||||
],
|
||||
"loginKind": [
|
||||
"first_visit"
|
||||
],
|
||||
"fbOperator": false,
|
||||
"fbReset": false,
|
||||
"reveal": false,
|
||||
"open": "https://arcade.enkisfelhom.hu/",
|
||||
"files": "https://files.enkisfelhom.hu/files/NVME%201TB/roms",
|
||||
"folders": [],
|
||||
"fbNote": false,
|
||||
"scrollWidth": 1270,
|
||||
"console": []
|
||||
},
|
||||
"/apps/romm/data": {
|
||||
"path": "/apps/romm/data",
|
||||
"title": "RomM",
|
||||
"tabs": [
|
||||
"settings",
|
||||
"data",
|
||||
"guide",
|
||||
"logs"
|
||||
],
|
||||
"install": false,
|
||||
"loginAnswer": [],
|
||||
"loginKind": [],
|
||||
"fbOperator": false,
|
||||
"fbReset": false,
|
||||
"reveal": false,
|
||||
"open": "https://arcade.enkisfelhom.hu/",
|
||||
"files": "https://files.enkisfelhom.hu/files/NVME%201TB/roms",
|
||||
"folders": [
|
||||
"https://files.enkisfelhom.hu/files/NVME%201TB/roms",
|
||||
"https://files.enkisfelhom.hu/files/NVME%201TB/roms-bios"
|
||||
],
|
||||
"fbNote": true,
|
||||
"scrollWidth": 1270,
|
||||
"console": []
|
||||
},
|
||||
"/apps/calibre-web": {
|
||||
"path": "/apps/calibre-web",
|
||||
"title": "Calibre-Web Automated",
|
||||
"tabs": [
|
||||
"settings",
|
||||
"data",
|
||||
"guide",
|
||||
"logs"
|
||||
],
|
||||
"install": false,
|
||||
"loginAnswer": [
|
||||
"fields"
|
||||
],
|
||||
"loginKind": [],
|
||||
"fbOperator": false,
|
||||
"fbReset": false,
|
||||
"reveal": false,
|
||||
"open": "https://books.enkisfelhom.hu/",
|
||||
"files": "https://files.enkisfelhom.hu/files/NVME%201TB/media/books",
|
||||
"folders": [],
|
||||
"fbNote": false,
|
||||
"scrollWidth": 1270,
|
||||
"console": []
|
||||
},
|
||||
"/apps/calibre-web/data": {
|
||||
"path": "/apps/calibre-web/data",
|
||||
"title": "Calibre-Web Automated",
|
||||
"tabs": [
|
||||
"settings",
|
||||
"data",
|
||||
"guide",
|
||||
"logs"
|
||||
],
|
||||
"install": false,
|
||||
"loginAnswer": [],
|
||||
"loginKind": [],
|
||||
"fbOperator": false,
|
||||
"fbReset": false,
|
||||
"reveal": false,
|
||||
"open": "https://books.enkisfelhom.hu/",
|
||||
"files": "https://files.enkisfelhom.hu/files/NVME%201TB/media/books",
|
||||
"folders": [
|
||||
"https://files.enkisfelhom.hu/files/Beolvas%C3%A1s/calibre",
|
||||
"https://files.enkisfelhom.hu/files/NVME%201TB/media/books"
|
||||
],
|
||||
"fbNote": true,
|
||||
"scrollWidth": 1270,
|
||||
"console": []
|
||||
},
|
||||
"/apps/plex": {
|
||||
"path": "/apps/plex",
|
||||
"title": "Plex",
|
||||
"tabs": [
|
||||
"settings",
|
||||
"data",
|
||||
"guide",
|
||||
"logs"
|
||||
],
|
||||
"install": false,
|
||||
"loginAnswer": [
|
||||
"first_visit"
|
||||
],
|
||||
"loginKind": [
|
||||
"first_visit"
|
||||
],
|
||||
"fbOperator": false,
|
||||
"fbReset": false,
|
||||
"reveal": false,
|
||||
"open": "https://plex.enkisfelhom.hu/",
|
||||
"files": "https://files.enkisfelhom.hu/files/NVME%201TB/media",
|
||||
"folders": [],
|
||||
"fbNote": false,
|
||||
"scrollWidth": 1270,
|
||||
"console": []
|
||||
},
|
||||
"/apps/plex/data": {
|
||||
"path": "/apps/plex/data",
|
||||
"title": "Plex",
|
||||
"tabs": [
|
||||
"settings",
|
||||
"data",
|
||||
"guide",
|
||||
"logs"
|
||||
],
|
||||
"install": false,
|
||||
"loginAnswer": [],
|
||||
"loginKind": [],
|
||||
"fbOperator": false,
|
||||
"fbReset": false,
|
||||
"reveal": false,
|
||||
"open": "https://plex.enkisfelhom.hu/",
|
||||
"files": "https://files.enkisfelhom.hu/files/NVME%201TB/media",
|
||||
"folders": [
|
||||
"https://files.enkisfelhom.hu/files/NVME%201TB/media/movies",
|
||||
"https://files.enkisfelhom.hu/files/NVME%201TB/media/tv"
|
||||
],
|
||||
"fbNote": true,
|
||||
"scrollWidth": 1270,
|
||||
"console": []
|
||||
},
|
||||
"cold:romm": {
|
||||
"target": "https://files.enkisfelhom.hu/files/NVME%201TB/roms",
|
||||
"first": "/login?redirect=/files/NVME%25201TB/roms",
|
||||
"after_login": "/files/NVME%201TB/roms"
|
||||
},
|
||||
"cold:calibre-web": {
|
||||
"target": "https://files.enkisfelhom.hu/files/NVME%201TB/media/books",
|
||||
"first": "/login?redirect=/files/NVME%25201TB/media/books",
|
||||
"after_login": "/files/NVME%201TB/media/books"
|
||||
},
|
||||
"cold:plex": {
|
||||
"target": "https://files.enkisfelhom.hu/files/NVME%201TB/media",
|
||||
"first": "/login?redirect=/files/NVME%25201TB/media",
|
||||
"after_login": "/files/NVME%201TB/media"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
200 /apps/filebrowser :: 1 data-fb-operator 1 data-fb-reset 1 data-login-answer="initial" 1 data-tab="guide" 1 data-tab="logs" 1 data-tab="settings"
|
||||
302 /apps/filebrowser /apps/filebrowser/install ::
|
||||
302 /apps/filebrowser /apps/filebrowser/data ::
|
||||
200 /apps/filebrowser/guide :: 1 data-infra-guide 1 data-tab="guide" 1 data-tab="logs" 1 data-tab="settings"
|
||||
200 /apps/romm :: 1 data-install-cta
|
||||
302 /apps/romm /apps/romm/data ::
|
||||
200 /apps/calibre-web :: 1 data-app-files="1" 1 data-login-answer="fields" 1 data-tab="data" 1 data-tab="guide" 1 data-tab="logs" 1 data-tab="settings" href="https://files.enkisfelhom.hu/files/NVME%201TB/media/books" target="_blank" rel="noopener" class="btn btn-outline" data-app-files
|
||||
|
||||
200 /apps/calibre-web/data :: 1 data-app-files="1" 1 data-fb-login-note 1 data-tab="data" 1 data-tab="guide" 1 data-tab="logs" 1 data-tab="settings" href="https://files.enkisfelhom.hu/files/NVME%201TB/media/books" target="_blank" rel="noopener" class="btn btn-outline" data-app-files
|
||||
|
||||
200 /apps/plex :: 1 data-app-files="1" 1 data-login-answer="first_visit" 1 data-login-kind="first_visit" 1 data-tab="data" 1 data-tab="guide" 1 data-tab="logs" 1 data-tab="settings" href="https://files.enkisfelhom.hu/files/NVME%201TB/media" target="_blank" rel="noopener" class="btn btn-outline" data-app-files
|
||||
|
||||
200 /apps/plex/data :: 1 data-app-files="1" 1 data-fb-login-note 2 data-media-folder 1 data-tab="data" 1 data-tab="guide" 1 data-tab="logs" 1 data-tab="settings" href="https://files.enkisfelhom.hu/files/NVME%201TB/media" target="_blank" rel="noopener" class="btn btn-outline" data-app-files
|
||||
|
||||
200 /apps/immich :: 1 data-install-cta
|
||||
200 /apps/bentopdf :: 1 data-login-answer="first_visit" 1 data-login-kind="first_visit" 1 data-tab="data" 1 data-tab="guide" 1 data-tab="logs" 1 data-tab="settings"
|
||||
@@ -0,0 +1,12 @@
|
||||
## 2026-10-16T13:26:04Z press reset (POST /apps/filebrowser/initial-credentials/reset)
|
||||
{"data":null,"error":"","ok":true}
|
||||
HTTP 200 in 3.362612s
|
||||
## the page after
|
||||
1 data-login-answer="initial"
|
||||
1 id="initcred-reveal"
|
||||
## reveal (value kept out of this file; length only)
|
||||
HTTP 200
|
||||
ok True len 16
|
||||
## a second press (now generated) must be refused
|
||||
{"data":null,"error":"A Fájlkezelő jelszavát a doboz már ismeri — a Megjelenítés gomb mutatja.","ok":false}
|
||||
HTTP 409
|
||||
@@ -0,0 +1,20 @@
|
||||
login NEWPW: 200
|
||||
login K2: 401
|
||||
login ADM: 401
|
||||
users:
|
||||
1 admin admin
|
||||
shares:
|
||||
count 0
|
||||
settings:
|
||||
sources ['Beolvasás', 'NVME 1TB', 'Megőrzött adatok']
|
||||
sha256 12a6170ac405fa72
|
||||
e920c91f524e filebrowser filebrowser gtstef/filebrowser:1.5.6-stable
|
||||
2026/10/16 13:20:19 handlers.go:3869: [INFO] [web] FileBrowser sync — no config/compose change, ensured running without recreate (1 storage path(s))
|
||||
e920c91f524e filebrowser filebrowser gtstef/filebrowser:1.5.6-stable
|
||||
2026/10/16 13:26:04 handlers.go:2742: [INFO] [web] filebrowser: new admin password requested from 37.191.56.193
|
||||
2026/10/16 13:26:04 filebrowser_password.go:220: [INFO] [infra] filebrowser: the household asked for a new admin password — stopping the file browser for its CLI
|
||||
2026/10/16 13:26:08 filebrowser_password.go:258: [INFO] [infra] filebrowser: admin password replaced at the household's press (value never logged); verified new=200 admin=refused in 3s
|
||||
2026/10/16 13:26:08 handlers.go:2780: [INFO] [web] filebrowser admin password revealed from 37.191.56.193 (value never logged)
|
||||
2026/10/16 13:26:08 handlers.go:2742: [INFO] [web] filebrowser: new admin password requested from 37.191.56.193
|
||||
"filebrowser_admin_state": "generated"
|
||||
filebrowser Up 19 seconds (healthy)
|
||||
@@ -0,0 +1,5 @@
|
||||
200 /apps/filebrowser :: data-fb-operator data-fb-reset data-login-answer="initial"
|
||||
302 /apps/filebrowser/install ::
|
||||
200 /apps/romm :: data-install-cta
|
||||
200 /apps/plex :: data-install-cta
|
||||
200 /apps/immich :: data-install-cta
|
||||
@@ -0,0 +1,2 @@
|
||||
{"ok":true,"message":"Telepítés elindítva – az állapot a kártyán követhető"}
|
||||
HTTP 202
|
||||
@@ -0,0 +1,50 @@
|
||||
## infra counted as not installed (v0.315.0 line)
|
||||
=== RUN TestAppPage_InfraAppHasNoInstall
|
||||
logins_folders_test.go:193: FileBrowser offers Install — an infrastructure app is part of the box
|
||||
--- FAIL: TestAppPage_InfraAppHasNoInstall (0.09s)
|
||||
## no fallback login answer (v0.319.0 behaviour)
|
||||
=== RUN TestAppPage_EveryInstalledAppHasOneLoginAnswer
|
||||
logins_folders_test.go:256: immich: 0 login answers, want exactly 1
|
||||
logins_folders_test.go:256: rallly: 0 login answers, want exactly 1
|
||||
logins_folders_test.go:256: bentopdf: 0 login answers, want exactly 1
|
||||
--- FAIL: TestAppPage_EveryInstalledAppHasOneLoginAnswer (0.10s)
|
||||
## no library preference in the Files target
|
||||
## absent drive still linked
|
||||
=== RUN TestAppPage_UnknownSourceIsTextNotLink
|
||||
logins_folders_test.go:315: romm links a folder on a drive that is not on the box
|
||||
--- FAIL: TestAppPage_UnknownSourceIsTextNotLink (0.08s)
|
||||
## plaintext stored
|
||||
=== RUN TestFileBrowserReset_SetsVerifiesAndStoresEncrypted
|
||||
filebrowser_reset_test.go:56: the stored value is the plaintext password (or empty)
|
||||
--- FAIL: TestFileBrowserReset_SetsVerifiesAndStoresEncrypted (0.00s)
|
||||
## verify of the new password removed
|
||||
=== RUN TestFileBrowserReset_FailedVerifyKeepsOperator
|
||||
filebrowser_reset_test.go:73: a reset whose new password does not log in reported success
|
||||
--- FAIL: TestFileBrowserReset_FailedVerifyKeepsOperator (0.00s)
|
||||
## state check removed
|
||||
=== RUN TestFileBrowserReset_OnlyInOperatorState
|
||||
filebrowser_reset_test.go:95: state "": err = <nil>, want ErrFileBrowserNotOperator
|
||||
filebrowser_reset_test.go:98: state "": the apply ran
|
||||
filebrowser_reset_test.go:101: state "" changed to "generated"
|
||||
filebrowser_reset_test.go:95: state "generated": err = <nil>, want ErrFileBrowserNotOperator
|
||||
filebrowser_reset_test.go:98: state "generated": the apply ran
|
||||
## protected deploy guard removed
|
||||
=== RUN TestDeployStack_RefusesProtectedStack
|
||||
--- FAIL: TestDeployStack_RefusesProtectedStack (0.00s)
|
||||
## no library preference in the Files target (rerun)
|
||||
=== RUN TestAppPage_FilesButtonTarget
|
||||
logins_folders_test.go:292: calibre-web: Files → https://files.example.hu/files/Beolvas%C3%A1s/calibre, want https://files.example.hu/files/M%C3%A9dia%20&%20k%C3%B6nyvt%C3%A1r/media/books
|
||||
--- FAIL: TestAppPage_FilesButtonTarget (0.08s)
|
||||
## protected deploy guard removed (rerun)
|
||||
=== RUN TestDeployStack_RefusesProtectedStack
|
||||
--- FAIL: TestDeployStack_RefusesProtectedStack (0.00s)
|
||||
panic: runtime error: invalid memory address or nil pointer dereference [recovered, repanicked]
|
||||
panic({0xac07c0?, 0x116c870?})
|
||||
/usr/local/go/src/runtime/panic.go:860 +0x13a
|
||||
filebrowser_reset_test.go:128: filebrowser (Protected=true): err = went on into the deploy (panicked on the bare manager), want ErrProtectedStack
|
||||
filebrowser_reset_test.go:131: filebrowser: left Deploying set
|
||||
filebrowser_reset_test.go:128: filebrowser (Protected=false): err = went on into the deploy (panicked on the bare manager), want ErrProtectedStack
|
||||
filebrowser_reset_test.go:131: filebrowser: left Deploying set
|
||||
filebrowser_reset_test.go:128: samba (Protected=false): err = went on into the deploy (panicked on the bare manager), want ErrProtectedStack
|
||||
XX DECOY: a bad value only in the English block rc=1 (expected 0)
|
||||
XX DECOY: the key under ANOTHER mapping rc=1 (expected 0)
|
||||
@@ -0,0 +1,12 @@
|
||||
== tester-1
|
||||
unhealthy=0 restarting=0 exited=0 created=0
|
||||
controller gitea.dooplex.hu/admin/felhom-controller:0.320.0 Up 13 minutes (healthy)
|
||||
filebrowser Up 11 hours (healthy)
|
||||
== demo-hp
|
||||
unhealthy=0 restarting=0 exited=0 created=0
|
||||
controller gitea.dooplex.hu/admin/felhom-controller:0.320.0 Up 34 seconds (healthy)
|
||||
filebrowser Up 15 minutes (healthy)
|
||||
== demo-felhom
|
||||
unhealthy=0 restarting=0 exited=0 created=0
|
||||
controller gitea.dooplex.hu/admin/felhom-controller:0.320.0 Up 13 minutes (healthy)
|
||||
filebrowser Up 11 hours (healthy)
|
||||
@@ -0,0 +1,3 @@
|
||||
tester-1 Location: /customers/tester-1?flash=floor_set
|
||||
demo-hp Location: /customers/demo-hp?flash=floor_set
|
||||
demo-felhom Location: /customers/demo-felhom?flash=floor_set
|
||||
@@ -0,0 +1,3 @@
|
||||
felhom.enkisfelhom.hu 200
|
||||
felhom.enkicsifelhom.hu 200
|
||||
felhom.demo-felhom.eu 200
|
||||
@@ -0,0 +1,3 @@
|
||||
15:48:34
|
||||
HTTP/1.1 303 See Other
|
||||
Location: /configuration?flash=floor_set
|
||||
@@ -0,0 +1,40 @@
|
||||
control NEGATIVE (a stub answering 200 over plain http): convicted, as it must (200 -)
|
||||
== tester-1 (enkicsifelhom.hu): 13 public name(s)
|
||||
OK control POSITIVE http://felhom.enkicsifelhom.hu/r1033-check?q=1 -> 301 https://felhom.enkicsifelhom.hu/r1033-check?q=1
|
||||
OK http://files.enkicsifelhom.hu/r1033-check?q=1 -> 301 https://files.enkicsifelhom.hu/r1033-check?q=1
|
||||
OK http://media.enkicsifelhom.hu/r1033-check?q=1 -> 301 https://media.enkicsifelhom.hu/r1033-check?q=1
|
||||
OK http://no-such-name-r1033.enkicsifelhom.hu/r1033-check?q=1 -> 301 https://no-such-name-r1033.enkicsifelhom.hu/r1033-check?q=1
|
||||
OK http://paperless.enkicsifelhom.hu/r1033-check?q=1 -> 301 https://paperless.enkicsifelhom.hu/r1033-check?q=1
|
||||
OK http://paste.enkicsifelhom.hu/r1033-check?q=1 -> 301 https://paste.enkicsifelhom.hu/r1033-check?q=1
|
||||
OK http://plex.enkicsifelhom.hu/r1033-check?q=1 -> 301 https://plex.enkicsifelhom.hu/r1033-check?q=1
|
||||
OK http://prowlarr.enkicsifelhom.hu/r1033-check?q=1 -> 301 https://prowlarr.enkicsifelhom.hu/r1033-check?q=1
|
||||
OK http://radarr.enkicsifelhom.hu/r1033-check?q=1 -> 301 https://radarr.enkicsifelhom.hu/r1033-check?q=1
|
||||
OK http://sonarr.enkicsifelhom.hu/r1033-check?q=1 -> 301 https://sonarr.enkicsifelhom.hu/r1033-check?q=1
|
||||
OK http://torrent.enkicsifelhom.hu/r1033-check?q=1 -> 301 https://torrent.enkicsifelhom.hu/r1033-check?q=1
|
||||
OK http://transmission.enkicsifelhom.hu/r1033-check?q=1 -> 301 https://transmission.enkicsifelhom.hu/r1033-check?q=1
|
||||
OK http://wiki.enkicsifelhom.hu/r1033-check?q=1 -> 301 https://wiki.enkicsifelhom.hu/r1033-check?q=1
|
||||
== demo-hp (enkisfelhom.hu): 14 public name(s)
|
||||
OK http://arcade.enkisfelhom.hu/r1033-check?q=1 -> 301 https://arcade.enkisfelhom.hu/r1033-check?q=1
|
||||
OK http://books.enkisfelhom.hu/r1033-check?q=1 -> 301 https://books.enkisfelhom.hu/r1033-check?q=1
|
||||
OK http://docs.enkisfelhom.hu/r1033-check?q=1 -> 301 https://docs.enkisfelhom.hu/r1033-check?q=1
|
||||
OK http://felhom.enkisfelhom.hu/r1033-check?q=1 -> 301 https://felhom.enkisfelhom.hu/r1033-check?q=1
|
||||
OK http://files.enkisfelhom.hu/r1033-check?q=1 -> 301 https://files.enkisfelhom.hu/r1033-check?q=1
|
||||
OK http://gist.enkisfelhom.hu/r1033-check?q=1 -> 301 https://gist.enkisfelhom.hu/r1033-check?q=1
|
||||
OK http://ha.enkisfelhom.hu/r1033-check?q=1 -> 301 https://ha.enkisfelhom.hu/r1033-check?q=1
|
||||
OK http://no-such-name-r1033.enkisfelhom.hu/r1033-check?q=1 -> 301 https://no-such-name-r1033.enkisfelhom.hu/r1033-check?q=1
|
||||
OK http://papir.enkisfelhom.hu/r1033-check?q=1 -> 301 https://papir.enkisfelhom.hu/r1033-check?q=1
|
||||
OK http://paste.enkisfelhom.hu/r1033-check?q=1 -> 301 https://paste.enkisfelhom.hu/r1033-check?q=1
|
||||
OK http://pdf.enkisfelhom.hu/r1033-check?q=1 -> 301 https://pdf.enkisfelhom.hu/r1033-check?q=1
|
||||
OK http://plex.enkisfelhom.hu/r1033-check?q=1 -> 301 https://plex.enkisfelhom.hu/r1033-check?q=1
|
||||
OK http://travel.enkisfelhom.hu/r1033-check?q=1 -> 301 https://travel.enkisfelhom.hu/r1033-check?q=1
|
||||
OK http://wiki.enkisfelhom.hu/r1033-check?q=1 -> 301 https://wiki.enkisfelhom.hu/r1033-check?q=1
|
||||
== demo-felhom (demo-felhom.eu): 8 public name(s)
|
||||
OK http://felhom.demo-felhom.eu/r1033-check?q=1 -> 301 https://felhom.demo-felhom.eu/r1033-check?q=1
|
||||
OK http://files.demo-felhom.eu/r1033-check?q=1 -> 301 https://files.demo-felhom.eu/r1033-check?q=1
|
||||
OK http://gist.demo-felhom.eu/r1033-check?q=1 -> 301 https://gist.demo-felhom.eu/r1033-check?q=1
|
||||
OK http://no-such-name-r1033.demo-felhom.eu/r1033-check?q=1 -> 301 https://no-such-name-r1033.demo-felhom.eu/r1033-check?q=1
|
||||
OK http://prowlarr.demo-felhom.eu/r1033-check?q=1 -> 301 https://prowlarr.demo-felhom.eu/r1033-check?q=1
|
||||
OK http://radarr.demo-felhom.eu/r1033-check?q=1 -> 301 https://radarr.demo-felhom.eu/r1033-check?q=1
|
||||
OK http://sonarr.demo-felhom.eu/r1033-check?q=1 -> 301 https://sonarr.demo-felhom.eu/r1033-check?q=1
|
||||
OK http://torrent.demo-felhom.eu/r1033-check?q=1 -> 301 https://torrent.demo-felhom.eu/r1033-check?q=1
|
||||
== every public name redirects http -> https
|
||||
@@ -0,0 +1,3 @@
|
||||
('min_controller_version', '0.320.0', '2026-10-16 13:48:34')
|
||||
('min_controller_version_declared_min_agent', '0.320.0=0.158.1', '2026-10-16 13:48:34')
|
||||
customers: no such table: customers
|
||||
@@ -0,0 +1,145 @@
|
||||
#!/usr/bin/env python3
|
||||
"""v0.320.0 logins & folders browser check. Minimal CDP client: log in to the controller through its PUBLIC address in headless Chrome, open each
|
||||
app-page tab, assert computed styles, collect console errors, save screenshots. The password is read
|
||||
from a 0600 file and only ever sent inside a CDP message; it is never printed."""
|
||||
import base64, json, os, socket, struct, subprocess, sys, time, urllib.request
|
||||
|
||||
CHROME = os.path.expanduser('~/.cache/ms-playwright/chromium_headless_shell-1243/chrome-headless-shell-linux64/chrome-headless-shell')
|
||||
BASE = sys.argv[1] # https://felhom.enkisfelhom.hu
|
||||
PWFILE = sys.argv[2] # the controller password (0600 file)
|
||||
FBPWFILE = sys.argv[3] # FileBrowser's admin password (0600 file) — for the cold „Fajlok" click
|
||||
OUT = sys.argv[4]
|
||||
PORT = 9333
|
||||
os.makedirs(OUT, exist_ok=True)
|
||||
prof = os.path.join(OUT, '.profile')
|
||||
proc = subprocess.Popen([CHROME, f'--remote-debugging-port={PORT}', '--no-sandbox', f'--user-data-dir={prof}',
|
||||
'--window-size=1280,900', 'about:blank'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||
try:
|
||||
for _ in range(50):
|
||||
try:
|
||||
tabs = json.load(urllib.request.urlopen(f'http://127.0.0.1:{PORT}/json'))
|
||||
break
|
||||
except Exception:
|
||||
time.sleep(0.2)
|
||||
ws_url = [t for t in tabs if t['type'] == 'page'][0]['webSocketDebuggerUrl']
|
||||
host, path = ws_url[len('ws://'):].split('/', 1)
|
||||
h, p = host.split(':')
|
||||
sock = socket.create_connection((h, int(p)))
|
||||
key = base64.b64encode(os.urandom(16)).decode()
|
||||
sock.send(f'GET /{path} HTTP/1.1\r\nHost: {host}\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Key: {key}\r\nSec-WebSocket-Version: 13\r\n\r\n'.encode())
|
||||
buf = b''
|
||||
while b'\r\n\r\n' not in buf:
|
||||
buf += sock.recv(4096)
|
||||
st = {'pending': buf.split(b'\r\n\r\n', 1)[1]}
|
||||
|
||||
def send(obj):
|
||||
data = json.dumps(obj).encode()
|
||||
hdr = bytearray([0x81])
|
||||
n = len(data)
|
||||
if n < 126: hdr.append(0x80 | n)
|
||||
elif n < 65536: hdr.append(0x80 | 126); hdr += struct.pack('>H', n)
|
||||
else: hdr.append(0x80 | 127); hdr += struct.pack('>Q', n)
|
||||
mask = os.urandom(4); hdr += mask
|
||||
sock.sendall(bytes(hdr) + bytes(b ^ mask[i % 4] for i, b in enumerate(data)))
|
||||
|
||||
def recv_exact(n):
|
||||
while len(st['pending']) < n:
|
||||
st['pending'] += sock.recv(1 << 20)
|
||||
out, st['pending'] = st['pending'][:n], st['pending'][n:]
|
||||
return out
|
||||
|
||||
def recv():
|
||||
msg = b''
|
||||
while True:
|
||||
b0, b1 = recv_exact(2)
|
||||
n = b1 & 0x7f
|
||||
if n == 126: n = struct.unpack('>H', recv_exact(2))[0]
|
||||
elif n == 127: n = struct.unpack('>Q', recv_exact(8))[0]
|
||||
msg += recv_exact(n)
|
||||
if b0 & 0x80: return json.loads(msg)
|
||||
|
||||
console = []
|
||||
mid = [0]
|
||||
def call(method, **params):
|
||||
mid[0] += 1; i = mid[0]
|
||||
send({'id': i, 'method': method, 'params': params})
|
||||
while True:
|
||||
m = recv()
|
||||
if m.get('method') == 'Runtime.exceptionThrown':
|
||||
console.append(('exception', m['params']['exceptionDetails'].get('text', ''), m['params']['exceptionDetails'].get('exception', {}).get('description', '')[:200]))
|
||||
elif m.get('method') == 'Runtime.consoleAPICalled' and m['params']['type'] in ('error', 'warning'):
|
||||
console.append((m['params']['type'], ' '.join(str(a.get('value', a.get('description', ''))) for a in m['params']['args'])[:200]))
|
||||
elif m.get('method') == 'Log.entryAdded' and m['params']['entry']['level'] in ('error', 'warning'):
|
||||
e = m['params']['entry']; console.append(('log-' + e['level'], e.get('text', '')[:200], e.get('url', '')))
|
||||
if m.get('id') == i:
|
||||
if 'error' in m: raise RuntimeError(f'{method}: {m["error"]}')
|
||||
return m.get('result', {})
|
||||
|
||||
def ev(expr):
|
||||
r = call('Runtime.evaluate', expression=expr, returnByValue=True, awaitPromise=True)
|
||||
return r.get('result', {}).get('value')
|
||||
|
||||
def goto(url):
|
||||
call('Page.navigate', url=url)
|
||||
for _ in range(100):
|
||||
time.sleep(0.2)
|
||||
if ev('document.readyState') == 'complete':
|
||||
break
|
||||
time.sleep(1.5) # images' onerror, layout
|
||||
|
||||
for d in ('Page', 'Runtime', 'Log', 'Network'):
|
||||
call(d + '.enable')
|
||||
pw = open(PWFILE).read().strip()
|
||||
goto(BASE + '/login')
|
||||
ev("(function(){var f=document.querySelector('input[type=password]'); f.value=%s; f.form.submit(); return 1})()" % json.dumps(pw))
|
||||
del pw
|
||||
time.sleep(3)
|
||||
print('after login url:', ev('location.pathname'))
|
||||
console.clear() # the login page is not under test
|
||||
|
||||
def shot(name):
|
||||
s = call('Page.captureScreenshot', format='png', captureBeyondViewport=True)
|
||||
open(os.path.join(OUT, name + '.png'), 'wb').write(base64.b64decode(s['data']))
|
||||
|
||||
report = {}
|
||||
MARK = """(function(){ function all(sel,attr){return [].map.call(document.querySelectorAll(sel),function(e){return e.getAttribute(attr)})}
|
||||
return {path: location.pathname, title: (document.querySelector('.app-hero-name')||{}).textContent,
|
||||
tabs: all('[data-tab]','data-tab'), install: !!document.querySelector('[data-install-cta]'),
|
||||
loginAnswer: all('[data-login-answer]','data-login-answer'), loginKind: all('[data-login-kind]','data-login-kind'),
|
||||
fbOperator: !!document.querySelector('[data-fb-operator]'), fbReset: !!document.querySelector('[data-fb-reset]'),
|
||||
reveal: !!document.querySelector('#initcred-reveal'), open: (document.querySelector('[data-app-open]')||{}).href || null,
|
||||
files: (document.querySelector('[data-app-files]')||{}).href || null,
|
||||
folders: [].map.call(document.querySelectorAll('.datapath-row'), function(r){var a=r.querySelector('a'); return (a?a.href:'TEXT:'+r.textContent.trim().slice(0,60))}),
|
||||
fbNote: !!document.querySelector('[data-fb-login-note]'), scrollWidth: document.documentElement.scrollWidth};})()"""
|
||||
for p in ['/apps/filebrowser', '/apps/filebrowser/guide', '/apps/romm', '/apps/romm/data', '/apps/calibre-web',
|
||||
'/apps/calibre-web/data', '/apps/plex', '/apps/plex/data']:
|
||||
n0 = len(console)
|
||||
goto(BASE + p)
|
||||
r = ev(MARK); r['console'] = console[n0:]
|
||||
report[p] = r
|
||||
shot(p.strip('/').replace('/', '-'))
|
||||
# The cold „Fajlok" click: this browser has NO FileBrowser session. Follow each app's button target.
|
||||
fbpw = open(FBPWFILE).read().strip()
|
||||
for slug in ['romm', 'calibre-web', 'plex']:
|
||||
target = report['/apps/' + slug].get('files')
|
||||
if not target:
|
||||
report['cold:' + slug] = 'no Files button'
|
||||
continue
|
||||
call('Network.clearBrowserCookies') # cold: no FileBrowser session (the controller one is re-made below)
|
||||
goto(target)
|
||||
first = ev('location.pathname + location.search')
|
||||
shot('cold-' + slug + '-1-login')
|
||||
ev("""(function(pw){var u=document.querySelector('input[type=text],input[name=username],input#username');
|
||||
var p=document.querySelector('input[type=password]'); if(!u||!p) return 0;
|
||||
function set(el,v){var d=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value'); d.set.call(el,v); el.dispatchEvent(new Event('input',{bubbles:true}));}
|
||||
set(u,'admin'); set(p,pw); var b=document.querySelector('form button[type=submit], form input[type=submit], button.button--blue');
|
||||
if(b){b.click()} else {p.form && p.form.requestSubmit && p.form.requestSubmit()} return 1})(%s)""" % json.dumps(fbpw))
|
||||
time.sleep(4)
|
||||
after = ev('location.pathname')
|
||||
shot('cold-' + slug + '-2-after-login')
|
||||
report['cold:' + slug] = {'target': target, 'first': first, 'after_login': after}
|
||||
del fbpw
|
||||
print(json.dumps(report, indent=1, ensure_ascii=False))
|
||||
finally:
|
||||
proc.terminate()
|
||||
subprocess.run(['rm', '-rf', prof])
|
||||
@@ -124,7 +124,7 @@ stopping line that lies.
|
||||
| **R-494** | Install & onboarding | P4 | **NARROWED 2026-09-14 by operator ruling → [P3-LOW] the hub COULD create the tunnel at customer creation, for a domain already on Cloudflare. Not blocking: every customer has their own domain and the operator creates the tunnel per day-0 A.1 (`architecture/01-topology-and-trust.md`).** *Original finding, kept:* **[P1-HIGH] A new customer's dashboard has NO reachable address unless the operator hand-makes a Cloudflare tunnel — the link in the setup-code mail is dead.** MEASURED 2026-09-14 on a fresh install from the public ISO (drill intervention **I1**): the claim mail points at `https://felhom.drill0242.felhom.eu`; that name has **no A and no AAAA** record (`dig @1.1.1.1`, control `felhom.enkisfelhom.hu` resolves); the hub has **no tunnel- or DNS-creation code** (`hub/internal/cloudflare/` holds only geo-rule removal; `cf_tunnel_token` is a pasted, optional form field, `configs.go:1478`) — day-0 runbook A.1 makes it a manual Cloudflare-dashboard step that nothing on the customer-create page asks for; the box's own split-horizon resolver on the appliance LAN IP answered `google.com` but not the dashboard name at 13:27:39Z; the agent applied the record at **13:27:44Z** (`lanresolver: applied split-horizon record … ip=192.168.0.158`, 3 m 46 s after the controller started), so the box CAN answer the name — **but only to a device that uses the box as its DNS server, and no document, screen or mail tells a household to do that**; the router and the installer-offered DNS answer nothing. The page was reachable only at the guest's LAN address with the name forced (`curl --resolve …:443:192.168.0.158`). **A volunteer could not have done that.** **What it needs:** an operator ruling — the hub creates the tunnel and DNS at customer creation, or the product gives a household a LAN address that works with no DNS change. | **READY — rank P3-LOW; owner: CC** **Re-ranked 2026-10-03: P3→P4: operator-side automation; the operator creates the tunnel by hand per the day-0 runbook.** | — | — | CC |
|
||||
| **R-504** | Install & onboarding | P4 | **[P3-LOW] `iso.felhom.eu` cannot show an index page on its own — its root returns 404, and the download page lives on the website instead.** MEASURED 2026-09-14: `https://iso.felhom.eu/` and `/index.html` → 404; only named objects answer. The host is an R2 bucket behind a custom domain; whether R2 would serve an uploaded `index.html` at `/` was **not measured** (uploading anything to the public bucket is a publication). The ISO v1.27.0 task puts the Hungarian download page at `felhom.eu/letoltes` (published with the ISO, after the operator's yes). **Remaining:** a redirect from `iso.felhom.eu/` to that page needs a Cloudflare rule the session has no credential for. | **WAITING-ON-OPERATOR — rank P3-LOW; owner: operator (Cloudflare rule)** **Re-ranked 2026-10-03: P3→P4: households are sent to the website's download page; the bare address is cosmetic.** | — | — | operator |
|
||||
|
||||
## Apps & catalog — 22 rows (P2 1, P3 12, P4 9)
|
||||
## Apps & catalog — 25 rows (P2 1, P3 14, P4 10)
|
||||
|
||||
| ID | Category | Sev | What | State | Blocked on | Next action | Owner |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
@@ -148,8 +148,11 @@ stopping line that lies.
|
||||
| **R-1043** | Apps & catalog | P4 | **Sonarr, Radarr and Prowlarr build their login redirect as an absolute `http://` URL.** They trust forwarded headers only from their „Trusted networks” (the household set `192.168.0.0/24`; traefik is `172.18.x`), so they ignore `X-Forwarded-Proto: https` and answer `Location: http://<name>/login` to an https visitor. Since controller v0.312.0 that hop is sent back to https by traefik (a GET, no password), so it is cosmetic; a template could seed traefik's network as trusted. (`audits/https-only-2026-10-14/``1A-3-4.txt`) | **OPEN** — filed 2026-10-14 | — | Seed the docker network as a trusted proxy in the *arr templates (with the R-1030 seed), measure the redirect | CC |
|
||||
| **R-1095** | Apps & catalog | P4 | **`/apps/{slug}/install` still opens for an app that is INSTALLED, and shows the install form read-only.** Found 2026-10-16 (app page tuning, controller v0.319.0): the settings tab stopped rendering the install form, but `appInstallPageHandler` renders `deploy` for any app, so a typed or stale `/install` address shows „<app> — Telepítés" with disabled fields and no button. Its secret guards hold (R-254/R-709 tests; parity cases `deploy_installed*`). | OPEN | the task forbade route changes | a 302 from `/apps/{slug}/install` to `/apps/{slug}/settings` for an installed app (the `redirectStackDeployGET` shape); then the deployed-only branches of `app_fields.html` can go | CC |
|
||||
| **R-1096** | Apps & catalog | P3 | **After a disk-full, an app's damaged Redis file keeps the app down for good, and nothing says what to do.** FOUND 2026-10-16 on demo-hp 9201: the standing RomM (installed 2026-08-21) was stopped; the dashboard's Start re-created it, `romm-redis` crash-looped (unhealthy), the box stopped it again (`crash_loop`, `09` §3 decision 28) and mailed `app_start_failed`. Its Redis volume dates from the 2026-09-24 thin-pool fill (R-672): Docmost's damaged append-only file was repaired by hand that day with `redis-check-aof --fix` (`audits/r672-2026-09-24/B7-redis-aof-fix.txt`); RomM's was not (the copy `romm_romm_redis_data.pre-aof-fix-20260924T135937Z` is still on the box). The Redis log was not read: the box removed the containers first. That RomM was then removed by operator ruling (`audits/romm-ghost-2026-10-16/A1/`). | OPEN — filed 2026-10-16 | — | List the catalog apps whose Redis runs with `--appendonly yes` (romm does); for each, decide whether its Redis needs persistence at all; reproduce on 9202 by damaging an append-only file, and make the box repair a copy or say what to do | CC |
|
||||
| **R-1100** | Apps & catalog | P3 | **One sign-in for the dashboard and FileBrowser (proxy auth) — spike first.** Since controller 0.320.0 every app with a folder has „Fájlok ↗", and the box can show FileBrowser's password — but FileBrowser still asks for its OWN login the first time, so the household signs in twice. FileBrowser Quantum has a proxy-auth method (`auth.methods.proxyAuth`, header-trusted) that could take the dashboard session through traefik; nobody has measured it. Design: `architecture/02-controller-module-map.md` „Logins and folders". | OPEN — filed 2026-10-16 (task logins-folders). | — | Spike on demo-hp: proxy auth at the pinned tag (source + measured), a forwardAuth in traefik that answers only for a dashboard session, what a stranger reaching `files.<domain>` with a forged header gets, and what happens to FileBrowser's shares. | CC |
|
||||
| **R-1102** | Apps & catalog | P3 | **Eight apps say „starts with a known, shared password" about a login that is generated, set at install, or made on the first visit.** `app_info.default_creds` with no `after_install` keeps the known-login sentence in effect (`web/known_login.go`), so the install dialog and the app page say „Ez az alkalmazás egy ismert, közös jelszóval indul: <default_creds>. Telepítés után azonnal változtasd meg." for grafana, gokapi, kimai, code-server, nzbget, nextcloud, transmission (a password the box generated or the household typed, shown in the login panel) and prowlarr („Az első belépéskor te hozod létre"). Found 2026-10-16 while adding the login panel's answers. | OPEN | — | Catalog: drop `default_creds` on these eight (hu + `i18n.en`, `check-copy-i18n.py --capture-freeze`) now that the login panel says where the login is — or keep the hint and give the controller a „hint, not a known password" form. An operator word on which. | CC |
|
||||
| **R-1097** | Apps & catalog | P3 | **RomM keeps the household's saves, save states and screenshots in an anonymous Docker volume, outside the backup.** FOUND 2026-10-16 from RomM 5.3.1's source and the running container: `ASSETS_BASE_PATH = /romm/assets` (`backend/config/__init__.py:39`); the image declares `VOLUME /romm`; the compose mounts nothing at `/romm/assets`, and `docker inspect` on 9201 showed an anonymous volume at `/romm`. Compose keeps an anonymous volume across a recreate, so an update does not lose it; a remove + reinstall or a restore does, and the app backup (named volumes + drive folders) probably does not hold it — NOT measured. Moving it to a named volume or the drive would hide what a box already has, so a fix needs a one-time copy. | OPEN — filed 2026-10-16 | — | Measure on 9202: a save in a browser game, back up, restore, read the save; then a named `romm_assets` volume with a one-time copy from the anonymous one | CC |
|
||||
| **R-1099** | Apps & catalog | P4 | **The N100 may hold RomM games in the old upload place, now shown under a fake „roms” platform.** Until 2026-10-16 RomM's own upload wrote to the drive's `roms/roms/<platform>/`; after the mapping fix (catalog `fb18471`, R-1089) those files are at RomM's `library/roms/roms/…`. Measured on 9201: such a game showed as missing in its platform and again under a platform „roms” (`audits/romm-ghost-2026-10-16/B/b-after.txt`). Files put in with the file manager are not affected — they are now found. The N100 runs RomM (hub `/apps/romm`); CC does not log in to the lived-in box. | OPEN — filed 2026-10-16 | Operator: look at `roms/roms/` on the N100's drive in the file manager | If it holds platform folders: move them up one level into `roms/`, then run „Scan” in RomM; if it is empty, close | Operator |
|
||||
| **R-1101** | Apps & catalog | P4 | **FileBrowser's stored password goes stale when the household changes it in FileBrowser — and the page tells them to.** In the `generated` state the panel shows „Az első bejelentkezés után azonnal változtasd meg!" under FileBrowser's login (R-513 copy). Changing it in FileBrowser leaves the box's copy wrong: „Megjelenítés" then shows a password that is refused, and the state stays `generated`, so „Új jelszó beállítása" is not offered. Found 2026-10-16. | OPEN | — | Either drop the „change it" line for FileBrowser, or have the reveal (or a tick) check the stored value against FileBrowser's login and fall back to `operator` when it is refused. | CC |
|
||||
|
||||
|
||||
## App updates — 4 rows (P3 4)
|
||||
|
||||