diff --git a/documentation/audits/rehearsal-2026-10-20/B3-apps/state-after-installs.txt b/documentation/audits/rehearsal-2026-10-20/B3-apps/state-after-installs.txt new file mode 100644 index 00000000..9278bada --- /dev/null +++ b/documentation/audits/rehearsal-2026-10-20/B3-apps/state-after-installs.txt @@ -0,0 +1,9 @@ +immich running deployed= True sub= photos gate= True +nextcloud running deployed= True sub= cloud gate= False +paperless-ngx running deployed= True sub= paperless gate= False +vaultwarden running deployed= True sub= vault gate= False +mealie running deployed= True sub= recipes gate= False +actualbudget running deployed= True sub= budget gate= True +radicale running deployed= True sub= calendar gate= False +bookstack running deployed= True sub= wiki gate= False +jellyfin not_deployed deployed= False sub= None gate= False diff --git a/documentation/audits/rehearsal-2026-10-20/B4-use/after-plug-watch.txt b/documentation/audits/rehearsal-2026-10-20/B4-use/after-plug-watch.txt new file mode 100644 index 00000000..7911216e --- /dev/null +++ b/documentation/audits/rehearsal-2026-10-20/B4-use/after-plug-watch.txt @@ -0,0 +1,40 @@ +14:13:55 diskread=3GB health=200 0.026323 +14:14:27 diskread=3GB health=200 0.010294 +14:14:59 diskread=4GB health=200 0.011968 +14:15:30 diskread=4GB health=200 0.009450 +14:16:02 diskread=4GB health=200 0.010128 +14:16:33 diskread=4GB health=200 0.009558 +14:17:05 diskread=4GB health=200 0.010571 +14:17:36 diskread=4GB health=200 0.009807 +14:18:08 diskread=4GB health=200 0.010191 +14:18:40 diskread=4GB health=200 0.009850 +14:19:11 diskread=4GB health=200 0.010203 +14:19:43 diskread=4GB health=200 0.011617 +14:20:14 diskread=4GB health=200 0.014898 +14:20:46 diskread=4GB health=200 0.010567 +14:21:17 diskread=4GB health=200 0.011040 +14:21:49 diskread=4GB health=200 0.009551 +14:22:21 diskread=4GB health=200 0.009957 +14:22:52 diskread=4GB health=200 0.010580 +14:23:24 diskread=4GB health=200 0.010163 +14:23:56 diskread=4GB health=200 0.010898 +14:24:27 diskread=4GB health=200 0.010501 +14:24:58 diskread=4GB health=200 0.011854 +14:25:30 diskread=4GB health=200 0.011567 +14:26:01 diskread=4GB health=200 0.010254 +14:26:32 diskread=4GB health=200 0.010620 +14:27:03 diskread=4GB health=200 0.011295 +14:27:35 diskread=4GB health=200 0.010245 +14:28:06 diskread=4GB health=200 0.010855 +14:28:37 diskread=4GB health=200 0.010371 +14:29:09 diskread=4GB health=200 0.015567 +14:29:40 diskread=4GB health=200 0.010465 +14:30:11 diskread=4GB health=200 0.010190 +14:30:42 diskread=4GB health=200 0.011160 +14:31:14 diskread=4GB health=200 0.009954 +14:31:45 diskread=4GB health=200 0.011131 +14:32:16 diskread=4GB health=200 0.011337 +14:32:48 diskread=4GB health=200 0.010336 +14:33:19 diskread=4GB health=200 0.011064 +14:33:50 diskread=4GB health=200 0.010944 +14:34:22 diskread=4GB health=200 0.010249 diff --git a/documentation/audits/rehearsal-2026-10-20/B4-use/box-metrics-after-plug.txt b/documentation/audits/rehearsal-2026-10-20/B4-use/box-metrics-after-plug.txt new file mode 100644 index 00000000..08aeb450 --- /dev/null +++ b/documentation/audits/rehearsal-2026-10-20/B4-use/box-metrics-after-plug.txt @@ -0,0 +1,16 @@ +UTC cpu% load1 mem_GB (the box's own /api/metrics/system history) +12:00 1.4 1.6 0.9384765625 +12:07 6.1 0.9 0.93359375 +12:14 3.0 0.6 0.970703125 +12:21 14.0 1.6 1.5107421875 +12:28 1.0 0.3 1.6123046875 +12:36 1.5 0.3 1.607421875 +12:43 11.8 1.4 1.6298828125 +12:50 15.2 1.5 2.056640625 +12:57 61.3 8.3 2.318359375 +13:33 78.1 139.3 2.2900390625 +13:55 78.1 151.6 2.3486328125 +14:09 0.0 2.0 1.1875 +keys: ['cpu', 'labels', 'load1', 'memory', 'temp'] +{"ok": true, "data": {"sync_status": {"last_sync": "2026-10-20T14:13:31.264994519Z", "last_status": "ok", "syncing": false}, "system": {"total_mem_mb": 4096, "used_mem_mb": 3638, "avail_mem_mb": 458, "mem_percent": 88.818359375, "disk_total_gb": 49.03971862792969, "disk_used_gb": 12.562141418457031, "disk_avail_gb": 33.96195983886719, "disk_percent": 27.001362904306276, "disk_known": true, "hdd_total_gb": 195.8027114868164, "hdd_used_gb": 0.10123825073242188, "hdd_avail_gb": 185.68584823608398, "hdd_percent": 0.054491543328877075, "hdd_configured": true, "hdd_known": true, "cpu_percent": 55.314861460957175, "load_avg_1": 3.31, "load_avg_5": 1.24, "load_avg_15": 0.44, "temperature_celsius": 0}}} +{"ok": true, "data": [{"name": "felhom-controller", "cpu_percent": 42.69, "mem_usage_mb": 92.52, "mem_limit_mb": 4096}, {"name": "actualbudget", "cpu_percent": 31.28, "mem_usage_mb": 128.6, "mem_limit_mb": 256}, {"name": "radicale", "cpu_percent": 28.31, "mem_usage_mb": 28.39, "mem_limit_mb": 128}, {"name": "bookstack-db", "cpu_percent": 27.86, "mem_usage_mb": 153.2, "mem_limit_mb": 256}, {"name": "immich-machine-learning", "cpu_percent": 27.57, "mem_usage_mb": 107.2, "mem_limit_mb": 2048}, {"name": "paperless-webserver", "cpu_percent": 27.41, "mem_usage_mb": 72.22, "mem_limit_mb": 1280}, {"name": "mealie", "cpu_percent": 26.57, "mem_usage_mb": 67.21, "mem_limit_mb": 1000}, {"name": "nextcloud-db", "cpu_percent": 26.17, "mem_usage_mb": 116.3, "mem_limit_mb": 512}, {"name": "immich-server", "cpu_percent": 22.66, "mem_usage_mb": 206, "mem_limit_mb": 3072}, {"name": "nextcloud", "cpu_percent": 19.4, "mem_usage_mb": 157.1, "mem_limit_mb": 1024}, {"name": "traefik", "cpu_percent": 10.78, "mem_usage_mb": 186.4, "mem_limit_mb": 4096}, {"name": "bookstack", "cpu_percent": 0.61, "mem_usage_mb": 19.94, "mem_limit_mb": 512}, {"name": "paperless-redis", "cpu_percent": 0.47, "mem_usage_mb": 26.13, "mem_limit_mb": 128}, {"name": "immich-redis", "cpu_percent": 0.43, "mem_usage_mb": 3.16, "mem_limit_mb": 128}, {"name": "nextcloud-redis", "cpu_percent": 0.42, "mem_usage_mb": 10.36, "mem_limit_mb": 128}, {"name": "paperless-postgres", "cpu_percent": 0.03, "mem_usage_mb": 58.27, "mem_limit_mb": 256}, {"name": "immich-postgres", "cpu_percent": 0.03, "mem_usage_mb": 133.1, "mem_limit_mb": 768}, {"name": "vaultwarden", "cpu_percent": 0, "mem_usage_mb": 72.27, "mem_limit_mb": 256}, {"name": "filebrowser", "cpu_percent": 0, "mem_usage_mb": 145, "mem_limit_mb": 256}]} diff --git a/documentation/audits/rehearsal-2026-10-20/B4-use/immich-upload.log b/documentation/audits/rehearsal-2026-10-20/B4-use/immich-upload.log new file mode 100644 index 00000000..1c49032a --- /dev/null +++ b/documentation/audits/rehearsal-2026-10-20/B4-use/immich-upload.log @@ -0,0 +1,49 @@ +12:59:51 +Traceback (most recent call last): + File "/root/work/apps.py", line 182, in + fn(*sys.argv[2:]) + ~~^^^^^^^^^^^^^^^ + File "/root/work/apps.py", line 168, in immich + st, out, _ = http("POST", base + "/api/assets", body, dict(tok, **{"Content-Type": ct})) + ~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/root/work/apps.py", line 34, in http + with urllib.request.urlopen(req, context=CTX, timeout=timeout) as r: + ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/lib/python3.13/urllib/request.py", line 189, in urlopen + return opener.open(url, data, timeout) + ~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^ + File "/usr/lib/python3.13/urllib/request.py", line 489, in open + response = self._open(req, data) + File "/usr/lib/python3.13/urllib/request.py", line 506, in _open + result = self._call_chain(self.handle_open, protocol, protocol + + '_open', req) + File "/usr/lib/python3.13/urllib/request.py", line 466, in _call_chain + result = func(*args) + File "/usr/lib/python3.13/urllib/request.py", line 1367, in https_open + return self.do_open(http.client.HTTPSConnection, req, + ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + context=self._context) + ^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/lib/python3.13/urllib/request.py", line 1323, in do_open + r = h.getresponse() + File "/usr/lib/python3.13/http/client.py", line 1459, in getresponse + response.begin() + ~~~~~~~~~~~~~~^^ + File "/usr/lib/python3.13/http/client.py", line 336, in begin + version, status, reason = self._read_status() + ~~~~~~~~~~~~~~~~~^^ + File "/usr/lib/python3.13/http/client.py", line 297, in _read_status + line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1") + ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^ + File "/usr/lib/python3.13/socket.py", line 719, in readinto + return self._sock.recv_into(b) + ~~~~~~~~~~~~~~~~~~~~^^^ + File "/usr/lib/python3.13/ssl.py", line 1304, in recv_into + return self.read(nbytes, buffer) + ~~~~~~~~~^^^^^^^^^^^^^^^^ + File "/usr/lib/python3.13/ssl.py", line 1138, in read + return self._sslobj.read(len, buffer) + ~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^ +TimeoutError: The read operation timed out +12:59:52Z immich admin-sign-up 400 b'{"message":"Admin setup is not available"}' +13:05:57 diff --git a/documentation/audits/rehearsal-2026-10-20/B4-use/nextcloud-upload-2.log b/documentation/audits/rehearsal-2026-10-20/B4-use/nextcloud-upload-2.log new file mode 100644 index 00000000..81376301 --- /dev/null +++ b/documentation/audits/rehearsal-2026-10-20/B4-use/nextcloud-upload-2.log @@ -0,0 +1 @@ +14:23:57 diff --git a/documentation/audits/rehearsal-2026-10-20/B4-use/nextcloud-upload.log b/documentation/audits/rehearsal-2026-10-20/B4-use/nextcloud-upload.log new file mode 100644 index 00000000..9e5784bf --- /dev/null +++ b/documentation/audits/rehearsal-2026-10-20/B4-use/nextcloud-upload.log @@ -0,0 +1,7 @@ +12:57:15 +13:12:06Z nextcloud rclone rc=-15 + 2026/10/20 12:57:17 NOTICE: Config file "/root/.config/rclone/rclone.conf" not found - using defaults + 2026/10/20 12:57:18 NOTICE: Csalad/Kulonleges_nevek/link_a_nagy_fajlra: Can't follow symlink without -L/--copy-links + 2026/10/20 12:57:18 NOTICE: Csalad/Kulonleges_nevek/Ödön születésnapja – fénykép lista.txt: Duplicate object found in source - ignoring + 2026/10/20 12:57:27 ERROR : Csalad/nagy_fajl_1_5GB.bin: Failed to copy: +13:12:06 diff --git a/documentation/audits/rehearsal-2026-10-20/B4-use/resume-watch.txt b/documentation/audits/rehearsal-2026-10-20/B4-use/resume-watch.txt new file mode 100644 index 00000000..a26c114c --- /dev/null +++ b/documentation/audits/rehearsal-2026-10-20/B4-use/resume-watch.txt @@ -0,0 +1,21 @@ +14:23:58 diskread=4GB health=200 0.010208 +14:24:30 diskread=4GB health=200 0.010427 +14:25:01 diskread=4GB health=200 0.012785 +14:25:32 diskread=4GB health=200 0.010109 +14:26:04 diskread=4GB health=200 0.009754 +14:26:35 diskread=4GB health=200 0.010561 +14:27:06 diskread=4GB health=200 0.013033 +14:27:37 diskread=4GB health=200 0.010545 +14:28:09 diskread=4GB health=200 0.010882 +14:28:40 diskread=4GB health=200 0.010998 +14:29:11 diskread=4GB health=200 0.009735 +14:29:42 diskread=4GB health=200 0.010706 +14:30:14 diskread=4GB health=200 0.012557 +14:30:45 diskread=4GB health=200 0.011448 +14:31:16 diskread=4GB health=200 0.010879 +14:31:47 diskread=4GB health=200 0.012360 +14:32:19 diskread=4GB health=200 0.009276 +14:32:50 diskread=4GB health=200 0.010233 +14:33:22 diskread=4GB health=200 0.013962 +14:33:53 diskread=4GB health=200 0.011101 +14:34:24 diskread=4GB health=200 0.012738 diff --git a/documentation/audits/rehearsal-2026-10-20/B4-use/state-after-stall.txt b/documentation/audits/rehearsal-2026-10-20/B4-use/state-after-stall.txt new file mode 100644 index 00000000..e69de29b diff --git a/documentation/audits/rehearsal-2026-10-20/B4-use/vm-counters-before-plug.txt b/documentation/audits/rehearsal-2026-10-20/B4-use/vm-counters-before-plug.txt new file mode 100644 index 00000000..5f7f74cb --- /dev/null +++ b/documentation/audits/rehearsal-2026-10-20/B4-use/vm-counters-before-plug.txt @@ -0,0 +1,6 @@ +diskread: 483315769272 +diskwrite: 47257374720 +mem: 7075991552 +memhost: 8218480640 +netin: 4738753971 +netout: 916386350 diff --git a/documentation/audits/rehearsal-2026-10-20/journal.md b/documentation/audits/rehearsal-2026-10-20/journal.md index 19e54ea1..4c4f7fa4 100644 --- a/documentation/audits/rehearsal-2026-10-20/journal.md +++ b/documentation/audits/rehearsal-2026-10-20/journal.md @@ -18,3 +18,28 @@ | 09:56 | B2 | the 10-18 17:42 link (used then) → „Ez a hivatkozás érvénytelen vagy lejárt" + an „Új linket kérek" button (not pressed) | | 09:56:58 | B2 | bind attempt 1: right pairing code + passphrase with one letter changed → „A megadott adatok nem megfelelőek…" — generic, correct; the page does not say how many of the 5 tries are left | | 09:57:09 | B2 | bind attempt 2: the stored value TESTER4_PASSWORD (from 2026-10-17, before Tester4 was re-created 10-18) → refused: it is stale. 2/5 used. **BLOCKED on the operator: the owner passphrase** | +| 12:00:57 | B2 | the operator's passphrase arrives in sec (intervention 1: an operator act the guide requires — the passphrase is handed over out of band by design) | +| 12:01:3x | B2 | bind attempt 3 (right code + passphrase) → „Sikeres összekötés" (`screens/b2-bind/`) | +| 12:02:25 | B2 | mail „[Felhom] Új beállító kód — újratelepült a szervered" (the guide's reinstall subject); code stored in sec | +| 12:02:43 | B2 | hub: host Tester4-816e21 created (NO REPORT yet); console turns to „a doboz össze van kötve" — the guide's „known bug: the code stays on screen" is FIXED (guide note stale); a stray „V" glyph follows the sentence (R-725 shape) | +| ≤12:13 | B2 | hub: Tester4-816e21 ONLINE, agent 0.161.0, controller 0.336.0 (the golden 0.331.0 → floor by itself); dashboard guest at 192.168.0.133 (DHCP) | +| 12:13 | B2 | harness: the household's address `https://felhom.tester4.test` has no DNS/tunnel (tester4.test is reserved); the browser LXC maps the name to the guest's LAN address in /etc/hosts, TLS not verified | +| 12:14:26 | B2 | claim with a typo in the setup code → „Hibás vagy lejárt kód" | +| 12:14:4x | B2 | claim with the right code + a 12-character password (exactly the minimum) → 302 → /launcher. **Install pressed → claimed: 27 min 50 s, of which 70 min of waiting for the passphrase are excluded (bind attempt 3 at 12:01)** | +| 12:15 | B2 | the yellow bar „A távoli mentés szünetel, amíg nem hozod létre a helyreállítási kódot" shows at once; the hub's Offsite page now lists Tester4 (0 B, no quota shown) and a WireGuard peer 10.77.0.6 → the operator switched off-site ON between 12:01 and 12:15 (intervention 2, an operator prerequisite the guide states) | +| 12:16:22 | B2 | recovery code ceremony through the bar's link: dashboard password → code ready in ~5 s → shown once (10 words) → the page asks words 2 and 7 → accepted → „Befejezés" → /backups/remote „A helyreállítási kód letétbe helyezve" — 16 s in all. The code went page → 0600 file → sec, never printed; every screenshot has the code blacked out (`screens/b2-escrow/`) | +| 12:16 | B2/B9 | the ceremony page shows technical checks to the household („Mentési tároló (PBS)", „Helyreállítási (DR) szint", „/usr/bin/age", „Rendszerjogosultság"); its step numbers jump 1 → 5 → 6; the remote page shows „restic + SFTP" and the Storage Box login `u629488-sub5@…your-storagebox.de /home/felhom-repo` | +| 12:17:13 | B2 | six wrong dashboard passwords from a fresh browser: five „Hibás jelszó.", the sixth „Túl sok hibás próbálkozás erről a címről. Próbáld újra egy perc múlva." (1 minute, per address). The guide's „15 percre zárol" is the SETUP page's rule; the login lock is not in the guide | +| — | B2 | the old (used) bind link: tested 09:56 → „érvénytelen vagy lejárt" + „Új linket kérek" (not pressed) | +| 12:18:09 | B3 | family card: „Ödön" and „Írisz" refused — „A név 1–32 karakter: kisbetű, szám, pont, kötőjel vagy aláhúzás." → added as odon, irisz, kata; each password answered once (→ sec) | +| 12:19 | B3 | the 200 GB second disk is NOT used by itself: the box made a 54.8 GB „Belső tárhely" on the system disk; the storage page offers „Új meghajtó inicializálása". The household pressed it: the first press did nothing (the required „Csatlakoztatási név" — „/mnt/" — was empty; only a browser tooltip); with `hdd_1` → „A meghajtó sikeresen inicializálva és regisztrálva", default for new installs (12:19:48) | +| 12:22–12:55 | B3 | through each app's own install page (real Chromium): **Immich** OK 117 s (2048 MB); **Nextcloud** admin `odon` + a password with spaces and ő/ű: „Időtúllépés — az alkalmazás még indulhat" at ~2 min, running a few minutes later; **Paperless-ngx** admin `irisz` + a 64-character password OK; **Vaultwarden** sign-up switched ON OK; **Mealie** OK; **Actual Budget** OK; **Radicale** OK; **BookStack** OK; **Jellyfin REFUSED**: „Nincs elég memória … telepítés után 4150 MB kellene, a gép 3712 MB-ot tud adni. Távolíts el egy alkalmazást…" (button disabled; no link to a page). 8 of 9 installed; memory meter 3638 / 3712 MB (98 %) | +| 12:22 | B3 | harness slip: my screenshot step blanked every field before the first Nextcloud press, so the form was sent empty and refused (15 min lost); fixed (values restored after each photo) and re-run | +| 12:24–12:55 | B9 | „Első lépések" texts: Immich mixes English („Administration → External Libraries → add /external/photos"); Paperless says „felhasználó \"admin\"" although the household set `irisz`; Vaultwarden says sign-up is closed although the household opened it; **Mealie names `mealie.tester4.test` while the app is at `recipes.tester4.test`** | +| 12:56:33 | B4 | Paperless: 53 PDFs posted through `/api/documents/post_document/` → 53 accepted (processing is the app's background work) | +| 12:57 | B4 | Radicale: calendar „Család" (MKCALENDAR 201) + 2 events (201, one crossing the DST change); Mealie: Gulyásleves + Csirkepörkölt with ingredients, steps and photos (201/200/200) | +| 12:57:15 | B4 | Nextcloud: the 3 393-file tree over WebDAV (rclone, 4 transfers) started; rclone keeps only ONE of the NFC/NFD name pair („Duplicate object found in source") and skips the symlink — tool limits, not the box's; the 1.5 GiB file FAILED at 12:57:27 with an HTML error page | +| 12:58–12:59 | B4 | Immich: the setup gate shows a stranger „Ez az alkalmazás még beállításra vár"; Ödön registered as admin through Immich's own form; the gate opened by itself within 20 s | +| 12:59:52 | B4 | Immich upload through `/api/assets`: the FIRST photo hangs 300 s and times out | +| ~13:05 → | B4 | **THE BOX STOPS ANSWERING.** The dashboard's `/api/health` gives no answer in 10–15 s; the VM reads ~4 GB/min from its disk (158 GB → 209 GB in 15 min) while network traffic stands still; ping fine. 13:12 the upload stopped (the family gives up) — no recovery 10 min later. **The hub reads the box ONLINE / OK all along** (host memory 39 %, controller OK, 13:22). Likely: the apps' own background work after the uploads (Paperless OCR of 53 PDFs, Immich's analysis, Nextcloud) on a guest booked to 98 % of its memory — NOT measured (no shell in the box) | +| 13:24 | B4 | no inside view of the box — tried: root SSH with the install-time password → „Permission denied" (replaced at step 4b, by design); DooPlex's key → denied; the hub-vaulted recovery password → operator-only reveal (the token is refused); the agent's local API token → only on the box. So the stall is described from outside only | diff --git a/documentation/audits/rehearsal-2026-10-20/screens/README.md b/documentation/audits/rehearsal-2026-10-20/screens/README.md new file mode 100644 index 00000000..8294ffc4 --- /dev/null +++ b/documentation/audits/rehearsal-2026-10-20/screens/README.md @@ -0,0 +1,142 @@ +# Rehearsal 2026-10-20 — every screen the household met + +Phone = 390×844, computer = 1366×768 (Chromium 155 + Playwright 1.52.0 in a throwaway container; see `../A4-browser/`). Hungarian and English browser languages. Codes, passwords and links are blacked out or never shown. Console captures are QEMU screendumps of the VM's monitor. + +## b1-site + +| screen | phone HU | phone EN | computer HU | computer EN | +|---|---|---|---|---| +| `b1-letoltes` | | | | | + +## b2-badpw + +| screen | phone HU | phone EN | computer HU | computer EN | +|---|---|---|---|---| +| `b2-badpw-0-login` | | — | — | — | +| `b2-badpw-6-after-six` | | — | — | — | + +## b2-bind + +| screen | phone HU | phone EN | computer HU | computer EN | +|---|---|---|---|---| +| `b2-bind-right-1-open` | — | — | | — | +| `b2-bind-right-2-answer` | — | — | | — | +| `b2-bind-stored-1-open` | | — | — | — | +| `b2-bind-stored-2-answer` | | — | — | — | +| `b2-bind-typo-1-open` | | — | — | — | +| `b2-bind-typo-2-answer` | | — | — | — | +| `b2-oldlink-1-open` | | — | — | — | + +## b2-claim + +| screen | phone HU | phone EN | computer HU | computer EN | +|---|---|---|---|---| +| `b2-claim-1-open` | | — | — | — | +| `b2-claim-2-typo-answer` | | — | — | — | +| `b2-claim-3-after-claim` | | — | — | — | + +## b2-escrow + +| screen | phone HU | phone EN | computer HU | computer EN | +|---|---|---|---|---| +| `b2-escrow-1-open` | | — | — | — | +| `b2-escrow-2-progress` | | — | — | — | +| `b2-escrow-3-reveal-gate` | | — | — | — | +| `b2-escrow-4-code-shown-REDACTED` | | — | — | — | +| `b2-escrow-5-words-ok` | | — | — | — | +| `b2-escrow-6-after-finish` | | — | — | — | + +## b3-apps + +| screen | phone HU | phone EN | computer HU | computer EN | +|---|---|---|---|---| +| `b3-apps-actualbudget-1-form` | — | — | | — | +| `b3-apps-actualbudget-2-result` | — | — | | — | +| `b3-apps-bookstack-1-form` | — | — | | — | +| `b3-apps-bookstack-2-result` | — | — | | — | +| `b3-apps-immich-1-install-page` | — | — | | — | +| `b3-apps-immich-2-result` | — | — | | — | +| `b3-apps-jellyfin-1-form` | — | — | | — | +| `b3-apps-jellyfin-refused` | | — | — | — | +| `b3-apps-mealie-1-form` | — | — | | — | +| `b3-apps-mealie-2-result` | — | — | | — | +| `b3-apps-nextcloud-1-form` | — | — | | — | +| `b3-apps-nextcloud-2-result` | — | — | | — | +| `b3-apps-paperless-ngx-1-form` | — | — | | — | +| `b3-apps-paperless-ngx-2-result` | — | — | | — | +| `b3-apps-radicale-1-form` | — | — | | — | +| `b3-apps-radicale-2-result` | — | — | | — | +| `b3-apps-vaultwarden-1-form` | — | — | | — | +| `b3-apps-vaultwarden-2-result` | — | — | | — | +| `b3-drives-1-storage` | — | — | | — | +| `b3-drives-2-dashboard` | — | — | | — | +| `b3-drives-3-init-open` | — | — | | — | +| `b3-drives-4-init-config` | — | — | | — | +| `b3-drives-5-init-after-press` | — | — | | — | +| `b3-drives-5-init-confirm` | — | — | | — | +| `b3-family-1-security` | | — | — | — | + +## b4-immich + +| screen | phone HU | phone EN | computer HU | computer EN | +|---|---|---|---|---| +| `b4-immich-0-stranger` | | — | — | — | +| `b4-immich-1-first-open` | | — | — | — | +| `b4-immich-2-signup-form` | — | — | | — | +| `b4-immich-3-signup-filled` | — | — | | — | +| `b4-immich-4-after-signup` | — | — | | — | + +## install + +**01-boot.png** + + + +**02-eula.png** + + + +**03-disk.png** + + + +**04-location.png** + + + +**05-password.png** + + + +**06-pw-email.png** + + + +**07-network.png** + + + +**07b-hostname.png** + + + +**08-summary.png** + + + +**09-installing.png** + + + +**10-progress.png** + + + +**11-console.png** + + + +**12-after-bind.png** + + + diff --git a/documentation/audits/rehearsal-2026-10-20/screens/b2-badpw/b2-badpw-0-login-phone-hu.png b/documentation/audits/rehearsal-2026-10-20/screens/b2-badpw/b2-badpw-0-login-phone-hu.png new file mode 100644 index 00000000..7542e3ff Binary files /dev/null and b/documentation/audits/rehearsal-2026-10-20/screens/b2-badpw/b2-badpw-0-login-phone-hu.png differ diff --git a/documentation/audits/rehearsal-2026-10-20/screens/b2-badpw/b2-badpw-6-after-six-phone-hu.png b/documentation/audits/rehearsal-2026-10-20/screens/b2-badpw/b2-badpw-6-after-six-phone-hu.png new file mode 100644 index 00000000..4955339b Binary files /dev/null and b/documentation/audits/rehearsal-2026-10-20/screens/b2-badpw/b2-badpw-6-after-six-phone-hu.png differ diff --git a/documentation/audits/rehearsal-2026-10-20/screens/b2-bind/b2-bind-right-1-open-desktop-hu.png b/documentation/audits/rehearsal-2026-10-20/screens/b2-bind/b2-bind-right-1-open-desktop-hu.png new file mode 100644 index 00000000..96e32206 Binary files /dev/null and b/documentation/audits/rehearsal-2026-10-20/screens/b2-bind/b2-bind-right-1-open-desktop-hu.png differ diff --git a/documentation/audits/rehearsal-2026-10-20/screens/b2-bind/b2-bind-right-2-answer-desktop-hu.png b/documentation/audits/rehearsal-2026-10-20/screens/b2-bind/b2-bind-right-2-answer-desktop-hu.png new file mode 100644 index 00000000..74468686 Binary files /dev/null and b/documentation/audits/rehearsal-2026-10-20/screens/b2-bind/b2-bind-right-2-answer-desktop-hu.png differ diff --git a/documentation/audits/rehearsal-2026-10-20/screens/b2-claim/b2-claim-1-open-phone-hu.png b/documentation/audits/rehearsal-2026-10-20/screens/b2-claim/b2-claim-1-open-phone-hu.png new file mode 100644 index 00000000..e2e70038 Binary files /dev/null and b/documentation/audits/rehearsal-2026-10-20/screens/b2-claim/b2-claim-1-open-phone-hu.png differ diff --git a/documentation/audits/rehearsal-2026-10-20/screens/b2-claim/b2-claim-2-typo-answer-phone-hu.png b/documentation/audits/rehearsal-2026-10-20/screens/b2-claim/b2-claim-2-typo-answer-phone-hu.png new file mode 100644 index 00000000..08214776 Binary files /dev/null and b/documentation/audits/rehearsal-2026-10-20/screens/b2-claim/b2-claim-2-typo-answer-phone-hu.png differ diff --git a/documentation/audits/rehearsal-2026-10-20/screens/b2-claim/b2-claim-3-after-claim-phone-hu.png b/documentation/audits/rehearsal-2026-10-20/screens/b2-claim/b2-claim-3-after-claim-phone-hu.png new file mode 100644 index 00000000..03175dfe Binary files /dev/null and b/documentation/audits/rehearsal-2026-10-20/screens/b2-claim/b2-claim-3-after-claim-phone-hu.png differ diff --git a/documentation/audits/rehearsal-2026-10-20/screens/b2-escrow/b2-escrow-1-open-phone-hu.png b/documentation/audits/rehearsal-2026-10-20/screens/b2-escrow/b2-escrow-1-open-phone-hu.png new file mode 100644 index 00000000..de7e03b6 Binary files /dev/null and b/documentation/audits/rehearsal-2026-10-20/screens/b2-escrow/b2-escrow-1-open-phone-hu.png differ diff --git a/documentation/audits/rehearsal-2026-10-20/screens/b2-escrow/b2-escrow-2-progress-phone-hu.png b/documentation/audits/rehearsal-2026-10-20/screens/b2-escrow/b2-escrow-2-progress-phone-hu.png new file mode 100644 index 00000000..49548c0e Binary files /dev/null and b/documentation/audits/rehearsal-2026-10-20/screens/b2-escrow/b2-escrow-2-progress-phone-hu.png differ diff --git a/documentation/audits/rehearsal-2026-10-20/screens/b2-escrow/b2-escrow-3-reveal-gate-phone-hu.png b/documentation/audits/rehearsal-2026-10-20/screens/b2-escrow/b2-escrow-3-reveal-gate-phone-hu.png new file mode 100644 index 00000000..9d522916 Binary files /dev/null and b/documentation/audits/rehearsal-2026-10-20/screens/b2-escrow/b2-escrow-3-reveal-gate-phone-hu.png differ diff --git a/documentation/audits/rehearsal-2026-10-20/screens/b2-escrow/b2-escrow-4-code-shown-REDACTED-phone-hu.png b/documentation/audits/rehearsal-2026-10-20/screens/b2-escrow/b2-escrow-4-code-shown-REDACTED-phone-hu.png new file mode 100644 index 00000000..ec3667e1 Binary files /dev/null and b/documentation/audits/rehearsal-2026-10-20/screens/b2-escrow/b2-escrow-4-code-shown-REDACTED-phone-hu.png differ diff --git a/documentation/audits/rehearsal-2026-10-20/screens/b2-escrow/b2-escrow-5-words-ok-phone-hu.png b/documentation/audits/rehearsal-2026-10-20/screens/b2-escrow/b2-escrow-5-words-ok-phone-hu.png new file mode 100644 index 00000000..d72d1855 Binary files /dev/null and b/documentation/audits/rehearsal-2026-10-20/screens/b2-escrow/b2-escrow-5-words-ok-phone-hu.png differ diff --git a/documentation/audits/rehearsal-2026-10-20/screens/b2-escrow/b2-escrow-6-after-finish-phone-hu.png b/documentation/audits/rehearsal-2026-10-20/screens/b2-escrow/b2-escrow-6-after-finish-phone-hu.png new file mode 100644 index 00000000..10ff1021 Binary files /dev/null and b/documentation/audits/rehearsal-2026-10-20/screens/b2-escrow/b2-escrow-6-after-finish-phone-hu.png differ diff --git a/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-apps-actualbudget-1-form-desktop-hu.png b/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-apps-actualbudget-1-form-desktop-hu.png new file mode 100644 index 00000000..2c9b2c55 Binary files /dev/null and b/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-apps-actualbudget-1-form-desktop-hu.png differ diff --git a/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-apps-actualbudget-2-result-desktop-hu.png b/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-apps-actualbudget-2-result-desktop-hu.png new file mode 100644 index 00000000..2cb3e953 Binary files /dev/null and b/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-apps-actualbudget-2-result-desktop-hu.png differ diff --git a/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-apps-bookstack-1-form-desktop-hu.png b/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-apps-bookstack-1-form-desktop-hu.png new file mode 100644 index 00000000..b0e7a180 Binary files /dev/null and b/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-apps-bookstack-1-form-desktop-hu.png differ diff --git a/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-apps-bookstack-2-result-desktop-hu.png b/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-apps-bookstack-2-result-desktop-hu.png new file mode 100644 index 00000000..e3191075 Binary files /dev/null and b/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-apps-bookstack-2-result-desktop-hu.png differ diff --git a/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-apps-immich-1-install-page-desktop-hu.png b/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-apps-immich-1-install-page-desktop-hu.png new file mode 100644 index 00000000..b2b3d803 Binary files /dev/null and b/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-apps-immich-1-install-page-desktop-hu.png differ diff --git a/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-apps-immich-2-result-desktop-hu.png b/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-apps-immich-2-result-desktop-hu.png new file mode 100644 index 00000000..eb4bb6ba Binary files /dev/null and b/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-apps-immich-2-result-desktop-hu.png differ diff --git a/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-apps-jellyfin-1-form-desktop-hu.png b/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-apps-jellyfin-1-form-desktop-hu.png new file mode 100644 index 00000000..c90d200a Binary files /dev/null and b/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-apps-jellyfin-1-form-desktop-hu.png differ diff --git a/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-apps-jellyfin-refused-phone-hu.png b/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-apps-jellyfin-refused-phone-hu.png new file mode 100644 index 00000000..5d122abb Binary files /dev/null and b/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-apps-jellyfin-refused-phone-hu.png differ diff --git a/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-apps-mealie-1-form-desktop-hu.png b/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-apps-mealie-1-form-desktop-hu.png new file mode 100644 index 00000000..2c4a02a1 Binary files /dev/null and b/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-apps-mealie-1-form-desktop-hu.png differ diff --git a/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-apps-mealie-2-result-desktop-hu.png b/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-apps-mealie-2-result-desktop-hu.png new file mode 100644 index 00000000..f007b537 Binary files /dev/null and b/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-apps-mealie-2-result-desktop-hu.png differ diff --git a/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-apps-nextcloud-1-form-desktop-hu.png b/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-apps-nextcloud-1-form-desktop-hu.png new file mode 100644 index 00000000..4c96ad75 Binary files /dev/null and b/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-apps-nextcloud-1-form-desktop-hu.png differ diff --git a/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-apps-nextcloud-2-result-desktop-hu.png b/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-apps-nextcloud-2-result-desktop-hu.png new file mode 100644 index 00000000..38aea9d8 Binary files /dev/null and b/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-apps-nextcloud-2-result-desktop-hu.png differ diff --git a/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-apps-paperless-ngx-1-form-desktop-hu.png b/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-apps-paperless-ngx-1-form-desktop-hu.png new file mode 100644 index 00000000..ed617864 Binary files /dev/null and b/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-apps-paperless-ngx-1-form-desktop-hu.png differ diff --git a/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-apps-paperless-ngx-2-result-desktop-hu.png b/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-apps-paperless-ngx-2-result-desktop-hu.png new file mode 100644 index 00000000..4c77075c Binary files /dev/null and b/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-apps-paperless-ngx-2-result-desktop-hu.png differ diff --git a/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-apps-radicale-1-form-desktop-hu.png b/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-apps-radicale-1-form-desktop-hu.png new file mode 100644 index 00000000..a6f5c835 Binary files /dev/null and b/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-apps-radicale-1-form-desktop-hu.png differ diff --git a/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-apps-radicale-2-result-desktop-hu.png b/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-apps-radicale-2-result-desktop-hu.png new file mode 100644 index 00000000..9a811234 Binary files /dev/null and b/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-apps-radicale-2-result-desktop-hu.png differ diff --git a/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-apps-vaultwarden-1-form-desktop-hu.png b/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-apps-vaultwarden-1-form-desktop-hu.png new file mode 100644 index 00000000..646e94a3 Binary files /dev/null and b/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-apps-vaultwarden-1-form-desktop-hu.png differ diff --git a/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-apps-vaultwarden-2-result-desktop-hu.png b/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-apps-vaultwarden-2-result-desktop-hu.png new file mode 100644 index 00000000..12644727 Binary files /dev/null and b/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-apps-vaultwarden-2-result-desktop-hu.png differ diff --git a/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-drives-1-storage-desktop-hu.png b/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-drives-1-storage-desktop-hu.png new file mode 100644 index 00000000..17d75607 Binary files /dev/null and b/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-drives-1-storage-desktop-hu.png differ diff --git a/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-drives-2-dashboard-desktop-hu.png b/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-drives-2-dashboard-desktop-hu.png new file mode 100644 index 00000000..a4bb8188 Binary files /dev/null and b/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-drives-2-dashboard-desktop-hu.png differ diff --git a/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-drives-3-init-open-desktop-hu.png b/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-drives-3-init-open-desktop-hu.png new file mode 100644 index 00000000..08568e20 Binary files /dev/null and b/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-drives-3-init-open-desktop-hu.png differ diff --git a/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-drives-4-init-config-desktop-hu.png b/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-drives-4-init-config-desktop-hu.png new file mode 100644 index 00000000..83d46456 Binary files /dev/null and b/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-drives-4-init-config-desktop-hu.png differ diff --git a/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-drives-5-init-after-press-desktop-hu.png b/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-drives-5-init-after-press-desktop-hu.png new file mode 100644 index 00000000..5575ba6c Binary files /dev/null and b/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-drives-5-init-after-press-desktop-hu.png differ diff --git a/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-drives-5-init-confirm-desktop-hu.png b/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-drives-5-init-confirm-desktop-hu.png new file mode 100644 index 00000000..b109b250 Binary files /dev/null and b/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-drives-5-init-confirm-desktop-hu.png differ diff --git a/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-family-1-security-phone-hu.png b/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-family-1-security-phone-hu.png new file mode 100644 index 00000000..e76fe683 Binary files /dev/null and b/documentation/audits/rehearsal-2026-10-20/screens/b3-apps/b3-family-1-security-phone-hu.png differ diff --git a/documentation/audits/rehearsal-2026-10-20/screens/b4-immich/b4-immich-0-stranger-phone-hu.png b/documentation/audits/rehearsal-2026-10-20/screens/b4-immich/b4-immich-0-stranger-phone-hu.png new file mode 100644 index 00000000..e5d25315 Binary files /dev/null and b/documentation/audits/rehearsal-2026-10-20/screens/b4-immich/b4-immich-0-stranger-phone-hu.png differ diff --git a/documentation/audits/rehearsal-2026-10-20/screens/b4-immich/b4-immich-1-first-open-phone-hu.png b/documentation/audits/rehearsal-2026-10-20/screens/b4-immich/b4-immich-1-first-open-phone-hu.png new file mode 100644 index 00000000..4707c85c Binary files /dev/null and b/documentation/audits/rehearsal-2026-10-20/screens/b4-immich/b4-immich-1-first-open-phone-hu.png differ diff --git a/documentation/audits/rehearsal-2026-10-20/screens/b4-immich/b4-immich-2-signup-form-desktop-hu.png b/documentation/audits/rehearsal-2026-10-20/screens/b4-immich/b4-immich-2-signup-form-desktop-hu.png new file mode 100644 index 00000000..994f965a Binary files /dev/null and b/documentation/audits/rehearsal-2026-10-20/screens/b4-immich/b4-immich-2-signup-form-desktop-hu.png differ diff --git a/documentation/audits/rehearsal-2026-10-20/screens/b4-immich/b4-immich-3-signup-filled-desktop-hu.png b/documentation/audits/rehearsal-2026-10-20/screens/b4-immich/b4-immich-3-signup-filled-desktop-hu.png new file mode 100644 index 00000000..1dbcc244 Binary files /dev/null and b/documentation/audits/rehearsal-2026-10-20/screens/b4-immich/b4-immich-3-signup-filled-desktop-hu.png differ diff --git a/documentation/audits/rehearsal-2026-10-20/screens/b4-immich/b4-immich-4-after-signup-desktop-hu.png b/documentation/audits/rehearsal-2026-10-20/screens/b4-immich/b4-immich-4-after-signup-desktop-hu.png new file mode 100644 index 00000000..edccf2a8 Binary files /dev/null and b/documentation/audits/rehearsal-2026-10-20/screens/b4-immich/b4-immich-4-after-signup-desktop-hu.png differ diff --git a/documentation/audits/rehearsal-2026-10-20/screens/install/12-after-bind.png b/documentation/audits/rehearsal-2026-10-20/screens/install/12-after-bind.png new file mode 100644 index 00000000..ae2dea47 Binary files /dev/null and b/documentation/audits/rehearsal-2026-10-20/screens/install/12-after-bind.png differ diff --git a/documentation/audits/rehearsal-2026-10-20/tools/apps.py b/documentation/audits/rehearsal-2026-10-20/tools/apps.py index 40a5bac3..18f3b4c0 100644 --- a/documentation/audits/rehearsal-2026-10-20/tools/apps.py +++ b/documentation/audits/rehearsal-2026-10-20/tools/apps.py @@ -90,7 +90,7 @@ def nextcloud(sub, user_env, pw_env): obscured = subprocess.run(["rclone", "obscure", "-"], input=pw, capture_output=True, text=True).stdout.strip() env = dict(os.environ, RCLONE_CONFIG_NC_TYPE="webdav", RCLONE_CONFIG_NC_URL=f"https://{sub}.{DOMAIN}/remote.php/dav/files/{user}", RCLONE_CONFIG_NC_VENDOR="nextcloud", RCLONE_CONFIG_NC_USER=user, RCLONE_CONFIG_NC_PASS=obscured) - r = subprocess.run(["rclone", "copy", "--no-check-certificate", "--transfers", "4", "--stats", "60s", "--stats-one-line", + r = subprocess.run(["rclone", "copy", "--no-check-certificate", "--transfers", os.environ.get("TRANSFERS", "1"), "--stats", "60s", "--stats-one-line", "-v", os.path.join(DATA, "nextcloud"), "nc:/"], env=env, capture_output=True, text=True) tail = [l for l in (r.stdout + r.stderr).splitlines() if "ERROR" in l or "Transferred" in l or "Skipping" in l or "NOTICE" in l] say(f"nextcloud rclone rc={r.returncode}") diff --git a/documentation/audits/rehearsal-2026-10-20/tools/counts.py b/documentation/audits/rehearsal-2026-10-20/tools/counts.py new file mode 100644 index 00000000..47cd8276 --- /dev/null +++ b/documentation/audits/rehearsal-2026-10-20/tools/counts.py @@ -0,0 +1,23 @@ +#!/usr/bin/python3 +"""counts.py — what each app holds now, read through the app's own API (B4/B5/B7 comparisons). Env names are args.""" +import json, os, subprocess, sys +sys.path.insert(0, "/root/work"); import apps +from apps import http, basic +def paperless(u, p): + st, out, _ = http("GET", "https://paperless.tester4.test/api/documents/?page_size=1", None, {"Authorization": basic(os.environ[u], os.environ[p])}) + t = json.loads(out).get("count") if st == 200 else out[:120] + st2, out2, _ = http("GET", "https://paperless.tester4.test/api/tasks/?status=FAILURE", None, {"Authorization": basic(os.environ[u], os.environ[p])}) + fails = [x.get("task_file_name") for x in json.loads(out2)] if st2 == 200 else out2[:100] + print("paperless documents:", t, "| failed tasks:", fails) +def immich(e, p): + st, out, _ = http("POST", "https://photos.tester4.test/api/auth/login", {"email": os.environ[e], "password": os.environ[p]}) + tok = {"Authorization": "Bearer " + json.loads(out)["accessToken"]} + st, out, _ = http("GET", "https://photos.tester4.test/api/assets/statistics", None, tok) + print("immich assets:", out.decode()[:200]) +def nextcloud(u, p): + obs = subprocess.run(["rclone", "obscure", "-"], input=os.environ[p], capture_output=True, text=True).stdout.strip() + env = dict(os.environ, RCLONE_CONFIG_NC_TYPE="webdav", RCLONE_CONFIG_NC_URL=f"https://cloud.tester4.test/remote.php/dav/files/{os.environ[u]}", + RCLONE_CONFIG_NC_VENDOR="nextcloud", RCLONE_CONFIG_NC_USER=os.environ[u], RCLONE_CONFIG_NC_PASS=obs) + r = subprocess.run(["rclone", "size", "--no-check-certificate", "nc:/Csalad"], env=env, capture_output=True, text=True) + print("nextcloud Csalad:", (r.stdout + r.stderr).strip().replace("\n", " | ")[:300]) +globals()[sys.argv[1]](*sys.argv[2:]) diff --git a/documentation/audits/rehearsal-2026-10-20/tools/ctl.py b/documentation/audits/rehearsal-2026-10-20/tools/ctl.py index cc1142b9..2e2115c4 100644 --- a/documentation/audits/rehearsal-2026-10-20/tools/ctl.py +++ b/documentation/audits/rehearsal-2026-10-20/tools/ctl.py @@ -74,3 +74,18 @@ if __name__ == "__main__": code, body = call(m, p, d) print(code) print(json.dumps(body, ensure_ascii=False, indent=1)[:6000]) + + +def form(path, fields, timeout=600): + """A form POST (application/x-www-form-urlencoded) with the session and the X-CSRF-Token header.""" + import urllib.parse + if _SESS is None: + login() + out = _curl(["-H", "Cookie: " + _SESS, "-H", "X-CSRF-Token: " + _CSRF, "-w", "\n%{http_code}", "-X", "POST", + "--data-binary", "@-", "-H", "Content-Type: application/x-www-form-urlencoded", BASE + path], + inp=urllib.parse.urlencode(fields), timeout=timeout) + body, _, code = out.rpartition("\n") + try: + return code, json.loads(body) + except ValueError: + return code, {"_raw": body[:800]} diff --git a/documentation/audits/rehearsal-2026-10-20/tools/escrow.py b/documentation/audits/rehearsal-2026-10-20/tools/escrow.py new file mode 100644 index 00000000..7d4dc288 --- /dev/null +++ b/documentation/audits/rehearsal-2026-10-20/tools/escrow.py @@ -0,0 +1,80 @@ +#!/opt/pw/bin/python +"""escrow.py — the household's recovery code ceremony in real Chromium (rehearsal 2026-10-20, B2). + +Env: PW (the dashboard password). The recovery code is read off the page into /root/work/rc (0600) — never printed — +and DooPlex moves it into `sec` and shreds the file. Before every screenshot the code block is replaced by black +bars (the page keeps the code in a JS closure, so the check still works). +usage: escrow.py OUTPREFIX [--size phone|desktop] +""" +import os, re, sys, time +from playwright.sync_api import sync_playwright + +out = sys.argv[1] +size = sys.argv[sys.argv.index("--size") + 1] if "--size" in sys.argv else "phone" +vp = {"phone": dict(viewport={"width": 390, "height": 844}, device_scale_factor=2, is_mobile=True, has_touch=True), + "desktop": dict(viewport={"width": 1366, "height": 768})}[size] +T0 = time.time() + + +def say(*a): + print(f"+{time.time() - T0:6.1f}s", *a, flush=True) + + +def shot(pg, name): + pg.evaluate("""() => { const b = document.getElementById('code-block'); if (b && b.textContent) b.textContent = '█████-█████-█████-█████-█████-█████-█████-█████-█████-█████'; + document.querySelectorAll('input').forEach(e => { if (e.type !== 'checkbox') e.value = ''; }); }""") + pg.screenshot(path=f"{out}-{name}-{size}-hu.png", full_page=True) + + +with sync_playwright() as p: + b = p.chromium.launch(executable_path="/usr/bin/chromium", args=["--no-sandbox"]) + ctx = b.new_context(locale="hu-HU", timezone_id="Europe/Budapest", ignore_https_errors=True, + storage_state="/root/work/state-dash.json", **vp) + pg = ctx.new_page() + pg.goto("https://felhom.tester4.test/backup/escrow", wait_until="networkidle") + pg.wait_for_selector("#warn-card", state="visible", timeout=60000) + pg.fill("#reauth-password", os.environ["PW"]) + pg.click("#start-btn") + say("start pressed") + pg.wait_for_timeout(1500) + shot(pg, "2-progress") + try: + pg.wait_for_selector("#reveal-card, #void-card", state="visible", timeout=300000) + except Exception: + say("no reveal within 300 s:", re.sub(r"\s+", " ", pg.inner_text("#progress-card"))[:400]) + shot(pg, "2b-stuck") + sys.exit(2) + if pg.is_visible("#void-card"): + say("VOID card:", re.sub(r"\s+", " ", pg.inner_text("#void-card"))[:400]) + shot(pg, "3-void") + sys.exit(3) + say("reveal card shown") + shot(pg, "3-reveal-gate") + pg.click("#reveal-btn") + pg.wait_for_selector("#reveal-shown", state="visible", timeout=60000) + code = pg.inner_text("#code-block").strip() + fd = os.open("/root/work/rc", os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600) + os.write(fd, code.encode()) + os.close(fd) + words = code.split("-") + say(f"code read off the page: {len(words)} words, {len(code)} characters (stored 0600, not printed)") + label = pg.inner_text("#verify-label") + pos = [int(x) for x in re.findall(r"\d+", label)][:2] + say("the page asks for words at positions", pos, "| label:", label) + shot(pg, "4-code-shown-REDACTED") + pg.fill("#verify-w1", words[pos[0] - 1]) + pg.fill("#verify-w2", words[pos[1] - 1]) + pg.click("#verify-btn") + pg.wait_for_timeout(4000) + if pg.is_visible("#verify-error"): + say("verify error:", pg.inner_text("#verify-error")) + pg.wait_for_selector("#finish-block", state="visible", timeout=60000) + say("words accepted; finish block:", re.sub(r"\s+", " ", pg.inner_text("#finish-block"))[:400]) + shot(pg, "5-words-ok") + pg.check("#finish-check") + pg.click("#finish-btn") + pg.wait_for_load_state("networkidle") + pg.wait_for_timeout(3000) + say("after finish:", pg.url, re.sub(r"\s+", " ", pg.inner_text("main") if pg.locator("main").count() else pg.inner_text("body"))[:900]) + shot(pg, "6-after-finish") + b.close() diff --git a/documentation/audits/rehearsal-2026-10-20/tools/family.py b/documentation/audits/rehearsal-2026-10-20/tools/family.py new file mode 100644 index 00000000..d2d06868 --- /dev/null +++ b/documentation/audits/rehearsal-2026-10-20/tools/family.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python3 +"""family.py — add the household's three family members through the „Család" card's endpoint (B3). +Tries each name first as the family would write it (accented), then the plain form. The box answers each new member's +password ONCE; it goes to a 0600 file per member (DooPlex moves it into sec) and is never printed.""" +import os, sys +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +import ctl +outdir = sys.argv[1] +for want, plain in (("Ödön", "odon"), ("Írisz", "irisz"), ("Kata", "kata")): + for name in (want, plain) if want.lower() != plain else (plain,): + code, d = ctl.form("/family/members/add", {"name": name}) + pw = (d.get("data") or {}).get("password") if isinstance(d, dict) else None + shown = {k: v for k, v in (d.get("data") or {}).items() if k != "password"} if isinstance(d, dict) and isinstance(d.get("data"), dict) else d + print(f"add {name!r}: {code} {shown if code == '200' else d}" + (" [password answered once → file]" if pw else "")) + if pw: + fd = os.open(os.path.join(outdir, f"fam-{plain}"), os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600) + os.write(fd, pw.encode()); os.close(fd) + break +print("members:", ctl.call("GET", "/family/members")[1]) diff --git a/documentation/audits/rehearsal-2026-10-20/tools/install_spec.py b/documentation/audits/rehearsal-2026-10-20/tools/install_spec.py new file mode 100644 index 00000000..abe2d78b --- /dev/null +++ b/documentation/audits/rehearsal-2026-10-20/tools/install_spec.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +"""install_spec.py APP [FIELD=VALUE ...] [--pw ENVNAME] — writes the ui.py steps for one app's install page: +open it, fill the named fields, fill every password box and its confirmation with the secret ENVNAME, press +„Telepítés indítása", wait for the result card (15 min), screenshot, print the text.""" +import json, sys +app = sys.argv[1]; steps = [{"goto": f"https://felhom.tester4.test/apps/{app}/install"}, {"wait": 2000}] +pw = None +for a in sys.argv[2:]: + if a.startswith("--pw="): + pw = a[5:] + elif "=" in a: + k, v = a.split("=", 1) + steps.append({"select" if k.startswith("select:") else "fill": f"[name={k.split(':')[-1]}]", ("option" if k.startswith("select:") else "value"): v}) +if pw: + steps += [{"fill_all": "input[data-field-type=password]", "env": pw}, {"fill_all": "input[data-confirm-for]", "env": pw}] +steps += [{"shot": f"{app}-1-form"}, {"click": "#deploy-form button[type=submit]"}, {"wait_for": "#deploy-result", "timeout": 900000}, + {"wait": 3000}, {"shot": f"{app}-2-result"}, {"text": True}] +print(json.dumps({"size": "desktop", "lang": "hu", "state": "/root/work/state-dash.json", "out": "/root/shots/b3-apps", "steps": steps})) diff --git a/documentation/audits/rehearsal-2026-10-20/tools/ui.py b/documentation/audits/rehearsal-2026-10-20/tools/ui.py new file mode 100644 index 00000000..832ce0c8 --- /dev/null +++ b/documentation/audits/rehearsal-2026-10-20/tools/ui.py @@ -0,0 +1,107 @@ +#!/opt/pw/bin/python +"""ui.py — drive the household's screens in real Chromium, step by step (rehearsal 2026-10-20). + +usage: ui.py STEPS.json (STEPS may also be '-' to read JSON from the env var UI_STEPS) +STEPS = {"size": "phone"|"desktop", "lang": "hu"|"en", "state": "/root/work/state-dash.json" (load+save cookies), + "out": "/root/shots/", "steps": [ ... ]} +step kinds: + {"goto": URL} {"click": SELECTOR} / {"click_text": TEXT} + {"fill": SELECTOR, "env": NAME} — the value comes from env NAME (never printed) + {"fill": SELECTOR, "value": TEXT} — a non-secret value + {"fill": SELECTOR, "env": NAME, "typo": true} — one letter changed (a household's typo) + {"shot": NAME} — blanks every text/password/textarea input first, then screenshots full page + {"wait": MS} {"text": true} + {"wait_for": SELECTOR, "timeout": MS} {"fill_all": SELECTOR, "env": NAME} — every enabled match gets the secret + {"select": SELECTOR, "option": VALUE} — prints the visible page text (trimmed) + {"expect_nav": true} before a click — waits for the navigation the click causes +Every step prints one line: what it did, the URL (any 32+ hex run masked), the HTTP status of navigations. +""" +import json, os, re, sys +from playwright.sync_api import sync_playwright + +spec = json.loads(os.environ["UI_STEPS"]) if sys.argv[1] == "-" else json.load(open(sys.argv[1])) +SIZES = {"phone": dict(viewport={"width": 390, "height": 844}, device_scale_factor=2, is_mobile=True, has_touch=True, + user_agent="Mozilla/5.0 (Linux; Android 14; Pixel 8) AppleWebKit/537.36 (KHTML, like Gecko) " + "Chrome/155.0.0.0 Mobile Safari/537.36"), + "desktop": dict(viewport={"width": 1366, "height": 768})} +LOC = {"hu": "hu-HU", "en": "en-US"} +mask = lambda u: re.sub(r"[0-9a-f]{32,}", "", u) +size, lang = spec.get("size", "phone"), spec.get("lang", "hu") +out = spec.get("out", "/root/shots/x") + + +def typo(v): + i = len(v) // 2 + while i < len(v) and not v[i].isalpha(): + i += 1 + return v[:i] + ("q" if v[i] != "q" else "w") + v[i + 1:] if i < len(v) else v + "x" + + +with sync_playwright() as p: + b = p.chromium.launch(executable_path="/usr/bin/chromium", args=["--no-sandbox"]) + kw = dict(locale=LOC[lang], extra_http_headers={"Accept-Language": LOC[lang]}, timezone_id="Europe/Budapest", + ignore_https_errors=True, **SIZES[size]) + st = spec.get("state") + if st and os.path.exists(st): + kw["storage_state"] = st + ctx = b.new_context(**kw) + pg = ctx.new_page() + resp = {"s": None} + pg.on("response", lambda r: resp.update(s=r.status) if r.request.is_navigation_request() else None) + nav_next = False + for s in spec["steps"]: + try: + if "goto" in s: + r = pg.goto(s["goto"], wait_until="networkidle", timeout=60000) + print(f"goto {mask(pg.url)} -> {r.status if r else '-'} {pg.title()!r}") + elif "fill" in s: + v = os.environ[s["env"]] if "env" in s else s["value"] + if s.get("typo"): + v = typo(v) + pg.fill(s["fill"], v) + print(f"fill {s['fill']} ({'secret ' + s['env'] if 'env' in s else repr(v)}{', with a typo' if s.get('typo') else ''})") + elif "click" in s or "click_text" in s: + target = pg.locator(s["click"]) if "click" in s else pg.get_by_text(s["click_text"], exact=False) + if nav_next: + with pg.expect_navigation(wait_until="networkidle", timeout=60000): + target.first.click() + nav_next = False + else: + target.first.click() + pg.wait_for_load_state("networkidle", timeout=60000) + print(f"click {s.get('click') or s.get('click_text')!r} -> {mask(pg.url)} status={resp['s']}") + elif "expect_nav" in s: + nav_next = True + elif "wait_for" in s: + pg.wait_for_selector(s["wait_for"], state="visible", timeout=s.get("timeout", 600000)) + print(f"visible {s['wait_for']}") + elif "fill_all" in s: + n = 0 + for el in pg.locator(s["fill_all"]).all(): + if el.is_enabled(): + el.fill(os.environ[s["env"]]); n += 1 + print(f"fill_all {s['fill_all']} ({n} fields, secret {s['env']})") + elif "select" in s: + pg.select_option(s["select"], s["option"]) + print(f"select {s['select']} = {s['option']!r}") + elif "wait" in s: + pg.wait_for_timeout(s["wait"]) + elif "shot" in s: + # blank every text/password field FOR THE PHOTO ONLY, then put the values back (a blanked form that is + # submitted afterwards refuses — the 2026-10-20 Nextcloud slip) + pg.evaluate("window.__keep = []; document.querySelectorAll('input[type=text],input[type=password],input:not([type]),textarea')" + ".forEach(e => { if (e.value) { window.__keep.push([e, e.value]); e.value = ''; } })") + f = f"{out}-{s['shot']}-{size}-{lang}.png" + pg.screenshot(path=f, full_page=True) + pg.evaluate("(window.__keep || []).forEach(([e, v]) => { e.value = v; }); window.__keep = [];") + hs = pg.evaluate("document.documentElement.scrollWidth > window.innerWidth") + print(f"shot {f} hscroll={hs}") + elif "text" in s: + print("text:", re.sub(r"\s+", " ", pg.inner_text("body"))[:1500]) + except Exception as e: # noqa: BLE001 — a failing step is a result; record it and stop + print(f"STEP FAILED {s}: {type(e).__name__}: {str(e)[:300]}") + break + if st: + ctx.storage_state(path=st) + os.chmod(st, 0o600) + b.close()