Apps burndown (controller 0.321.0 released): R-1097, R-1102, R-1101, R-1095, R-1098 closed; R-1103 opened; build-hub.sh 1.25.0
gates / gates (push) Successful in 6m52s
gates / gates (push) Successful in 6m52s
build-hub.sh refuses an existing tag and an unanswered registry (test_build_hub_push_once.sh, red-proved). Register 179 -> 175. R-1067 stays BLOCKED: Tester-2 reports 0.292.0. Design notes in 02 and 07; STATUS; evidence audits/burndown-apps-2026-10-16/; REPORT-burndown-apps-2026-10-16.md. Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
romm rommapp/romm:5.3.1 ['/redis-data', '/romm']
|
||||
privatebin privatebin/pdo:2.0.6 ['/run', '/tmp', '/var/lib/nginx/tmp']
|
||||
@@ -0,0 +1,7 @@
|
||||
customers reporting RomM telemetry, with their newest report (hub DB + WAL, read 2026-10-16):
|
||||
demo-felhom rows 1 newest 2026-10-13 09:37:46
|
||||
demo-hp rows 4450 newest 2026-10-16 16:32:25
|
||||
customers reporting ANY app in the last 2 hours:
|
||||
demo-felhom
|
||||
demo-hp
|
||||
tester-1
|
||||
@@ -0,0 +1,18 @@
|
||||
2026/10/16 13:47:23 main.go:814: [INFO] Self-update enabled (check every 6h, auto-update: false, auto-update time: 04:30)
|
||||
2026/10/16 14:51:36 night_chain.go:108: [INFO] [night-chain] manual run started from 172.18.0.4:40328: [db-dump tier2 offsite update-leg]
|
||||
2026/10/16 14:51:36 night_chain.go:84: [INFO] [night-chain] manual run of tonight's chain: dump → second drive → off-site → update leg
|
||||
2026/10/16 14:51:36 night_chain.go:77: [INFO] [night-chain] db-dump: started
|
||||
2026/10/16 14:53:42 night_chain.go:82: [INFO] [night-chain] db-dump: done in 2m6s
|
||||
2026/10/16 14:53:42 night_chain.go:77: [INFO] [night-chain] tier2: started
|
||||
2026/10/16 14:54:10 night_chain.go:82: [INFO] [night-chain] tier2: done in 28s
|
||||
2026/10/16 14:54:10 night_chain.go:77: [INFO] [night-chain] offsite: started
|
||||
2026/10/16 14:57:08 night_chain.go:82: [INFO] [night-chain] offsite: done in 2m58s
|
||||
2026/10/16 14:57:08 night_chain.go:77: [INFO] [night-chain] update-leg: started
|
||||
2026/10/16 14:57:08 unattended.go:270: [INFO] [update-leg] started (manual-chain): window 02:30, no step starts at or after 20:12
|
||||
2026/10/16 14:57:08 unattended.go:316: [INFO] [update-leg] kimai: skipped — held
|
||||
2026/10/16 14:57:08 unattended.go:248: [INFO] [update-leg] update leg (manual-chain): done=0 undone=0 held=0 failed=0 skipped=1 in 0s [skipped: kimai=held]
|
||||
2026/10/16 14:57:08 night_chain.go:82: [INFO] [night-chain] update-leg: done in 0s
|
||||
2026/10/16 14:57:08 night_chain.go:93: [INFO] [night-chain] finished in 5m32s
|
||||
romm Up About a minute (healthy)
|
||||
romm-redis Up About a minute (healthy)
|
||||
romm-db Up About a minute (healthy)
|
||||
@@ -0,0 +1,40 @@
|
||||
== mounts of the running romm container
|
||||
volume <ANONYMOUS 64-hex> -> /redis-data
|
||||
volume <ANONYMOUS 64-hex> -> /romm
|
||||
volume romm_romm_config -> /romm/config
|
||||
bind -> /romm/resources
|
||||
bind -> /romm/library/bios
|
||||
bind -> /romm/library/roms
|
||||
|
||||
== compose volumes
|
||||
167:volumes:
|
||||
168- romm_config:
|
||||
169- romm_db_data:
|
||||
170- romm_redis_data:
|
||||
171-
|
||||
172-networks:
|
||||
173- traefik-public:
|
||||
18:# Cover art etc → ${HDD_PATH}/appdata/romm/resources (app-internal, NOT browsable)
|
||||
29: image: rommapp/romm:5.3.1@sha256:0d66b4ea152a237c7f24b95e87459f46d3c596a7c0b35a82f26bc971487278b3
|
||||
40: if [ ! -f /romm/config/config.yml ]; then
|
||||
42: cat > /romm/config/config.yml << 'CONF'
|
||||
80: # on demo-hp 9201 (R-1089, felhom.eu audits/romm-ghost-2026-10-16/A1). Now roms/<platform>/ IS
|
||||
83: - ${USERDATA_PATH}/roms:/romm/library/roms
|
||||
84: - ${USERDATA_PATH}/roms-bios:/romm/library/bios
|
||||
85: - ${HDD_PATH}/appdata/romm/resources:/romm/resources
|
||||
86: - romm_config:/romm/config
|
||||
== what earlier app backups of romm hold
|
||||
/mnt/felhom-drives/hdd_1/kept/romm/2026-10-16_113539/unit/volume-dumps
|
||||
total 203084
|
||||
drwxr-xr-x 2 root root 4096 Oct 4 05:35 .
|
||||
drwxr-xr-x 5 root root 4096 Oct 4 11:18 ..
|
||||
-rw-r--r-- 1 root root 2560 Oct 4 05:35 romm_romm_config.tar
|
||||
-rw-r--r-- 1 root root 163786752 Oct 4 05:35 romm_romm_db_data.tar
|
||||
-rw-r--r-- 1 root root 44148736 Oct 4 05:35 romm_romm_redis_data.tar
|
||||
== anonymous volume contents
|
||||
assets
|
||||
config
|
||||
library
|
||||
resources
|
||||
/var/lib/docker/volumes/567f3e5915421b542fc2e67d3195d03b32ef07ef79f2851c6198653cf29e00f6/_data/assets
|
||||
28K /var/lib/docker/volumes/567f3e5915421b542fc2e67d3195d03b32ef07ef79f2851c6198653cf29e00f6/_data
|
||||
@@ -0,0 +1,49 @@
|
||||
== 1. sync the catalog
|
||||
sync -> 200 {'ok': True, 'data': {'ok': True, 'updated': ['code-server', 'gokapi', 'grafana', 'kimai', 'nextcloud', 'nzbget', 'prowlarr', 'romm', 'transmission'], 'message': 'Sablonok frissítve — frissítve: code-
|
||||
applied compose names romm_assets: 3
|
||||
== 2. restart RomM (Újraindítás)
|
||||
restart -> 200 {'ok': True, 'data': {'state': 'starting'}, 'message': 'Stack romm restart requested — state now: starting'}
|
||||
state: running
|
||||
mounts after restart:
|
||||
volume <ANONYMOUS> -> /redis-data
|
||||
volume <ANONYMOUS> -> /romm
|
||||
volume romm_romm_assets -> /romm/assets
|
||||
volume romm_romm_config -> /romm/config
|
||||
bind -> /romm/resources
|
||||
bind -> /romm/library/bios
|
||||
bind -> /romm/library/roms
|
||||
== 3. a save where RomM writes saves (dummy bytes, as the app's own user)
|
||||
drwxr-xr-x 3 root root 4096 Oct 16 16:51 ..
|
||||
-rw-r--r-- 1 root root 32768 Oct 16 16:51 r1097-proof.sav
|
||||
4a55d00c3ee6bccd55649e48fd19d84703f755f84474ac0b6a41299b114ee6a5 /romm/assets/romm/users/1/saves/gba/r1097-proof/r1097-proof.sav
|
||||
== 4. the by-day backup (night chain)
|
||||
night-chain -> 202 {'data': {'legs': ['db-dump', 'tier2', 'offsite', 'update-leg']}, 'message': 'started', 'ok': True}
|
||||
night chain last line: 2026/10/16 14:57:08 night_chain.go:93: [INFO] [night-chain] finished in 5m32s after 342s
|
||||
romm volume dumps now: /mnt/felhom-drives/hdd_1/backups/primary/romm/volume-dumps/:
|
||||
total 176584
|
||||
drwxr-xr-x 2 root root 4096 Oct 16 14:56 .
|
||||
drwxr-xr-x 5 root root 4096 Oct 16 14:56 ..
|
||||
-rw-r--r-- 1 root root 37888 Oct 16 14:56 romm_romm_assets.tar
|
||||
-rw-r--r-- 1 root root 2560 Oct 16 14:56 romm_romm_config.tar
|
||||
-rw-r--r-- 1 root root 162300416 Oct 16 14:56 romm_romm_db_data.tar
|
||||
-rw-r--r-- 1 root root 18463744 Oct 16 14:56 romm_romm_redis_data.tar
|
||||
/mnt/felhom-drives/hdd_1/backups/primary/romm/volume-dumps/romm_romm_assets.tar
|
||||
|
||||
== 5. remove RomM, KEEPING its backups
|
||||
remove -> 200 {'ok': True, 'data': {'removed': 'romm', 'volumes_removed': ['romm_romm_assets', 'romm_romm_config', 'romm_romm_db_data', 'romm_romm_redis_data'], 'hdd_paths_removed': [], 'hdd_paths_preserved': ['/mn
|
||||
volumes left: romm_romm_redis_data.pre-aof-fix-20260924T135937Z
|
||||
== 6. restore from the backups page
|
||||
restore: {'ok': True, 'snapshot_id': 'helyi', 'http': 'HTTP/2 302', 'seconds': 77.0}
|
||||
state: running
|
||||
mounts after restore:
|
||||
volume <ANONYMOUS> -> /redis-data
|
||||
volume <ANONYMOUS> -> /romm
|
||||
volume romm_romm_assets -> /romm/assets
|
||||
volume romm_romm_config -> /romm/config
|
||||
bind -> /romm/resources
|
||||
bind -> /romm/library/bios
|
||||
bind -> /romm/library/roms
|
||||
drwxr-xr-x 3 root root 4096 Oct 16 16:58 ..
|
||||
-rw-r--r-- 1 root root 32768 Oct 16 16:51 r1097-proof.sav
|
||||
4a55d00c3ee6bccd55649e48fd19d84703f755f84474ac0b6a41299b114ee6a5 /romm/assets/romm/users/1/saves/gba/r1097-proof/r1097-proof.sav
|
||||
== VERDICT: the save came back byte-identical: True
|
||||
@@ -0,0 +1,6 @@
|
||||
tables with customer: ['customer_notifications', 'customer_configs', 'customer_claims', 'customer_resets']
|
||||
every customer's NEWEST report (hub DB + WAL):
|
||||
demo-hp 0.320.0 2026-10-16 14:58:09
|
||||
tester-1 0.320.0 2026-10-16 14:49:33
|
||||
demo-felhom 0.320.0 2026-10-16 14:49:28
|
||||
Tester-2 0.292.0 2026-10-04 18:06:24
|
||||
BIN
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 186 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 98 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 72 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 44 KiB |
@@ -0,0 +1,60 @@
|
||||
rm: cannot remove '/mnt/5_hdd/felhom.eu/git/felhom.eu/documentation/audits/burndown-apps-2026-10-16/browser/.profile/Default': Directory not empty
|
||||
after login url: /launcher
|
||||
{
|
||||
"/apps/calibre-web/data": {
|
||||
"path": "/apps/calibre-web/data",
|
||||
"backupSections": 1,
|
||||
"rows": [
|
||||
"nightly",
|
||||
"second",
|
||||
"target"
|
||||
],
|
||||
"offDrivePBS": false,
|
||||
"valueFont": "\"Plus Jakarta Sans\"",
|
||||
"exportToggle": [
|
||||
"Megjelenítés",
|
||||
"Jelszó mutatása"
|
||||
],
|
||||
"fbReveal": false,
|
||||
"fbStale": false,
|
||||
"changeItLine": false,
|
||||
"scrollWidth": 1270,
|
||||
"console": []
|
||||
},
|
||||
"/apps/filebrowser": {
|
||||
"path": "/apps/filebrowser",
|
||||
"backupSections": 0,
|
||||
"rows": [],
|
||||
"offDrivePBS": false,
|
||||
"valueFont": null,
|
||||
"exportToggle": null,
|
||||
"fbReveal": true,
|
||||
"fbStale": false,
|
||||
"changeItLine": false,
|
||||
"scrollWidth": 1280,
|
||||
"console": []
|
||||
},
|
||||
"/apps/calibre-web/install": {
|
||||
"path": "/apps/calibre-web/settings",
|
||||
"backupSections": 0,
|
||||
"rows": [],
|
||||
"offDrivePBS": false,
|
||||
"valueFont": null,
|
||||
"exportToggle": null,
|
||||
"fbReveal": false,
|
||||
"fbStale": false,
|
||||
"changeItLine": false,
|
||||
"scrollWidth": 1270,
|
||||
"console": []
|
||||
},
|
||||
"filebrowser": {
|
||||
"path": "/files/Beolvas%C3%A1s",
|
||||
"updateBanner": false,
|
||||
"installApp": false,
|
||||
"releaseBanners": 0,
|
||||
"globals": {
|
||||
"disablePWAInstall": true,
|
||||
"updateAvailable": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
control: bookstack (keeps default_creds):
|
||||
302 known-login markers: 0
|
||||
grafana 200 known-login=0 answer=
|
||||
grafana/install 200 known-login=0 answer=
|
||||
gokapi 200 known-login=0 answer=
|
||||
gokapi/install 200 known-login=0 answer=
|
||||
kimai 200 known-login=0 answer=data-login-answer="fields"
|
||||
kimai/install 302 known-login=0 answer=
|
||||
code-server 200 known-login=0 answer=
|
||||
code-server/install 200 known-login=0 answer=
|
||||
nzbget 200 known-login=0 answer=
|
||||
nzbget/install 200 known-login=0 answer=
|
||||
nextcloud 200 known-login=0 answer=
|
||||
nextcloud/install 200 known-login=0 answer=
|
||||
transmission 200 known-login=0 answer=
|
||||
transmission/install 200 known-login=0 answer=
|
||||
prowlarr 200 known-login=0 answer=
|
||||
prowlarr/install 200 known-login=0 answer=
|
||||
control bookstack: 200 known-login=0 alapertelmezett=0
|
||||
control wger: 200 known-login=0 alapertelmezett=0
|
||||
control mealie: 200 known-login=0 alapertelmezett=0
|
||||
control claper: 200 known-login=0 alapertelmezett=0
|
||||
@@ -0,0 +1,3 @@
|
||||
FAIL unreachable (000) -> refused (exit 1)
|
||||
FAIL server error (500) -> refused (exit 1)
|
||||
2 of 5 cases FAILED
|
||||
@@ -0,0 +1,4 @@
|
||||
XX DECOY: the PARENT /romm mounted rc=0 (expected 1)
|
||||
XX DECOY: a CHILD /romm/assets/x mounted rc=0 (expected 1)
|
||||
AssertionError: Lists differ: [' default_creds: "admin / x"\n'] != []
|
||||
FAILED (failures=1)
|
||||
@@ -0,0 +1,26 @@
|
||||
## refused = any non-200 (FileBrowser down reads as refused)
|
||||
=== RUN TestFileBrowserStoredPassword_RefusedOnlyOnA401
|
||||
filebrowser_reset_test.go:182: FileBrowser down reads as refused — an unproven refusal must not tell the household its password changed
|
||||
--- FAIL: TestFileBrowserStoredPassword_RefusedOnlyOnA401 (0.00s)
|
||||
## no stored-password check on the page
|
||||
=== RUN TestFileBrowserLogin_StoredPasswordRefusedOffersReset
|
||||
logins_folders_test.go:365: stale: the page still offers the reveal of a password FileBrowser refuses
|
||||
--- FAIL: TestFileBrowserLogin_StoredPasswordRefusedOffersReset (0.08s)
|
||||
## the change-it line back on FileBrowser
|
||||
=== RUN TestFileBrowserLogin_StoredPasswordRefusedOffersReset
|
||||
logins_folders_test.go:380: FileBrowser still says to change the password after the first login (R-1101)
|
||||
--- FAIL: TestFileBrowserLogin_StoredPasswordRefusedOffersReset (0.08s)
|
||||
## no redirect for an installed app's /install
|
||||
=== RUN TestInstallAddress_InstalledRedirectsToSettings
|
||||
logins_folders_test.go:432: installed: 200 → "", want 302 → /apps/calibre-web/settings
|
||||
--- FAIL: TestInstallAddress_InstalledRedirectsToSettings (0.08s)
|
||||
## off-drive back in a Data tab string (hu)
|
||||
=== RUN TestAppData_OneBackupSection
|
||||
## a second backup section
|
||||
=== RUN TestAppData_OneBackupSection
|
||||
logins_folders_test.go:408: hu: 2 backup sections, want 1
|
||||
logins_folders_test.go:408: en: 2 backup sections, want 1
|
||||
--- FAIL: TestAppData_OneBackupSection (0.10s)
|
||||
## (redo) off-drive/PBS back in the always-shown 2nd-copy text (hu)
|
||||
logins_folders_test.go:415: hu: off-drive/PBS on the Data tab ("off-drive")
|
||||
--- FAIL: TestAppData_OneBackupSection (0.09s)
|
||||
@@ -0,0 +1,6 @@
|
||||
FAIL --push, tag present -> refuses: exit 0, want 1
|
||||
FAIL --push, tag absent -> proceeds: no 'is not present in the registry'
|
||||
FAIL --multiarch, tag present -> refuses: exit 0, want 1
|
||||
FAIL --push, registry unreachable -> refuses: exit 0, want 1
|
||||
FAIL --push, auth failure -> refuses: exit 0, want 1
|
||||
5 of 6 cases FAILED
|
||||
@@ -0,0 +1,15 @@
|
||||
== tester-1
|
||||
unhealthy=0 restarting=0 exited=0 created=0
|
||||
controller gitea.dooplex.hu/admin/felhom-controller:0.321.0 Up 18 seconds (healthy)
|
||||
filebrowser Up 16 seconds (healthy)
|
||||
2
|
||||
== demo-hp
|
||||
unhealthy=0 restarting=0 exited=0 created=0
|
||||
controller gitea.dooplex.hu/admin/felhom-controller:0.321.0 Up 21 seconds (healthy)
|
||||
filebrowser Up 3 minutes (healthy)
|
||||
2
|
||||
== demo-felhom
|
||||
unhealthy=0 restarting=0 exited=0 created=0
|
||||
controller gitea.dooplex.hu/admin/felhom-controller:0.321.0 Up 25 seconds (healthy)
|
||||
filebrowser Up 23 seconds (healthy)
|
||||
2
|
||||
@@ -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 @@
|
||||
demo-hp Location: /customers/demo-hp?flash=floor_set
|
||||
tester-1 Location: /customers/tester-1?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 @@
|
||||
17:22:20
|
||||
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,7 @@
|
||||
FIRST READ (hub.db, -wal, -shm copied ONE BY ONE with kubectl exec cat) — STALE:
|
||||
('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')
|
||||
SECOND READ (one tar of the three files) — 17:23:
|
||||
('min_controller_version', '0.321.0', '2026-10-16 15:22:20')
|
||||
('min_controller_version_declared_min_agent', '0.321.0=0.158.1', '2026-10-16 15:22:20')
|
||||
THIRD CHANNEL — GET /configuration with CC's token: the page shows 0.321.0
|
||||
@@ -0,0 +1,138 @@
|
||||
#!/usr/bin/env python3
|
||||
"""v0.321.0 apps-burndown browser check (Data tab, FileBrowser page and FileBrowser itself, an install address). 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 = {}
|
||||
for p in ['/apps/calibre-web/data', '/apps/filebrowser', '/apps/calibre-web/install']:
|
||||
n0 = len(console)
|
||||
goto(BASE + p)
|
||||
report[p] = ev("""(function(){ return {path: location.pathname,
|
||||
backupSections: document.querySelectorAll('[data-backup-section]').length,
|
||||
rows: [].map.call(document.querySelectorAll('[data-backup-row]'), function(e){return e.getAttribute('data-backup-row')}),
|
||||
offDrivePBS: /off-drive|\\bPBS\\b/i.test(document.body.innerText),
|
||||
valueFont: (function(){var v=document.querySelector('[data-backup-row] .app-field-value'); return v?getComputedStyle(v).fontFamily.split(',')[0]:null})(),
|
||||
exportToggle: (function(){var b=document.getElementById('exportPwToggle'); return b?[b.textContent.trim(), b.getAttribute('aria-label')]:null})(),
|
||||
fbReveal: !!document.querySelector('#initcred-reveal'), fbStale: !!document.querySelector('[data-fb-stale]'),
|
||||
changeItLine: document.body.innerText.indexOf('hagyd figyelmen') >= 0,
|
||||
scrollWidth: document.documentElement.scrollWidth};})()""")
|
||||
report[p]['console'] = console[n0:]
|
||||
shot(p.strip('/').replace('/', '-'))
|
||||
# FileBrowser itself, logged in with the box's own password: no update banner, no install-app prompt.
|
||||
fbpw = open(FBPWFILE).read().strip()
|
||||
call('Network.clearBrowserCookies')
|
||||
goto(BASE.replace('://felhom.', '://files.') + '/')
|
||||
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))
|
||||
del fbpw
|
||||
time.sleep(5)
|
||||
report['filebrowser'] = ev("""(function(){ var t=document.body.innerText;
|
||||
return {path: location.pathname, updateBanner: /update is available/i.test(t), installApp: /install app/i.test(t),
|
||||
releaseBanners: document.querySelectorAll('.release-banner').length,
|
||||
globals: (function(){ try { var g=window.globalVars||{}; return {disablePWAInstall: g.disablePWAInstall, updateAvailable: g.updateAvailable}; } catch(e) { return null; } })()};})()""")
|
||||
shot('filebrowser-sidebar')
|
||||
print(json.dumps(report, indent=1, ensure_ascii=False))
|
||||
finally:
|
||||
proc.terminate()
|
||||
subprocess.run(['rm', '-rf', prof])
|
||||
@@ -0,0 +1,59 @@
|
||||
#!/usr/bin/env python3
|
||||
"""romm_saves.py — R-1097 proof on demo-hp 9201 through the product's endpoints (box_walk helpers, TARGET=9201).
|
||||
Sync the catalog, restart RomM (the applied compose gains romm_assets), write a save where RomM writes saves, run the
|
||||
by-day night chain (the sanctioned backup trigger), remove RomM KEEPING its backups, restore from the backups page,
|
||||
and read the save back. Env: SC (0600 scratch with .ctlpw), EV. The save is a dummy file; no secret is printed."""
|
||||
import os, sys, time, hashlib
|
||||
sys.path.insert(0, "/mnt/5_hdd/felhom.eu/git/app-catalog-felhom.eu/scripts")
|
||||
import box_walk as w
|
||||
APP = "romm"
|
||||
SAVE_DIR = "/romm/assets/romm/users/1/saves/gba/r1097-proof"
|
||||
SAVE = SAVE_DIR + "/r1097-proof.sav"
|
||||
log = open(os.path.join(w.EV, "romm-saves.txt"), "a", buffering=1)
|
||||
def say(*a):
|
||||
w.say(*a); log.write(" ".join(map(str, a)) + "\n")
|
||||
def mounts():
|
||||
return w.guest("docker inspect romm --format '{{range .Mounts}}{{.Type}} {{.Name}} -> {{.Destination}}{{println}}{{end}}' "
|
||||
"| sed -E 's/volume [0-9a-f]{64}/volume <ANONYMOUS>/'").strip()
|
||||
def save_state():
|
||||
return w.guest(f"docker exec romm sh -c 'ls -la {SAVE_DIR} 2>&1; sha256sum {SAVE} 2>&1' | tail -3").strip()
|
||||
|
||||
w.login()
|
||||
say("== 1. sync the catalog")
|
||||
code, d = w.ctl("POST", "/api/sync"); say("sync ->", code, str(d)[:200])
|
||||
say("applied compose names romm_assets:", w.guest("grep -c romm_assets /opt/docker/stacks/romm/docker-compose.yml").strip())
|
||||
say("== 2. restart RomM (Újraindítás)")
|
||||
code, d = w.ctl("POST", f"/api/stacks/{APP}/restart"); say("restart ->", code, str(d)[:160])
|
||||
for _ in range(40):
|
||||
time.sleep(5)
|
||||
if w.stack(APP).get("state") == "running":
|
||||
break
|
||||
say("state:", w.stack(APP).get("state")); say("mounts after restart:\n" + mounts())
|
||||
say("== 3. a save where RomM writes saves (dummy bytes, written with docker exec into the running container)")
|
||||
w.guest(f"docker exec romm sh -c 'mkdir -p {SAVE_DIR} && head -c 32768 /dev/urandom > {SAVE}'")
|
||||
before = save_state(); say(before)
|
||||
say("== 4. the by-day backup (night chain)")
|
||||
t0 = time.time(); code, d = w.ctl("POST", "/api/debug/backup/night-chain"); say("night-chain ->", code, str(d)[:160])
|
||||
for _ in range(180):
|
||||
time.sleep(10)
|
||||
tail = w.guest("docker logs --since 60m felhom-controller 2>&1 | grep 'night-chain' | tail -1").strip()
|
||||
if "finished in" in tail or "REFUSED" in tail:
|
||||
break
|
||||
say("night chain last line:", tail[-220:], f"after {round(time.time()-t0)}s")
|
||||
say("romm volume dumps now:", w.guest("ls -la /mnt/felhom-drives/hdd_1/backups/*/romm/volume-dumps/ /mnt/felhom-drives/hdd_1/*/romm/recovery-unit/volume-dumps/ 2>/dev/null; find /mnt/felhom-drives/hdd_1 -name 'romm_romm_assets.tar' -newer /proc/1 2>/dev/null | head -5"))
|
||||
say("== 5. remove RomM, KEEPING its backups")
|
||||
w.ctl("POST", f"/api/stacks/{APP}/stop"); time.sleep(10)
|
||||
code, d = w.ctl("POST", f"/api/stacks/{APP}/remove", {"remove_hdd_data": False, "remove_backups": False})
|
||||
say("remove ->", code, str(d)[:200])
|
||||
time.sleep(5)
|
||||
say("volumes left:", w.guest("docker volume ls --format '{{.Name}}' | grep -i romm").strip() or "(none)")
|
||||
say("== 6. restore from the backups page")
|
||||
r = w.restore(APP); say("restore:", {k: r.get(k) for k in ("ok", "snapshot_id", "http", "seconds")})
|
||||
for _ in range(60):
|
||||
time.sleep(5)
|
||||
if w.stack(APP).get("state") == "running":
|
||||
break
|
||||
say("state:", w.stack(APP).get("state")); say("mounts after restore:\n" + mounts())
|
||||
after = save_state(); say(after)
|
||||
same = before.split()[-2:] == after.split()[-2:]
|
||||
say("== VERDICT: the save came back byte-identical:", same)
|
||||
Reference in New Issue
Block a user