38848ffbeb
gates / gates (push) Successful in 21s
Golden 0.242.0 baked, round-trip verified and vouched (cadence rule, R-468). Fresh box from the public ISO on demo-hp: landed on the vouched set, two apps deployed and used, backup, remove, byte-identical restore, power cut and code typo all PASS. Stopped for a volunteer by R-493 (no instructions) and R-494 (the setup mail's dashboard link has no DNS; intervention I1). R-493..R-500 filed. Capability map: first-hour row added (PARTIAL), journey row scoped. Stopgap Hungarian volunteer guide written. Hub teardown layer pending. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
483 lines
32 KiB
Markdown
483 lines
32 KiB
Markdown
# Journal — DRILL fresh install 0.242.0, 2026-09-14
|
||
|
||
Every observable in the order taken. Times are **UTC** unless marked. Hub log lines print **CEST**
|
||
(UTC+2). Screens are in `screens/`, named by step.
|
||
|
||
## Baselines (re-verified 12:5x UTC)
|
||
|
||
| repo | main | version |
|
||
|---|---|---|
|
||
| felhom-controller | `406755fa8fba` | v0.242.0 |
|
||
| felhom-agent | `4586f0f7f6d1` | v0.130.0 |
|
||
| felhom.eu | `41590f8ee618` | hub v0.112.0 |
|
||
|
||
Hub, before the bake: agent `0.130.0` vouched · golden `0.236.0` vouched · `min_agent 0.129.0` ·
|
||
floor `min_controller_version 0.242.0`. Highest register id **R-492**.
|
||
|
||
## Phase 0.1 — golden 0.242.0
|
||
|
||
Baked 13:01:04 → finished before 13:06:42 · round-trip verified · vouched **13:08:41**.
|
||
Full record: `documentation/tests/golden-0.242.0-2026-09-14/README.md`.
|
||
|
||
## Phase 0.2 — what a volunteer receives
|
||
|
||
Searched, with what was searched:
|
||
|
||
| source | what was looked for | result |
|
||
|---|---|---|
|
||
| `website/*.html` (9 pages) | `iso.felhom`, `iso`, `letolt`, `telepit` | **no mention of the installer, of `iso.felhom.eu`, or of any install step** (only `technologiak.html` "comparison" and two FAQ answers on power loss / backups matched the loose pattern) |
|
||
| `https://iso.felhom.eu/` | the root, `index.html` | **HTTP 404** both. Only a named object answers: `felhom-installer-1.26.1-pve9.2-1.iso` (200, 1 705 322 496 B, last-modified 2026-07-31) |
|
||
| `RUNBOOK-onboarding-draft-v4.md` | customer steps | **operator document**, operator present at the visit ("operator narrates"); C7 marked never executed |
|
||
| `email.md` in the workspace | `find /mnt/5_hdd/felhom.eu -maxdepth 5 -iname '*email*.md'` | **absent** (only `audits/FINDING-app-email-rollout-2026-06-29.md`, unrelated) |
|
||
| the R-11 tester one-pager | `find … -iname '*one-pager*'`, register | **never written** — ROADMAP-HISTORY: "RULED 2026-07-21 (channel); doc is the architect's" |
|
||
| hub e-mails to a new customer | `hub/internal/notify/templates.go` | two: **„Kösd össze a Felhom dobozodat"** (self-bind link, at customer creation) and **„Elindult a Felhom szervered — beállító kód"** (claim code, after day-0). Neither says how to get or install the software |
|
||
|
||
## Harness limits, declared before the walk (not interventions — the volunteer is not limited this way)
|
||
|
||
- **H1 — no mailbox.** Both customer mails go only to the registered address. The hub stores
|
||
hashes; the Resend key on DooPlex is send-only (`401 restricted_api_key … only send emails`,
|
||
probed 13:00). A volunteer reads their inbox; this harness cannot.
|
||
- **H2 — keyboard.** `qm sendkey` emits US scancodes; the installer defaults to **Hungarian**. The
|
||
layout was switched to U.S. English (as walk 5). A volunteer keeps Hungarian.
|
||
- **H3 — the USB stick.** Auto-reboot was unticked so the VM does not re-enter the installer from the
|
||
still-attached ISO; the ISO is detached by `qm set` after the install. A volunteer pulls the stick.
|
||
- **H4 — the Terminal UI entry** was chosen over the graphical default for key-drivability. Both
|
||
entries were release-gated.
|
||
|
||
## Scope choice
|
||
|
||
- Customer created **with the DR tier OFF** (the form defaults it ON). The DR tier provisions an
|
||
**ep0** namespace and token; the brief fences ep0. Offsite is OFF by default and stayed OFF.
|
||
|
||
## Step 1 — download
|
||
|
||
13:01:27 on demo-hp: `curl https://iso.felhom.eu/felhom-installer-1.26.1-pve9.2-1.iso` → rc 0,
|
||
**32 s**, 1 705 322 496 B, sha256 `f3cc86d5f0ec68bba4155c994b4fa84e208d50209bb6e815636c99e5441059a6`
|
||
= the published `.sha256` exactly. (No release report records a sha for 1.26.1; walk 5 recorded
|
||
"same sha256" without the value.) **A volunteer must already know the exact file name** — the site
|
||
lists nothing.
|
||
|
||
## Hub side — the operator's steps
|
||
|
||
13:03:03 `POST /configs/new` customer `drill0242`, name "Drill 0.242 first hour", domain
|
||
`drill0242.felhom.eu`, e-mail `drill0242@felhom.eu`, DR tier off → **303** 13:03:04. Hub log (CEST):
|
||
|
||
```
|
||
15:03:04 [INFO] Customer config created: drill0242
|
||
15:03:04 [INFO] self-bind link (hash 4270bdd2…, valid 7 days) emailed to the registered address of drill0242
|
||
15:03:04 [INFO] self-bind link auto-minted for drill0242 on customer creation (the console banner's promised email now exists)
|
||
```
|
||
|
||
The 5-word **Tulajdonosi jelmondat** was read from the customer page into a `0600` file on DooPlex
|
||
(shape: 5 words, 42 chars). **The operator must hand it to the volunteer out of band — nothing sends
|
||
it.** (The self-bind mail says „amelyet a beállításkor kaptál" — "which you received at setup".)
|
||
|
||
## Step 2 — install
|
||
|
||
VM **330 `drill0242-fresh-install`** on demo-hp: q35/OVMF (`pre-enrolled-keys=0`), 4 cores, 8 GB,
|
||
`cpu=host`, `scsi0` 200 G · `scsi1` 50 G · `scsi2` 50 G qcow2 on **`nvme-scratch`** (dir at
|
||
`/mnt/hdd_1`, the mount root), MAC `bc:24:11:0c:23:cc`.
|
||
|
||
| UTC | screen | what it said / what was done |
|
||
|---|---|---|
|
||
| 13:03:43 | power on | — |
|
||
| 13:03:49 | GRUB (`s01`, `s02`) | Felhom logo, „Saját felhőd, saját szabályaid"; entries **„Felhom telepítés"** / **„Felhom telepítés (szöveges mód)"** — the only Hungarian the installer shows |
|
||
| 13:05:09 | `s04` | **English** Proxmox EULA, „I agree" |
|
||
| 13:05:27 | `s05` | „Target harddisk: /dev/sda (QEMU HARDDISK) (200.00 GiB)" — default is the right disk here; **three disks present, no guidance which** |
|
||
| 13:05:43 | `s06` | Country Hungary · Timezone Europe/Budapest · **Keyboard Hungarian** (H2: changed to U.S. English) |
|
||
| 13:09:18 | `s13` | Root password [at least 8 characters] · Confirm · **Administrator email prefilled `mail@example.invalid`** — the volunteer must decide what to type; set `drill0242@felhom.eu` |
|
||
| 13:12:01 | `s16` | nic0 `bc:24:11:0c:23:cc` · **Hostname `pve.example.invalid`** · IP `192.168.0.134/24` (the DHCP lease, frozen as static) · GW `192.168.0.1` · DNS `192.168.0.250` |
|
||
| 13:12:21 | `s17` | Next on the defaults → **„Invalid values: hostname does not look valid"**. The default is refused; the volunteer must invent a hostname. Set `drill0242.felhom.eu` |
|
||
| 13:14:40 | `s22` | Summary: `ext4` · `/dev/sda` · `Europe/Budapest` · `U.S. English` · `drill0242@felhom.eu` · `nic0` · `drill0242.felhom.eu` · `192.168.0.134/24` · `192.168.0.1` · `192.168.0.250` · „[X] Automatically reboot" (H3: unticked, `s25`) |
|
||
| 13:15:54 | Install pressed | — |
|
||
| ≤13:19:18 | `s28` | „Success — Installation finished - reboot now?" (**≤ 3 m 24 s** of copying; disk 4.0 G) |
|
||
| 13:19:44 | first boot | H3: `qm stop`, `--delete ide2`, `--boot order=scsi0` in its own `qm set` (verified `boot: order=scsi0`, ide2 lines 0), `qm start` |
|
||
|
||
## Step 3 — the first screen and the claim
|
||
|
||
**13:21:10 — the hub lists an Unclaimed appliance (86 s after first power-on):** appliance 25,
|
||
pairing code `***-***` (redacted), MAC `bc:24:11:0c:23:cc`, "Standard PC (Q35 + ICH9, 2009)", 7.7 GB,
|
||
three SSH host keys.
|
||
|
||
**The console (`s29`, code redacted), verbatim:**
|
||
|
||
```
|
||
Welcome to the Proxmox Virtual Environment. Please use your web browser to
|
||
configure this server - connect to:
|
||
https://192.168.0.134:8006/
|
||
drill0242 login:
|
||
Felhom — a doboz készen áll, és a párosításra vár.
|
||
Párosító kód: ***-***
|
||
Nyisd meg az e-mailben kapott linket, és add meg
|
||
ezt a kódot és a jelszavadat.
|
||
Ez a képernyő magától frissül — nincs teendő a
|
||
doboznál, és nyugodtan itt hagyhatod bekapcsolva.
|
||
```
|
||
|
||
Two things a stranger meets here: **an English line telling them to open the Proxmox admin page**,
|
||
above the Felhom text; and **„a jelszavadat" ("your password")** for the secret the self-bind mail
|
||
calls **„Tulajdonosi jelmondat"** (R-323 renamed the mail, not the console).
|
||
|
||
**The bind (H1 consequence).** The volunteer's route is the self-bind link, which is in a mailbox this
|
||
harness cannot read. The operator's documented fallback was used: `POST /appliances/25/bind`
|
||
(`customer_id=drill0242`) at **13:21:43** → 303 `flash=appliance_bound`. **The self-bind page itself
|
||
was therefore NOT exercised.**
|
||
|
||
Hub log (CEST = UTC+2):
|
||
|
||
```
|
||
15:21:44 appliance 25 BOUND to customer drill0242 (mode=appliance) — delivery staged for its next poll
|
||
15:21:45 appliance credentials DELIVERED once to appliance 25 (… passphrase withheld)
|
||
15:21:52 [claim] claim code (gen 1) emailed to the registered address of drill0242
|
||
15:22:21 host enrolled: drill0242-3f4b42 (customer drill0242)
|
||
15:22:21 vaulted break-glass recovery credential for host drill0242-3f4b42 (user=root@pam, secret 32 chars)
|
||
15:22:22 Artifact manifest served for customer drill0242 (agent=0.130.0 golden=0.242.0)
|
||
15:22:38 wg registered: host=drill0242-3f4b42 … ip=10.77.0.5/32 changed=true gen=1 sync=ok
|
||
15:22:39 DR-recipe host-half stored for customer drill0242 (host drill0242-3f4b42, v1)
|
||
15:23:58 Event from drill0242: controller_started (info) — Controller elindult (0.242.0)
|
||
15:23:58 managed floor SERVED for drill0242: floor 0.242.0, agent requirement "0.129.0" from manifest (golden 0.242.0)
|
||
```
|
||
|
||
**Note on ep0:** `wg registered … sync=ok` is a WireGuard peer written on ep0 by enrolment itself,
|
||
with the DR tier OFF. The brief fenced ep0; the product touches it on every enrolment. Teardown owes
|
||
its removal.
|
||
|
||
## Step 4 — "ready", and the version
|
||
|
||
| | vouched | landed |
|
||
|---|---|---|
|
||
| agent | 0.130.0 | **0.130.0** (`felhom-agent --version`) |
|
||
| controller | golden 0.242.0 | **0.242.0** (`docker ps`: `felhom-controller:0.242.0 … (healthy)`) |
|
||
|
||
**No self-update happened because none was needed** — golden == floor. Power-on → controller running
|
||
**4 m 14 s**; bind → controller **2 m 15 s**. Infra: `traefik:v3.6.7`, `gtstef/filebrowser:1.3.3-stable`.
|
||
First local whole-guest backup ran unaided 15:28:53–15:29:23 CEST, OK (seen as a `backup` lock).
|
||
|
||
**The console still shows the pairing banner and the stale code after bind** (`s30`, 13:24:38) —
|
||
R-214, still open.
|
||
|
||
## Intervention I1 — the dashboard has no address (R-494, filed 13:2x BEFORE acting)
|
||
|
||
The claim mail says `https://felhom.drill0242.felhom.eu`. Measured:
|
||
|
||
```
|
||
felhom.drill0242.felhom.eu @1.1.1.1 A: (none) AAAA: (none)
|
||
control felhom.enkisfelhom.hu @1.1.1.1 A: 104.21.3.175 172.67.130.252
|
||
https://felhom.drill0242.felhom.eu curl rc 6 (cannot resolve)
|
||
@192.168.0.1 (router) (none)
|
||
@192.168.0.250 (installer-offered DNS) (none)
|
||
@192.168.0.134 (the box) 13:27:39Z (none) — google.com resolves: the resolver is alive
|
||
agent 15:27:44 CEST lanresolver: applied split-horizon record vmid=9201 domain=drill0242.felhom.eu ip=192.168.0.158
|
||
@192.168.0.134 (the box) 13:29:18Z 192.168.0.158
|
||
```
|
||
|
||
The hub has no tunnel/DNS creation code; `cf_tunnel_token` is an optional pasted field. **Act:** the
|
||
dashboard is reached at the guest LAN address with the name forced (`--resolve
|
||
felhom.drill0242.felhom.eu:443:192.168.0.158`), from demo-hp — which is where a browser on the
|
||
household LAN would be. `GET /` → 302 `/claim`; the page, verbatim:
|
||
|
||
> **A szerver beállítása** — Drill 0.242 first hour — „Add meg az e-mailben kapott beállító kódot,
|
||
> majd válassz saját jelszót a vezérlőpult védelméhez." · Beállító kód (`szó-szó-szó`) · Új jelszó
|
||
> (min. 12 karakter) · Új jelszó megerősítése · **Beállítás és belépés** · „Nem kaptad meg a kódot?
|
||
> **Új kód kérése**"
|
||
|
||
## H1 — the setup code (harness substitution, not a volunteer act)
|
||
|
||
The code is in the mail. Observe access to the appliance: `POST /hosts/drill0242-3f4b42/reveal-recovery-credential`
|
||
→ 200 (the designed operator path; it emits an audit event), stored `0600`, never printed. Then
|
||
`pct exec 9201 -- docker exec felhom-controller … --print-reset-code`.
|
||
|
||
**Harness slip, recorded:** the first mint at 13:29:28 (**generation 2**) was captured with a
|
||
pattern `[a-z-]*` that does not match accented letters, and the raw output was shredded before the
|
||
capture was checked — **that code was lost unseen**. Re-minted (generation 3) with a byte-agnostic
|
||
capture. Both mints supersede the mailed generation-1 code.
|
||
|
||
## Step 3 (cont.) — the claim, through the front door
|
||
|
||
All dashboard requests from here are made **from demo-hp** (the household LAN) to the guest's traefik
|
||
on `192.168.0.158:443` with the dashboard name forced — intervention I1, once, for the whole walk.
|
||
Secrets travel on stdin to curl, never on a command line; the dashboard password is a generated
|
||
24-character value in a `0600` file on DooPlex.
|
||
|
||
| UTC | request | result |
|
||
|---|---|---|
|
||
| 13:31:19 | `GET /claim` | 200; `felhom_claim_csrf` cookie (82 B) + form token (64 hex) |
|
||
| 13:31:20 | `POST /claim` `_csrf, code, new_password, confirm_password` | **302 → `/`**, `Set-Cookie: felhom_session` |
|
||
| 13:31:2x | `GET /launcher` | **200** — „Indítópult — drill0242.felhom.eu", one tile **Filebrowser**, „Indítópult megosztása … A megosztás jelenleg ki van kapcsolva." Menu: Vezérlőpult · Alkalmazások · Tárhely (Meghajtók, Hálózati tárhely) · Biztonsági mentés (Áttekintés, Távoli mentés, Alkalmazások, Visszaállítás) · Megosztás · Rendszermonitor · **Debug** · Beállítások · version `0.242.0` |
|
||
|
||
**Power-on → claimed dashboard: 27 m 37 s** (13:03:43 → 13:31:20), of which the harness's own
|
||
keyboard driving is most of the install phase.
|
||
|
||
## Step 5 — the app list
|
||
|
||
`GET /stacks` → 200, „Alkalmazások — drill0242.felhom.eu", filters Mind / Futó / Leállítva /
|
||
Telepíthető, **52** „Telepítés" links. `GET /stacks/bookstack/deploy` and `/stacks/privatebin/deploy`
|
||
→ 200. Both pages, verbatim in the parts a stranger reads:
|
||
|
||
> **BookStack — Telepítés** · „Egyszerű, könyv-szerű wiki és dokumentáció platform" · ~150M · Pi
|
||
> kompatibilis · Memória 1058 MB / 3712 MB (28%) · **„Hol lesznek az adatok: ennél az alkalmazásnál
|
||
> nincs külön adatmeghajtó-választás, ezért az adatai a rendszermeghajtóra kerülnek (/mnt/sys_drive).
|
||
> A mentései így is elkészülnek."** · „Automatikusan generált értékek … Jegyezze fel a szükséges
|
||
> jelszavakat!" (Alkalmazás kulcs, Adatbázis jelszó — Megjelenítés) · Aldomain `wiki` · „Az aldomain
|
||
> telepítés után nem módosítható…"
|
||
|
||
> **PrivateBin — Telepítés** · „Titkosított jegyzet és szöveg megosztás" · ~30M · Memória 1089 MB /
|
||
> 3712 MB (29%) · same data sentence · Aldomain `paste`
|
||
|
||
Observation, not yet a row: the drive page's „Meglévő meghajtó csatolása" candidate list
|
||
(`GET /api/disks/candidates`) offers **the guest's own system volume**
|
||
`/dev/mapper/pve-vm--9201--disk--1` (`mount_source /mnt/sys_drive`, `already_mounted: true`,
|
||
`data_bearing: true`) beside the two blank 50 G disks.
|
||
|
||
## Step 5 (cont.) — deploy
|
||
|
||
Both through the deploy page's own call: every named input of `#deploy-form` →
|
||
`POST /api/stacks/<app>/deploy {"values":{…}}`, then `GET /api/stacks/<app>` every 5 s (the page
|
||
polls every 3 s). Generated secrets travelled on stdin and were shredded.
|
||
|
||
| app | fields sent | POST | state transitions | running after |
|
||
|---|---|---|---|---|
|
||
| bookstack | `APP_KEY, DB_PASSWORD, SUBDOMAIN=wiki` | 13:34:38 → **202** „Telepítés elindítva – az állapot a kártyán követhető" | +6 s `deploying` · **+26 s `degraded`** · +42 s `starting` · +68 s `running` | **68 s** |
|
||
| privatebin | `SUBDOMAIN=paste` | 13:35:47 → **202** | +5 s `starting` · +21 s `running` | **21 s** |
|
||
|
||
Observation: the deploy page's poll handles `deploying / running / starting / unhealthy / exited /
|
||
stopped` and has **no `degraded` branch** (`deploy.html` ~1040–1095), so for those 16 s the page keeps
|
||
showing its previous step. Harmless here; recorded.
|
||
|
||
**App pages** (`/apps/bookstack`, `/apps/privatebin`): „Fut" · **„Naprakész"** · „Megnyitás ↗".
|
||
BookStack's „Első lépések": „Nyisd meg a **wiki.DOMAIN** címet a böngészőben · Jelentkezz be:
|
||
admin@admin.com / password · Változtasd meg azonnal az admin jelszót és email címet …" and a box
|
||
„Alapértelmezett belépés admin@admin.com / password — Az első bejelentkezés után azonnal változtasd
|
||
meg!". PrivateBin: „Nyisd meg a **paste.DOMAIN** címet …" → **R-498** (52 of 53 templates).
|
||
|
||
## Step 6 — using both, through their front doors
|
||
|
||
Front doors from demo-hp: `wiki.drill0242.felhom.eu` → 302 `/login`; `paste.drill0242.felhom.eu` →
|
||
200, 22 896 B, PrivateBin UI (English).
|
||
|
||
**BookStack** (over HTTPS, so its `secure` cookies work — R-460's 419 was a plain-HTTP effect):
|
||
|
||
| UTC | act | result |
|
||
|---|---|---|
|
||
| 13:37:18 | log in `admin@admin.com / password` (the page's own instruction) | 302 → `/`; logged-in markers 3 |
|
||
| 13:37:19 | change password via `POST /users/1` | **405 — harness wrong route** (BookStack ≥23 is `/settings/users/1`) |
|
||
| 13:37:52 | change admin e-mail + password via `POST /settings/users/1` `_method=PUT` | 302 → `/settings/users`; **old default login now → `/login` (refused); new → `/` (accepted)** |
|
||
| 13:38:44 | create book „Családi receptek DRILL0242" | 302 → `/books/csaladi-receptek-drill0242` |
|
||
| 13:38:4x | save a draft with `_method=PUT` | **405 — harness** (the route is POST) |
|
||
| 13:39:35 | new draft 3 → `POST` name „Töltött káposzta", body `DRILL0242-OLDAL Töltött káposzta: árvíztűrő tükörfúrógép 2026-09-14` | 302 → `/books/csaladi-receptek-drill0242/page/toltott-kaposzta` |
|
||
| 13:39:3x | upload attachment `nagymama-recept.bin`, 262 144 B random, sha256 `2e252913844f8931…` | 200, attachment id 1 |
|
||
| 13:39:3x | read back | page 200, sentence present **2** (negative control **0**); attachment listed 1; `GET /attachments/1` 200 262 144 B, **sha256 equal: YES** |
|
||
|
||
**PrivateBin** — the browser's own v2 format (AES-256-GCM, PBKDF2-SHA256 100 000, zlib), key only in
|
||
the fragment, as a browser does:
|
||
|
||
| UTC | act | result |
|
||
|---|---|---|
|
||
| 13:38:08 | `POST /` a paste „DRILL-0242 privatebin sentinel … — árvíztűrő tükörfúrógép", expire 1 week | 200 `{"status":0,"id":"3963448c37a87005",…}` |
|
||
| 13:38:08 | `GET /?pasteid=3963448c37a87005`, decrypt | 200, 471 B, **decrypted == sent: True** (sha256 `5920d11290cca367…`); **wrong key → `InvalidTag`** |
|
||
|
||
## Step 7 — the backups pages, read as a first-timer (13:38:55)
|
||
|
||
`/backups` verbatim, in order: „Csak egy másolat készül (nincs második meghajtó) — a 3-2-1 mentéshez
|
||
csatlakoztasson egy második meghajtót vagy offsite tárolót." · „A rendszermentés jelenleg ugyanazon a
|
||
lemezen van, mint a rendszer — így hibás fájlok ellen véd, lemezhiba ellen nem. …" · Rendszer (/)
|
||
1.96 GB / 68.7 GB (3%) · DB mentések – · **Utolsó teljes mentés 2026-09-14 15:28 (10 perce) 624.3 MB
|
||
Helyi tároló (local) Naprakész** · **„Következő mentés — 0 órája — a mentési ablakon belül"** ·
|
||
Visszaállítás ellenőrizve „Még nem futott" · Mentés most · window 02:30 / 03:30 / 04:15 / 04:30–08:30
|
||
· Távoli rendszermentés „nincs beállítva".
|
||
|
||
- **Honest:** both warnings; the whole-box backup's date and target.
|
||
- **Confusing:** „Következő mentés" (next backup) with „0 órája" ("0 hours ago") under it — the value is
|
||
the age of the LAST backup (`backups.html:101-102`, `.AgeHours`).
|
||
|
||
`/backups/apps`: „Utolsó adatbázis mentés: Még nem futott"; BookStack „Konfig + DB + Adatok" and
|
||
PrivateBin „Konfig + Adatok", each „1. mentés Auto helyi **Utolsó: most**" · „Nincs 2. (off-drive)
|
||
másolat" · „3. mentés Nincs beállítva".
|
||
|
||
**„Utolsó: most" was checked, not assumed:** `timeAgoStr` renders „most" only for a parseable time
|
||
under a minute old (an empty value renders nothing, `funcmap.go:283-296`), and
|
||
`GET /api/backup/snapshots?stack=…` returned for both apps
|
||
`{"time":"2026-09-14T13:38:53Z","short_id":"helyi","tier":1,"drive_label":"Belső SSD (rendszer)"}` —
|
||
3 s before the page was read. **True.** (That unit predates the BookStack content of 13:39:35.)
|
||
|
||
`/stacks/bookstack/backup` → **R-499**: „… már szerepelnek a teljes rendszermentésben (PBS) … ehhez
|
||
az alkalmazáshoz nincs külön teendő." — this box has no PBS.
|
||
|
||
`/backups/restore`: app select BookStack / PrivateBin, „Pillanatkép: — Válasszon alkalmazást —",
|
||
„Még nincs mentés felhasználói adattal."
|
||
|
||
## Step 8 — „Mentés most" (the app-backups page's button, `POST /api/backup/run`)
|
||
|
||
| UTC | observable |
|
||
|---|---|
|
||
| 13:41:21 | pressed → `{"ok":true,"message":"Mentés elindítva"}` |
|
||
| +5 s | `{"enabled":true,"running":true}` |
|
||
| +21 s | `{"db_dump":{"count":1,"duration":"15.752362463s","last_run":"2026-09-14T13:41:37.325510764Z","success":true},"enabled":true,"running":false}` |
|
||
| 13:41:42 | both apps' restore points: `{"time":"2026-09-14T13:41:37Z","short_id":"helyi","tier":1,"drive_label":"Belső SSD (rendszer)"}` — **one entry each; the 13:38:53 unit is gone** (one local point kept) |
|
||
| 13:41:4x | `/backups/apps`: „Utolsó adatbázis mentés: **2026-09-14 15:41 (most)**"; Adatbázisok: `bookstack · MariaDB · 59.0 KB · 15:41 · 41 tábla · OK` |
|
||
|
||
**The date moved, to the true time, on both pages (R-476 class: holds).** 21 s end to end.
|
||
|
||
## Step 9 — version labels and Update
|
||
|
||
Both app pages: „Fut · **Naprakész**". `GET /api/stacks/<app>` at 13:42:28:
|
||
|
||
```
|
||
bookstack template_images {bookstack: lscr.io/linuxserver/bookstack:26.05.2, bookstack-db: mariadb:12.3}
|
||
catalog_images {bookstack: lscr.io/linuxserver/bookstack:26.05.2, bookstack-db: mariadb:12.3}
|
||
privatebin template_images {privatebin: privatebin/pdo:2.0.5} catalog_images {privatebin: privatebin/pdo:2.0.5}
|
||
```
|
||
|
||
**The catalog offers no newer version of either app, so there was nothing to press. Skipped, per the
|
||
brief.** „Naprakész" is true on this evidence (installed == catalog).
|
||
|
||
## Step 10 — removal: what the dialog shows for PrivateBin
|
||
|
||
The app page's delete opens `removeStack()` (`layout.html:396`), which loads
|
||
`/api/stacks/privatebin/hdd-data` → `{"hdd_paths":null,"has_hdd_data":false}` and
|
||
`/backup-data` → `[{"path":"/mnt/sys_drive/felhom-data/backups/primary/privatebin","size_human":"40K","exists":true}]`.
|
||
Rendered text, in order:
|
||
|
||
> **Alkalmazás eltávolítása: privatebin** · „Az alkalmazás visszaáll "Nincs telepítve" állapotba. A
|
||
> sablon megmarad, újratelepíthető." · „Ez a művelet nem visszavonható!" · **Mindig törlődik:** Docker
|
||
> kötetek (adatbázis, alkalmazás konfiguráció) · Telepítési konfiguráció (app.yaml) · Másodlagos mentés
|
||
> ütemezése · **Mentési adatok:** /mnt/sys_drive/felhom-data/backups/primary/privatebin (40K) ·
|
||
> ☐ **Mentési adatok törlése** · „Az éjszakai restic pillanatképek nem törölhetők egyenként — a
|
||
> megőrzési szabályok szerint automatikusan elavulnak." · Mégsem · Eltávolítás
|
||
|
||
**No „delete my data" box appears** — PrivateBin has no drive data; its data is in a Docker volume,
|
||
which the dialog says is **always** deleted. „Delete my data too" was therefore performed as **every
|
||
delete box ticked**: `remove_backups: true` (the only box), `remove_hdd_data: false` (no box shown).
|
||
Observation: the sentence about „éjszakai restic pillanatképek" appears on a box with no restic
|
||
configured at all.
|
||
|
||
## Step 11 prep — the household loses a recipe (13:43:07)
|
||
|
||
Through BookStack's own delete: `GET …/page/toltott-kaposzta/delete` → token; `POST …/page/toltott-kaposzta`
|
||
`_method=DELETE` → 302 to the book. Then: page **404**, `/attachments/1` **404**, the book page lists
|
||
the title **0** times. The last backup containing both is the 13:41:37 unit (step 8).
|
||
|
||
## Step 11 — restore BookStack from its backup, through the restore page
|
||
|
||
The page's own submit: `POST /backup/restore` `_csrf` (from the page meta), `stack_name=bookstack`,
|
||
`snapshot_id=helyi` (the only point, 13:41:37 — step 8).
|
||
|
||
| UTC | observable |
|
||
|---|---|
|
||
| 13:44:13 | **302** → `/backups/restore?flash=Visszaállítás elindult — az állapot itt frissül.` |
|
||
| +6 s | app `stopped` |
|
||
| +11 s | `starting` |
|
||
| +26 s | `running` (probe pending) |
|
||
| +32 s (13:44:45) | `running`, `health_probe.healthy: true` |
|
||
| 13:45:09 | **read back through the front door** — login with the post-change password → `/`; page `…/page/toltott-kaposzta` **200**; sentence present **2** (negative control **0**); `árvíztűrő tükörfúrógép` present **2**; `/attachments/1` **200, 262 144 B, sha256 equal to the pre-backup upload: YES** |
|
||
|
||
**DATA: PASS.** The recipe the household deleted at 13:43:07 is back, with its attachment
|
||
byte-identical and its Hungarian text intact, 32 s after pressing restore.
|
||
|
||
Reading the page at 13:45:11, after completion: the one-shot flash is gone and the page shows the
|
||
empty form again („Pillanatkép: — Válasszon alkalmazást — · Még nincs mentés felhasználói adattal.").
|
||
**CORRECTED 13:46 — the first reading here was wrong.** The page's script polls
|
||
`/api/backup/restore-status`, which at 13:46:03 returned `"last":{"op":"restore","stack":"bookstack","ok":true,
|
||
"message":"A(z) bookstack: 2 adatkötet és az adatbázis visszaállítva — az alkalmazás újraindult.",
|
||
"finished_at":"2026-09-14T13:44:39Z"},"last_recent":true`. So a Hungarian finish message exists and
|
||
names what came back; **whether the page shows it is script-rendered and was not observed** (no browser
|
||
here). The server-rendered HTML alone shows only the empty form.
|
||
|
||
## Step 10 — removal, done the way the screens lead
|
||
|
||
**Harness shortcut, withdrawn as a finding.** At 13:43:36 the harness called
|
||
`POST /api/stacks/privatebin/remove` on a **running** app and got **409** `stack "privatebin" is still
|
||
running — stop it first before removing` (English). The screens never offer that: for an operational
|
||
app `stacks.html:99-102` shows **Frissítés · Újraindítás · Leállítás** and no „Eltávolítás"; the app
|
||
page shows none either. Nothing changed on the box (before/after identical, `step10-remove-observe.txt`).
|
||
|
||
| UTC | act | result |
|
||
|---|---|---|
|
||
| 13:45:26 | „Leállítás" → `POST /api/stacks/privatebin/stop` | `{"ok":true,"message":"Stack privatebin stop completed"}` (English message; the UI shows its own text) · `stopped` at +6 s |
|
||
| 13:45:32 | observe before | volume `privatebin_privatebin_data` ×1 · containers 0 · `/opt/docker/stacks/privatebin/.felhom.yml` · backup dir **40K** |
|
||
| 13:45:33 | „Eltávolítás" with every box ticked → `POST …/remove {"remove_hdd_data":false,"remove_backups":true}` | **200** `{"removed":"privatebin","volumes_removed":["privatebin_privatebin_data"],"hdd_paths_removed":[],"hdd_paths_preserved":[],"backup_paths_removed":["/mnt/sys_drive/felhom-data/backups/primary/privatebin (40K)"]}` |
|
||
| 13:45:39 | observe after | volumes **0** · containers **0** · backup dir **No such file or directory** · only the catalog template `.felhom.yml` remains (the dialog: „A sablon megmarad") |
|
||
| 13:45:4x | front door / API | `paste.` → **404** · restore points `[]` · state `not_deployed`, `deployed=false` |
|
||
|
||
**PASS — the drive is clean of the app's data and its backups, and the response names each thing it
|
||
removed.** (`volumes_removed` is populated here; R-489 recorded `null` over removed volumes on
|
||
demo-hp — this box did not reproduce it.)
|
||
|
||
## Step 12 — status pages, read as a customer would a week later (13:46)
|
||
|
||
- `/launcher`: tiles **BookStack**, **Filebrowser** (PrivateBin gone — correct).
|
||
- `/dashboard`: „3 Futó alkalmazás · 0 Leállítva · 55 Összes alkalmazás" · Memória 1.0 GB / 4.0 GB ·
|
||
Rendszer (/) 2.11 GB / 68.7 GB · **Lemezek állapota: QEMU QEMU HARDDISK „Nincs adat" „0 °C"** ·
|
||
**„Utolsó mentés: 2026-09-14 13:41"** while `/backups/apps` says **15:41** for the same run → **R-500**
|
||
(`dashboard.html:154` formats without the box zone).
|
||
- `/monitoring`: „Hub kapcsolat — Kapcsolódva … Utolsó sikeres jelentés: most"; graphs „Még nincsenek
|
||
adatok…". The banner „A gazdagép metrikái jelenleg nem elérhetők" appeared in my text extraction,
|
||
**but it is `display:none` by default and shown only by script** (`monitoring.html:15`) — **not
|
||
evidence; withdrawn.** Whether the host card fills in was not observable without a browser.
|
||
- Observation, not filed: „0 °C" beside „Nincs adat" on a virtual disk.
|
||
|
||
## Evidence off the machine, end of Phase 1 (13:46)
|
||
|
||
`box-logs-phase1/`: `controller.log` (526 lines), `agent-journal.log` (2461), `bootstrap-journal.log`
|
||
(173, pairing code **redacted**), `box-state.txt`. Secret sweep over the copies for the claim code,
|
||
passphrase, dashboard password, BookStack password, break-glass credential and installer root password:
|
||
**0 each**; control (passphrase planted in a throwaway copy) **1**.
|
||
|
||
## Accident A1 — power cut (`A1-power-cut.txt`)
|
||
|
||
| UTC | observable |
|
||
|---|---|
|
||
| 13:47:31 | before: `felhom-controller:0.242.0` · `bookstack:26.05.2` · `mariadb:12.3` · `filebrowser:1.3.3-stable` · `traefik:v3.6.7`, all healthy · agent 0.130.0 |
|
||
| 13:47:33 | `qm stop 330` (hard) |
|
||
| 13:48:36 | `qm start 330` (60 s dark) |
|
||
| +37 s | appliance answers SSH |
|
||
| +2 m 07 s (13:50:43) | hub: `Event from drill0242: controller_started (info) — Controller elindult (0.242.0)` |
|
||
| +2 m 9 s | dashboard `/api/health` 200 |
|
||
| 13:58:11 | the dashboard session did not survive: `/api/stacks/bookstack` → **401 `authentication required`** (the customer logs in again; the harness loop misread this as unparseable for 8 min — harness fault, stopped) |
|
||
| 13:58:33 | after re-login: bookstack **running, healthy**, template images **unchanged**; every container `Up 7 minutes (healthy)`, **same five image tags**; agent 0.130.0 |
|
||
| 13:58:36 | BookStack front door: page 200, sentence **2** (control 0), Hungarian **2**, attachment **262 144 B, sha256 equal: YES** |
|
||
|
||
Hub log across the cut (non-routine lines only): `DR-recipe host-half stored` 15:49:04 CEST,
|
||
`controller_started` 15:50:43, `DR-recipe app-half stored` 15:50:44. **No alarm, no warning, no
|
||
customer mail event.**
|
||
|
||
**A1: PASS — every app back on the same version (Slice 3 holds), data intact, no false alarm, ≈2 min
|
||
to a working dashboard. Cost to the household: one re-login.**
|
||
|
||
## Accident A2 — a typo in the code
|
||
|
||
**Where it can be tried.** The setup code is single-use and this box is already claimed, so the
|
||
first-claim screen cannot be re-entered. The login page's „Elfelejtett jelszó" opens the **same
|
||
`/claim` form and the same code gate**, titled „Jelszó visszaállítása — Felhom · Add meg az e-mailben
|
||
kapott beállító kódot, majd válassz új jelszót." A2 was run there, on the same box. **The pairing-code
|
||
typo on the hub's self-bind page was NOT exercised** (H1: no link).
|
||
|
||
Code: generation 4, minted by H1 at 13:59:30. The typo is the real code with its last letter changed
|
||
— exactly one character different, same length (checked, never printed). Limiter, from source
|
||
(`claim.go:32-33`, `:170-200`): **5 failures → 15-minute lock, counted per source AND globally.**
|
||
|
||
| UTC | attempt | result |
|
||
|---|---|---|
|
||
| 14:00:09 | **typo** | **200**, form re-rendered (not accepted) |
|
||
| 14:00:10 | **the right code**, new 24-char password | **302 → `/`** — accepted after the typo |
|
||
| 14:00:10 | log in with the new password / the old one | **302** / **200** (old refused) |
|
||
| 14:00:10–12 | four more wrong codes (the spent code) | 200 each |
|
||
| 14:00:12 | the 5th failure | box: `[WARN] [web] claim: code lockout tripped (source 192.168.0.104) — 15 min` · `Event pushed: claim_lockout (warning) — Túl sok hibás beállító kód — a beállító oldal 15 percre zárolva` |
|
||
| 14:00:12 | a 6th attempt, during the lock | 200 |
|
||
| 14:00:16 | hub | `Event from drill0242: claim_lockout (warning) …` · **`Operator email sent for drill0242/claim_lockout`** |
|
||
|
||
**Harness note:** the text filter used on these replies missed the refusal wording, so the on-screen
|
||
messages are taken from the reply bodies below, and the plain „Hibás vagy lejárt kód" reading is
|
||
repeated after the lock expires.
|
||
| 14:15:26 | a wrong code **after** the 15-minute window | **200**, error box **„Hibás vagy lejárt kód"** — the form accepts attempts again |
|
||
|
||
On-screen wording, read from the reply bodies: during the lock **„Túl sok próbálkozás — próbáld újra
|
||
15 perc múlva."**; after it **„Hibás vagy lejárt kód"**. Both Hungarian, both true.
|
||
|
||
**A2: PASS** — a one-letter typo is refused without harm, the right code is accepted right after it,
|
||
the lock is honest about its length and lifts on time. Two properties recorded, not filed: the lock is
|
||
**global** as well as per source (`claim.go:188-200`) — five wrong guesses from anywhere lock the real
|
||
household out for 15 minutes, by design against guessing; and the lockout e-mail went to the
|
||
**operator only** (`Operator email sent for drill0242/claim_lockout`), while the screen tells the
|
||
customer.
|