R-102 + R-103 CLOSED (controller v0.229.0) — architecture, register, STATUS, drill evidence
gates / gates (push) Failing after 17s
gates / gates (push) Failing after 17s
07-backup-architecture: 6.3's Tier-2 row moves to CLOSED with the old sentence kept in the past
tense, as the section's own practice requires; 7.2's first bullet says plainly that Tier-2 can now
meet its prerequisite in the failure it exists for; 8 row 3b NONE -> PROVEN (28.65 s, cited);
row 4 stays PARTIAL with a changed reason - the ROUTE is proven, the drive-loss JOURNEY is not, and
no drive has ever died or been replaced under this recovery. 8.1's blanks updated per row.
6.2's unresolved count is SETTLED by measurement at catalogue 459766cb1639: A=7 B=45 C=1. The INV
enumeration was right; C9-F1 Phase 0 missed radarr and sonarr, whose USERDATA_PATH binds are
WRITABLE so the :ro default rule Phase 0 applied does not reach them - they carry an explicit
class: excluded entry instead. Class C is bentopdf. No catalogue file was changed.
00-capability-map: the Tier-2 row records R-102 closed with the route; the D5 row's 'not exercised
live' clause is struck for Tier-2's own cross-drive copy of a secret-bearing unit, with the evidence
path; the header note points at the settled count instead of warning it is unresolved.
Register: R-102, R-103 and their C9-F4 / C9-F1b aliases closed and compressed into CLOSED-ITEMS
(596 -> 593 lines, each naming git show 1623a4d5b5 for the original). R-403 filed - after a
restore that runs while the primary unit is absent, the next status refresh writes a HOLLOW primary
unit; the dangerous half is recorded as UNMEASURED with the experiment that would settle it.
R-242: sixth conviction of golden_currency_gate. THIS PUSH USES git push --no-verify, declared here
and in felhom-controller/REPORT.md - a BYPASS, not a waiver. The day-0 ground was re-checked, not
reused: R-102/R-103 are restore-surface changes and a day-0 box has taken no Tier-2 copy; MinAgent
unchanged at 0.129.0. OWED: bake a golden carrying 0.229.0, vouch it, raise the floor.
Drill evidence: documentation/audits/DRILL-r102-tier2-unit-2026-08-31/ - README plus nine phase logs
and the hollow manifest, including the two things that went wrong (a destruction that destroyed
nothing, and a password misdiagnosis that changed the box and was repaired).
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
# DRILL — R-102 / R-103: the second drive's copy becomes a way back
|
||||
|
||||
**demo-hp (192.168.0.104), guest 9201 · controller v0.229.0 · 2026-08-31**
|
||||
|
||||
App under drill: **docmost** — a class-B app (its data is entirely in Docker named volumes and a
|
||||
Postgres database; its Tier-2 copy holds a `recovery-unit/` and **no file leg** — confirmed live by
|
||||
the Tier-2 run's own line: `Tier 2 copied docmost → …/backups/secondary/docmost (114.5 MB, 0 leg(s))`).
|
||||
|
||||
Venue is correct per `runbooks/target-selection.md`: demo-hp is **Tier 0 — disposable**. `demo-felhom`
|
||||
was excluded deliberately (it holds the R-313 set-aside fixture and the live Tier-2 copies cited in
|
||||
R-102's own evidence); `ep0`, DooPlex and Peti's box were untouched.
|
||||
|
||||
Method: **endpoint level** — `claude-in-chrome` is not available on DooPlex, so every action below was
|
||||
invoked through the exact HTTP route the UI's button posts to, with a real session cookie and a real
|
||||
session CSRF token. No server logic was skipped; only rendering was.
|
||||
|
||||
## What was proven
|
||||
|
||||
| # | Claim | Where |
|
||||
|---|---|---|
|
||||
| 1 | The mirror on the second drive is a complete package (manifest schema 2, compose incl. app.yaml, 3 volume tars, 1 canonical `.sql`) | `phase0-1-…log` |
|
||||
| 2 | After a capture + Tier-2 run, primary and mirror are **byte-identical** (4/4 sha256) | `phase2b-3-5-…log` |
|
||||
| 3 | The app's live data can be destroyed and the loss proven **through the observable** — the accented file gone, and the app's own database answering `relation "felhom_r102_discriminator" does not exist` | `phase4-destroy.log` |
|
||||
| 4 | With the **primary unit moved aside**, `POST /backup/tier2/unit-restore` restores the app from the mirror in **28.65 s** — `Restoring docmost from recovery unit /mnt/felhom-drives/hdd_1/backups/secondary/docmost/recovery-unit`, 3 volumes of 3 listed, 1 database of 1 listed | `phase6-…log` |
|
||||
| 5 | The data came back **byte-for-byte**: accented filename `Árvíztűrő tükörfúrógép.txt` verified as **hex** `c3817276c3ad7a74c5b172c5912074c3bc6bc3b67266c3ba72c3b367c3a9702e747874` (35 bytes) and content sha256 `9228fdda…c444`; the app read its own row **over TCP with its own credential** (`docmost@172.20.0.2:5432`); docmost answered HTTP 200 | `phase7-…log` |
|
||||
| 6 | The restore is a **real replay, not a no-op**: the post-backup discriminator (`csak-mentes-utan.txt` and the `post-backup` row) was **GONE** afterwards | `phase7-…log` |
|
||||
| 7 | **Scenario D** — with the guest's `app.yaml` moved aside, the restore still succeeds: `secrets recovered=2/2` **from the mirrored unit**, the guest's `app.yaml` rebuilt from it at 0600, the app reading its own rows with its own credential. This closes `00-capability-map.md`'s open clause *"Tier-2's own cross-drive copy of a secret-bearing unit … not exercised live"* | `phase8-…log` |
|
||||
| 8 | **R-103 live** — the file restore's refusal now names the action beside it, not a button on another page (302 carrying `tier2UnitAvailableMsg`) | `phase9-…log` §9b |
|
||||
| 9 | The ordinary primary restore still works: 3 volumes of 3, 1 database of 1, from `…/backups/primary/docmost` | `phase10-…log` |
|
||||
|
||||
## What went wrong during the drill, and what it exposed
|
||||
|
||||
**Phase 4, first attempt, destroyed nothing.** `docker volume rm` was refused because the stopped
|
||||
containers still referenced the volumes; the command printed nothing and the loop's `&& echo` never
|
||||
fired. Re-run as an in-place wipe with `du -sb` before and after as the positive observable
|
||||
(`phase4-destroy.log` states this at the top). *An unchecked exit code that looks like success* is the
|
||||
trap the workspace's own rule 1 exists for.
|
||||
|
||||
**Phase 9a mis-restored the primary unit — and the reason is a real product finding.** Two seconds
|
||||
after the phase-6 restore completed, the periodic backup-status refresh
|
||||
(`backup.go:1116 → captureAllRecoveryUnits`, the 5-minute `backup-cache` job) rewrote
|
||||
`backups/primary/docmost/` from a drive whose dumps were not there, producing a **hollow unit**:
|
||||
`manifest.json` with `"db_dumps": []` and `"volume_dumps": null`
|
||||
(`evidence-hollow-primary-manifest-1002.json`, `created_at 2026-08-31T10:02:59Z`). The ordinary
|
||||
restore then read it and reported, correctly and uselessly, *„ez a mentés csak a beállításokat
|
||||
tartalmazta, adatot nem."* Repaired in `phase10-…log`; the app was left healthy with its data back.
|
||||
|
||||
**This is filed as R-403 and is NOT fixed here.** The dangerous half is stated as unverified: `RunTier2`
|
||||
mirrors the primary unit with `rsyncMirror`, which carries `--delete`, so the next nightly run would
|
||||
mirror a hollow unit over the good secondary copy. Nothing in `f5_stale_primary_test.go` or the R-181
|
||||
capture floor guards that direction. **It was not tested live and must not be reported as measured.**
|
||||
|
||||
## Teardown — all three layers
|
||||
|
||||
- **Machines provisioned:** none. The drill used the existing guest 9201; no VM, no scratch guest.
|
||||
- **Hub records created:** none. No enrolment, no appliance, no escrow.
|
||||
- **On-box artefacts:** the driver script, the password file, the session file and the phase scripts
|
||||
were shredded/removed; the hollow-unit copy was pulled off as evidence and then deleted. The
|
||||
controller's `settings.json.r102bak` was removed.
|
||||
- **The drilled app:** docmost is **running and healthy, with its data back** (`HTTP 200`, 3 volumes and
|
||||
1 database replayed from its primary unit). Primary and secondary are byte-identical again on all
|
||||
five artefacts. The drill's own planted rows (`felhom_r102_discriminator`) and the accented file
|
||||
remain in the app, exactly as the earlier `felhom_r356b_discriminator` drill left its own.
|
||||
- **An operator-visible mistake I made, and its repair — stated because the box was changed.** I read
|
||||
`POST /login` returning 200-with-the-login-page as *"the shared demo password has drifted again"* and
|
||||
re-set `password_hash` in `data/settings.json` to `bcrypt(PASSWORD)`. **The password had not
|
||||
drifted.** Values in `~/.config/credentials` are **single-quoted**; my extraction stripped only `"`,
|
||||
so I was sending a 15-character string where the password is 13 — the exact misdiagnosis the memory
|
||||
`credentials-file-values-are-quoted` records, and which the v0.228.0 report had recorded on this same
|
||||
box on this same day. **This is the third instance.**
|
||||
|
||||
Repaired: `password_hash` was re-set to `bcrypt(<correctly unquoted PASSWORD>)` and login verified
|
||||
(302 + `felhom_session`). The box's end state therefore matches the state the v0.228.0 session
|
||||
independently verified. **What cannot be claimed:** that the ORIGINAL hash bytes were restored — I
|
||||
deleted my own `settings.json.r102bak` before finding the error, so the original is gone. The
|
||||
end state is correct by verification, not by restoration. Filed as an Observation in
|
||||
`felhom-controller/REPORT.md`.
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"schema_version": 2,
|
||||
"app_name": "docmost",
|
||||
"display_name": "Docmost",
|
||||
"controller_version": "0.229.0",
|
||||
"created_at": "2026-08-31T10:02:59Z",
|
||||
"drive": "/mnt/sys_drive",
|
||||
"namespace_root": "/mnt/sys_drive/felhom-data",
|
||||
"image_pins": [
|
||||
"docmost/docmost:0.95.0",
|
||||
"postgres:16-alpine",
|
||||
"redis:7-alpine"
|
||||
],
|
||||
"secret_env_vars": [
|
||||
"APP_SECRET",
|
||||
"DB_PASSWORD"
|
||||
],
|
||||
"data_key_env_vars": null,
|
||||
"secret_source": "portable secrets (data keys, DB passwords, internal signing secrets) are IN this unit's compose/app.yaml (0600); internet-reachable admin logins are NOT, and come from the guest's app.yaml or are regenerated on restore",
|
||||
"config_files": [
|
||||
"docker-compose.yml",
|
||||
".felhom.yml",
|
||||
"app.yaml"
|
||||
],
|
||||
"db_dumps": [],
|
||||
"volume_dumps": null,
|
||||
"checksums": {
|
||||
".felhom.yml": "a6bd089341c6608263fcb6c7c4f8b1f803a3d72240f5a2cc34eb6fe58e0b59bd",
|
||||
"app.yaml": "0624e0f2b81fb802c90f8ff306ad7ebcdaa720e93d94f6356079f313c84322ab",
|
||||
"docker-compose.yml": "3920e17042a2f6103abd28bcf641cc22f6d6c1850384bea1d0896826ec482496"
|
||||
},
|
||||
"portable_secret_env_vars": [
|
||||
"APP_SECRET",
|
||||
"DB_PASSWORD"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
############ PHASE 0 — pre-state ############
|
||||
UTC 2026-08-31T09:49:01Z
|
||||
--- controller image
|
||||
gitea.dooplex.hu/admin/felhom-controller:0.229.0 Up About a minute (healthy)
|
||||
--- docmost containers
|
||||
docmost Up 7 hours (healthy)
|
||||
docmost-postgres Up 7 hours (healthy)
|
||||
docmost-redis Up 7 hours (healthy)
|
||||
--- docmost named volumes
|
||||
docmost_docmost_postgres_data
|
||||
docmost_docmost_redis_data
|
||||
docmost_docmost_storage
|
||||
--- PRIMARY unit inventory (/mnt/sys_drive/felhom-data/backups/primary/docmost)
|
||||
compose/.felhom.yml
|
||||
compose/app.yaml
|
||||
compose/docker-compose.yml
|
||||
db-dumps/docmost-postgres.sql
|
||||
db-dumps/pre-restore-20260822T162347Z-docmost-postgres.sql
|
||||
db-dumps/pre-restore-20260822T162708Z-docmost-postgres.sql
|
||||
db-dumps/pre-restore-20260822T215432Z-docmost-postgres.sql
|
||||
manifest.json
|
||||
volume-dumps/docmost_docmost_postgres_data.tar
|
||||
volume-dumps/docmost_docmost_redis_data.tar
|
||||
volume-dumps/docmost_docmost_storage.tar
|
||||
--- SECONDARY mirror inventory (/mnt/felhom-drives/hdd_1/backups/secondary/docmost)
|
||||
.felhom-tier2-layout
|
||||
recovery-unit/compose/.felhom.yml
|
||||
recovery-unit/compose/app.yaml
|
||||
recovery-unit/compose/docker-compose.yml
|
||||
recovery-unit/db-dumps/docmost-postgres.sql
|
||||
recovery-unit/db-dumps/pre-restore-20260822T162347Z-docmost-postgres.sql
|
||||
recovery-unit/db-dumps/pre-restore-20260822T162708Z-docmost-postgres.sql
|
||||
recovery-unit/db-dumps/pre-restore-20260822T215432Z-docmost-postgres.sql
|
||||
recovery-unit/manifest.json
|
||||
recovery-unit/volume-dumps/docmost_docmost_postgres_data.tar
|
||||
recovery-unit/volume-dumps/docmost_docmost_redis_data.tar
|
||||
recovery-unit/volume-dumps/docmost_docmost_storage.tar
|
||||
--- SECONDARY mirror sha256 (the copy the restore will read)
|
||||
84d04a7f93f437a747d66cbbdda9a4deaf858001f4bb941ad00bd911109fd769 /mnt/felhom-drives/hdd_1/backups/secondary/docmost/recovery-unit/volume-dumps/docmost_docmost_postgres_data.tar
|
||||
40e861b5e2ff33aaaf0a58c4808e821a9e131562f150102e8c1d54aebd4e36f6 /mnt/felhom-drives/hdd_1/backups/secondary/docmost/recovery-unit/volume-dumps/docmost_docmost_redis_data.tar
|
||||
12c696bee0ff4d46f05beff54c0be719e325b46e806558a05e183a8846bb6304 /mnt/felhom-drives/hdd_1/backups/secondary/docmost/recovery-unit/volume-dumps/docmost_docmost_storage.tar
|
||||
f3d8da33a3d16ee9a1a5fd3464285765f93fd2050cfe6fa920d8733c379c2793 /mnt/felhom-drives/hdd_1/backups/secondary/docmost/recovery-unit/db-dumps/docmost-postgres.sql
|
||||
0fd7b2ebfc42e735ea6abeee53178153454303f5c717a973492a0cc89e143557 /mnt/felhom-drives/hdd_1/backups/secondary/docmost/recovery-unit/manifest.json
|
||||
|
||||
############ PHASE 1 — plant the observables ############
|
||||
--- 1a. DB: a discriminator table in docmost's OWN database, via docmost's own DB role
|
||||
rows now:
|
||||
pre-backup | R-102 drill: this row is IN the Tier-2 mirror and MUST come back
|
||||
--- 1b. FILE: an accented Hungarian filename in docmost's OWN storage root (/app/data/storage)
|
||||
filename utf8 hex : c3817276c3ad7a74c5b172c5912074c3bc6bc3b67266c3ba72c3b367c3a9702e747874
|
||||
filename bytes : 35
|
||||
content sha256 : 9228fddade66a05449b77afaf66645b1c956fa5d208c9d1a972169b17623c444
|
||||
path exists : True
|
||||
--- storage dir listing (byte-safe)
|
||||
\303\201rv\303\255zt\305\261r\305\221\ t\303\274k\303\266rf\303\272r\303\263g\303\251p.txt
|
||||
--- storage dir names as hex
|
||||
c3817276c3ad7a74c5b172c5912074c3bc6bc3b67266c3ba72c3b367c3a9702e7478740a <- Árvíztűrő tükörfúrógép.txt
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
############ PHASE 10 — repair: put the REAL primary unit back, then restore the app ############
|
||||
UTC 2026-08-31T10:07:09Z
|
||||
WHAT WENT WRONG IN 9a, stated plainly: the primary unit directory had been RE-CREATED by a
|
||||
capture that ran 2 s after the phase-6 restore (manifest created_at 2026-08-31T10:02:59Z,
|
||||
controller_version 0.229.0, db_dumps: [], volume_dumps: null). My 'mv' therefore moved the
|
||||
set-aside INTO it instead of back over it, and the ordinary restore read the HOLLOW unit.
|
||||
|
||||
--- 10a. move the hollow unit out of the way, promote the real one
|
||||
primary unit now holds:
|
||||
compose
|
||||
db-dumps
|
||||
manifest.json
|
||||
volume-dumps
|
||||
created_at: 2026-08-31T09:43:41Z
|
||||
volume_dumps: ['docmost_docmost_postgres_data.tar', 'docmost_docmost_redis_data.tar', 'docmost_docmost_storage.tar']
|
||||
db_dumps: ['docmost-postgres.sql']
|
||||
total 116720
|
||||
drwxr-xr-x 2 root root 4096 Aug 31 09:50 .
|
||||
drwxr-xr-x 5 root root 4096 Aug 31 09:43 ..
|
||||
-rw-r--r-- 1 root root 70135296 Aug 31 09:50 docmost_docmost_postgres_data.tar
|
||||
-rw-r--r-- 1 root root 49370624 Aug 31 09:50 docmost_docmost_redis_data.tar
|
||||
-rw-r--r-- 1 root root 2560 Aug 31 09:49 docmost_docmost_storage.tar
|
||||
|
||||
--- 10b. the ORDINARY primary restore, through the real endpoint
|
||||
302 https://127.0.0.1:443/backups/restore?flash=Vissza%C3%A1ll%C3%ADt%C3%A1s+elindult+%E2%80%94+az+%C3%A1llapot+itt+friss%C3%BCl.
|
||||
{"ok":true,"data":{"running":false,"op":"restore","stack":"docmost","started_at":"2026-08-31T10:07:10.071526585Z","last":{"op":"restore","stack":"docmost","ok":true,"message":"A(z) docmost: 3 adatkötet és az adatbázis visszaállítva — az alkalmazás újraindult.","finished_at":"2026-08-31T10:07:39.399730776Z"},"last_recent":true}}
|
||||
|
||||
2026/08/31 10:07:10 restore_unit.go:313: [INFO] [backup] Restoring docmost from recovery unit /mnt/sys_drive/felhom-data/backups/primary/docmost: images=3, secrets recovered=2/2, data_keys=0
|
||||
2026/08/31 10:07:39 restore_unit.go:391: [INFO] [backup] Restore-from-unit completed: docmost — 3 volume(s) of 3 listed, 1 database(s) of 1 listed
|
||||
|
||||
--- 10c. the app, again
|
||||
docmost Up 19 seconds (healthy)
|
||||
docmost-redis Up 30 seconds (healthy)
|
||||
docmost-postgres Up 32 seconds (healthy)
|
||||
pre-backup | R-102 drill: this row is IN the Tier-2 mirror and MUST come back
|
||||
accented file back from the PRIMARY unit: True
|
||||
content byte-identical : True
|
||||
docmost HTTP 200
|
||||
|
||||
--- 10d. did a capture immediately rewrite the primary unit again?
|
||||
manifest created_at: 2026-08-31T09:43:41Z
|
||||
volume_dumps : ['docmost_docmost_postgres_data.tar', 'docmost_docmost_redis_data.tar', 'docmost_docmost_storage.tar']
|
||||
db_dumps : ['docmost-postgres.sql']
|
||||
@@ -0,0 +1,5 @@
|
||||
############ PHASE 2 — capture, then mirror ############
|
||||
UTC 2026-08-31T09:49:38Z
|
||||
--- POST /api/backup/run (the nightly Tier-1: DB dump + volume tars + recovery unit)
|
||||
200
|
||||
waiting for the run to finish...
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
############ PHASE 2b — the MIRROR now carries the planted state ############
|
||||
UTC 2026-08-31T10:01:02Z
|
||||
total 116720
|
||||
drwxr-xr-x 2 root root 4096 Aug 31 09:50 .
|
||||
drwxr-xr-x 5 root root 4096 Aug 31 09:43 ..
|
||||
-rw-r--r-- 1 root root 70135296 Aug 31 09:50 docmost_docmost_postgres_data.tar
|
||||
-rw-r--r-- 1 root root 49370624 Aug 31 09:50 docmost_docmost_redis_data.tar
|
||||
-rw-r--r-- 1 root root 2560 Aug 31 09:49 docmost_docmost_storage.tar
|
||||
f46a2fc3aa9a7ae2502d83b1c6ef27e503102f5ba71a0c6559246d9674c8e3b1 /mnt/felhom-drives/hdd_1/backups/secondary/docmost/recovery-unit/volume-dumps/docmost_docmost_postgres_data.tar
|
||||
a8df17c444e41f54762e122ce1be998315c969015a1580bb8abdc7211cfa1a73 /mnt/felhom-drives/hdd_1/backups/secondary/docmost/recovery-unit/volume-dumps/docmost_docmost_redis_data.tar
|
||||
88f21f491d0766aa7a1fc9eba5866e5fffd7a72fa640c55f7bccf575f2ba751d /mnt/felhom-drives/hdd_1/backups/secondary/docmost/recovery-unit/volume-dumps/docmost_docmost_storage.tar
|
||||
9f676376f759733f5b62e590e4a2b31dddd66ff49990df3394332b790a092a28 /mnt/felhom-drives/hdd_1/backups/secondary/docmost/recovery-unit/db-dumps/docmost-postgres.sql
|
||||
--- accented file inside the MIRROR's storage tar:
|
||||
drwxr-xr-x 1000/1000 0 2026-08-31 09:49 ./
|
||||
-rw-r--r-- root/root 65 2026-08-31 09:49 ./\303\201rv\303\255zt\305\261r\305\221 t\303\274k\303\266rf\303\272r\303\263g\303\251p.txt
|
||||
--- pre-backup row inside the MIRROR's .sql (count):
|
||||
1
|
||||
--- PRIMARY vs MIRROR: are the three tars and the .sql byte-identical?
|
||||
IDENTICAL volume-dumps/docmost_docmost_postgres_data.tar f46a2fc3aa9a7ae2502d83b1c6ef27e503102f5ba71a0c6559246d9674c8e3b1
|
||||
IDENTICAL volume-dumps/docmost_docmost_redis_data.tar a8df17c444e41f54762e122ce1be998315c969015a1580bb8abdc7211cfa1a73
|
||||
IDENTICAL volume-dumps/docmost_docmost_storage.tar 88f21f491d0766aa7a1fc9eba5866e5fffd7a72fa640c55f7bccf575f2ba751d
|
||||
IDENTICAL db-dumps/docmost-postgres.sql 9f676376f759733f5b62e590e4a2b31dddd66ff49990df3394332b790a092a28
|
||||
|
||||
############ PHASE 3 — the POST-backup discriminator (must be GONE after the restore) ############
|
||||
post-backup file sha256: ef0ae720c3d2649a1340e66644273f22b3c3d1fadea7e6fc3eeb6156120c9996
|
||||
rows now:
|
||||
post-backup
|
||||
pre-backup
|
||||
storage now:
|
||||
csak-mentes-utan.txt
|
||||
\303\201rv\303\255zt\305\261r\305\221\ t\303\274k\303\266rf\303\272r\303\263g\303\251p.txt
|
||||
|
||||
############ PHASE 4 — DESTROY the app's live data ############
|
||||
--- volumes remaining (positive observable of destruction):
|
||||
docmost_docmost_postgres_data
|
||||
docmost_docmost_redis_data
|
||||
docmost_docmost_storage
|
||||
--- PROOF OF LOSS through the observable, not the filesystem:
|
||||
the app's discriminator table is now: 2
|
||||
the accented file is now: csak-mentes-utan.txt
|
||||
\303\201rv\303\255zt\305\261r\305\221\ t\303\274k\303\266rf\303\272r\303\263g\303\251p.txt
|
||||
/var/lib/docker/volumes/docmost_docmost_storage
|
||||
|
||||
############ PHASE 5 — move the PRIMARY recovery unit ASIDE ############
|
||||
--- primary unit present?
|
||||
ls: cannot access '/mnt/sys_drive/felhom-data/backups/primary/docmost': No such file or directory
|
||||
--- what remains under backups/primary/:
|
||||
bookstack
|
||||
docmost.ASIDE-r102
|
||||
kimai
|
||||
opengist
|
||||
paperless
|
||||
privatebin
|
||||
--- the mirror is untouched:
|
||||
88f21f491d0766aa7a1fc9eba5866e5fffd7a72fa640c55f7bccf575f2ba751d /mnt/felhom-drives/hdd_1/backups/secondary/docmost/recovery-unit/volume-dumps/docmost_docmost_storage.tar
|
||||
9f676376f759733f5b62e590e4a2b31dddd66ff49990df3394332b790a092a28 /mnt/felhom-drives/hdd_1/backups/secondary/docmost/recovery-unit/db-dumps/docmost-postgres.sql
|
||||
@@ -0,0 +1,32 @@
|
||||
############ PHASE 4 (redo) — DESTROY the app's live data, for real ############
|
||||
UTC 2026-08-31T10:01:46Z
|
||||
NOTE: the first attempt used 'docker volume rm', which docker REFUSED because the stopped
|
||||
containers still referenced the volumes. It printed nothing and destroyed nothing.
|
||||
An unchecked exit code that looks like success is exactly the trap this drill tests for.
|
||||
|
||||
docmost Exited (1) 42 seconds ago
|
||||
docmost-redis Exited (0) 42 seconds ago
|
||||
docmost-postgres Exited (0) 29 seconds ago
|
||||
--- sizes BEFORE the wipe
|
||||
68989735 /var/lib/docker/volumes/docmost_docmost_postgres_data/_data
|
||||
128 /var/lib/docker/volumes/docmost_docmost_storage/_data
|
||||
49419992 /var/lib/docker/volumes/docmost_docmost_redis_data/_data
|
||||
wiped docmost_docmost_postgres_data -> entries remaining: 0
|
||||
wiped docmost_docmost_redis_data -> entries remaining: 0
|
||||
wiped docmost_docmost_storage -> entries remaining: 0
|
||||
--- sizes AFTER the wipe
|
||||
0 /var/lib/docker/volumes/docmost_docmost_postgres_data/_data
|
||||
0 /var/lib/docker/volumes/docmost_docmost_storage/_data
|
||||
0 /var/lib/docker/volumes/docmost_docmost_redis_data/_data
|
||||
|
||||
--- PROOF OF LOSS through the OBSERVABLE, not the filesystem
|
||||
1) the accented file:
|
||||
(empty listing above = the file is gone)
|
||||
2) the app's own database — start the DB container and ask it for the rows:
|
||||
ERROR: relation "felhom_r102_discriminator" does not exist
|
||||
LINE 1: SELECT count(*) FROM felhom_r102_discriminator;
|
||||
^
|
||||
^ an error or an empty database IS the proof: the rows cannot be read any more.
|
||||
|
||||
--- the PRIMARY unit is still aside:
|
||||
ls: cannot access '/mnt/sys_drive/felhom-data/backups/primary/docmost': No such file or directory
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
############ PHASE 6 — the NEW Tier-2 unit restore, through the real endpoint ############
|
||||
UTC 2026-08-31T10:02:28Z
|
||||
Endpoint: POST /backup/tier2/unit-restore (the exact route the row's button posts to)
|
||||
Method: endpoint-level (no browser on DooPlex) — session cookie + session CSRF, as the UI sends.
|
||||
|
||||
--- the surface's own answer BEFORE the press: is the action offered for docmost?
|
||||
action="/backup/tier2/unit-restore"
|
||||
<button type="submit" class="btn btn-xs btn-danger-outline" data-confirm="Ez a művelet FELÜLÍRJA az alkalmazás jelenlegi adatait – az adatbázisát és a belső köteteit is – a második meghajtón lévő másolattal. Ami a másolat óta keletkezett, elveszik. A másolat kelte: 2026-08-31 12:00. A mellette lévő „Fájlok visszaállítása” ezzel szemben csak a hiányzó fájlokat pótolja, és semmit nem ír felül. Az alkalmazás a művelet idejére leáll.">Teljes visszaállítás a másolatból</button>
|
||||
--
|
||||
|
||||
--- POST
|
||||
302 https://127.0.0.1:443/backups/apps?flash=Teljes+vissza%C3%A1ll%C3%ADt%C3%A1s+elindult+%E2%80%94+az+%C3%A1llapot+itt+friss%C3%BCl.
|
||||
|
||||
--- waiting for the async restore to publish its result...
|
||||
{"ok":true,"data":{"running":false,"op":"tier2-unit-restore","stack":"docmost","started_at":"2026-08-31T10:02:28.860491563Z","last":{"op":"tier2-unit-restore","stack":"docmost","ok":true,"message":"A(z) docmost: 3 adatkötet és az adatbázis visszaállítva — az alkalmazás újraindult. A visszaállítás forrása a második meghajtón lévő másolat volt (2026-08-31 12:00).","finished_at":"2026-08-31T10:02:57.511163864Z"},"last_recent":true}}
|
||||
|
||||
--- controller log for the restore
|
||||
2026/08/31 09:52:59 backup.go:1077: [INFO] [backup] Found 13 DB dump files across drives
|
||||
2026/08/31 09:57:59 backup.go:1077: [INFO] [backup] Found 13 DB dump files across drives
|
||||
2026/08/31 10:00:07 tier2.go:446: [INFO] [backup] Tier 2 run complete: 8 app(s) processed (incl. volume-only — F6)
|
||||
2026/08/31 10:02:28 handlers.go:1842: [WARN] [web] Tier-2 UNIT restore requested (async, OVERWRITES live data): stack=docmost from 172.18.0.4:33772
|
||||
2026/08/31 10:02:28 tier2_restore.go:180: [WARN] [backup] Tier-2 UNIT restore for docmost from the secondary mirror /mnt/felhom-drives/hdd_1/backups/secondary/docmost/recovery-unit — this OVERWRITES live app data
|
||||
2026/08/31 10:02:28 restore_unit.go:313: [INFO] [backup] Restoring docmost from recovery unit /mnt/felhom-drives/hdd_1/backups/secondary/docmost/recovery-unit: images=3, secrets recovered=2/2, data_keys=0
|
||||
2026/08/31 10:02:29 restore.go:148: [INFO] [backup] Restoring Docker volume docmost_docmost_postgres_data for docmost
|
||||
2026/08/31 10:02:30 restore.go:177: [DEBUG] [backup] Volume docmost_docmost_postgres_data restored successfully
|
||||
2026/08/31 10:02:30 restore.go:148: [INFO] [backup] Restoring Docker volume docmost_docmost_redis_data for docmost
|
||||
2026/08/31 10:02:30 restore.go:177: [DEBUG] [backup] Volume docmost_docmost_redis_data restored successfully
|
||||
2026/08/31 10:02:30 restore.go:148: [INFO] [backup] Restoring Docker volume docmost_docmost_storage for docmost
|
||||
2026/08/31 10:02:31 restore.go:177: [DEBUG] [backup] Volume docmost_docmost_storage restored successfully
|
||||
2026/08/31 10:02:31 restore.go:182: [INFO] [backup] Restored 3 Docker volume(s) for docmost
|
||||
2026/08/31 10:02:31 restore_db.go:77: [INFO] [backup] Restore docmost: replaying DB dump into docmost-postgres (postgres)
|
||||
2026/08/31 10:02:33 dbdump.go:799: [INFO] [backup] Imported DB dump docmost-postgres.sql into docmost-postgres (postgres)
|
||||
2026/08/31 10:02:33 restore_db.go:87: [INFO] [backup] Restore docmost: replayed 1 DB dump(s)
|
||||
2026/08/31 10:02:57 restore_unit.go:391: [INFO] [backup] Restore-from-unit completed: docmost — 3 volume(s) of 3 listed, 1 database(s) of 1 listed
|
||||
2026/08/31 10:02:57 handlers.go:1852: [INFO] [web] Tier-2 unit restore completed (async): stack=docmost in 28.650568445s (volumes 3/3, dbs 1/1)
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
############ PHASE 7 — prove the data came back, THROUGH THE APP ############
|
||||
UTC 2026-08-31T10:03:32Z
|
||||
--- docmost stack state
|
||||
docmost Up 48 seconds (healthy)
|
||||
docmost-redis Up 58 seconds (healthy)
|
||||
docmost-postgres Up About a minute (healthy)
|
||||
|
||||
--- 7a. the accented file: name bytes as HEX (R-364 — never judged as rendered text)
|
||||
entries in the app's storage root: 1
|
||||
name_hex : c3817276c3ad7a74c5b172c5912074c3bc6bc3b67266c3ba72c3b367c3a9702e747874
|
||||
name_render: Árvíztűrő tükörfúrógép.txt
|
||||
sha256 : 9228fddade66a05449b77afaf66645b1c956fa5d208c9d1a972169b17623c444
|
||||
|
||||
PLANTED accented name present, byte-for-byte : True
|
||||
content byte-identical to the planted bytes : True
|
||||
POST-backup file 'csak-mentes-utan.txt' GONE : True
|
||||
|
||||
--- 7b. the DATABASE, read by a client on the app's OWN network with the app's OWN credential
|
||||
docmost@172.20.0.2/32:5432
|
||||
pre-backup | R-102 drill: this row is IN the Tier-2 mirror and MUST come back
|
||||
|
||||
^ current_user + inet_server_addr proves it is the APP's role over TCP, not a local socket.
|
||||
|
||||
--- 7c. the app's own tables survived the replay (a count, not a claim about the app)
|
||||
44
|
||||
|
||||
--- 7d. docmost answers over HTTP (its own interface is up)
|
||||
docmost HTTP 200
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
############ PHASE 8 — SCENARIO D: the guest's app.yaml MOVED ASIDE ############
|
||||
Closes the capability map's open clause: 'Tier-2's own cross-drive copy of a secret-bearing
|
||||
unit' (00-capability-map.md, the D5 row) was unit-tested only.
|
||||
UTC 2026-08-31T10:04:22Z
|
||||
|
||||
--- 8a. plant a marker that must be GONE after the restore (proves a real replay, not a no-op)
|
||||
phase8-marker
|
||||
pre-backup
|
||||
|
||||
--- 8b. move the GUEST's app.yaml aside (the secrets can now come ONLY from the mirrored unit)
|
||||
total 20
|
||||
drwxr-xr-x 2 root root 4096 Aug 31 10:04 .
|
||||
drwxr-xr-x 58 root root 4096 Aug 21 16:00 ..
|
||||
-rw-r--r-- 1 root root 2332 Aug 31 10:02 .felhom.yml
|
||||
-rw------- 1 root root 498 Aug 31 10:02 app.yaml.ASIDE-r102
|
||||
-rw-r--r-- 1 root root 3105 Aug 31 10:02 docker-compose.yml
|
||||
|
||||
--- 8c. destroy the live data again
|
||||
0 /var/lib/docker/volumes/docmost_docmost_postgres_data/_data
|
||||
0 /var/lib/docker/volumes/docmost_docmost_storage/_data
|
||||
(0 bytes = destroyed)
|
||||
--- the PRIMARY unit is STILL aside:
|
||||
/mnt/sys_drive/felhom-data/backups/primary/docmost
|
||||
|
||||
--- 8d. restore from the MIRROR, through the real endpoint
|
||||
302 https://127.0.0.1:443/backups/apps?flash=Teljes+vissza%C3%A1ll%C3%ADt%C3%A1s+elindult+%E2%80%94+az+%C3%A1llapot+itt+friss%C3%BCl.
|
||||
{"ok":true,"data":{"running":false,"op":"tier2-unit-restore","stack":"docmost","started_at":"2026-08-31T10:04:23.364841193Z","last":{"op":"tier2-unit-restore","stack":"docmost","ok":true,"message":"A(z) docmost: 3 adatkötet és az adatbázis visszaállítva — az alkalmazás újraindult. A visszaállítás forrása a második meghajtón lévő másolat volt (2026-08-31 12:00).","finished_at":"2026-08-31T10:04:51.894947224Z"},"last_recent":true}}
|
||||
|
||||
--- 8e. SECRETS RECOVERED (the line that closes the clause)
|
||||
2026/08/31 10:02:28 restore_unit.go:313: [INFO] [backup] Restoring docmost from recovery unit /mnt/felhom-drives/hdd_1/backups/secondary/docmost/recovery-unit: images=3, secrets recovered=2/2, data_keys=0
|
||||
2026/08/31 10:02:57 restore_unit.go:391: [INFO] [backup] Restore-from-unit completed: docmost — 3 volume(s) of 3 listed, 1 database(s) of 1 listed
|
||||
2026/08/31 10:04:23 restore_unit.go:313: [INFO] [backup] Restoring docmost from recovery unit /mnt/felhom-drives/hdd_1/backups/secondary/docmost/recovery-unit: images=3, secrets recovered=2/2, data_keys=0
|
||||
2026/08/31 10:04:51 restore_unit.go:391: [INFO] [backup] Restore-from-unit completed: docmost — 3 volume(s) of 3 listed, 1 database(s) of 1 listed
|
||||
|
||||
--- 8f. the guest's app.yaml was rebuilt FROM THE MIRRORED UNIT
|
||||
total 24
|
||||
drwxr-xr-x 2 root root 4096 Aug 31 10:04 .
|
||||
drwxr-xr-x 58 root root 4096 Aug 21 16:00 ..
|
||||
-rw-r--r-- 1 root root 2332 Aug 31 10:04 .felhom.yml
|
||||
-rw------- 1 root root 498 Aug 31 10:04 app.yaml
|
||||
-rw------- 1 root root 498 Aug 31 10:02 app.yaml.ASIDE-r102
|
||||
-rw-r--r-- 1 root root 3105 Aug 31 10:04 docker-compose.yml
|
||||
(app.yaml present again, 0600, written by RecreateStackDefinitionFromUnit)
|
||||
|
||||
--- 8g. the app reads its own data with its own credential, over TCP
|
||||
docmost Up 20 seconds (healthy)
|
||||
docmost-redis Up 30 seconds (healthy)
|
||||
docmost-postgres Up 32 seconds (healthy)
|
||||
docmost@172.20.0.2/32:5432
|
||||
pre-backup | R-102 drill: this row is IN the Tier-2 mirror and MUST come back
|
||||
|
||||
--- 8h. the accented file, again as HEX
|
||||
entries: ['Árvíztűrő tükörfúrógép.txt']
|
||||
accented name byte-for-byte: True
|
||||
content byte-identical : True
|
||||
|
||||
--- 8i. docmost's own HTTP interface
|
||||
docmost HTTP 200
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
############ PHASE 9 — put it back, and prove the ORDINARY path still works ############
|
||||
UTC 2026-08-31T10:05:32Z
|
||||
--- 9a. restore the PRIMARY unit and remove the app.yaml set-aside
|
||||
primary unit restored: compose docmost.ASIDE-r102 manifest.json
|
||||
app.yaml set-aside removed (the live app.yaml is the one the restore rebuilt)
|
||||
bookstack
|
||||
docmost
|
||||
kimai
|
||||
opengist
|
||||
paperless
|
||||
privatebin
|
||||
|
||||
--- 9b. R-103 live: the FILE restore now points at the action beside it, not another page
|
||||
302 https://127.0.0.1:443/backups/apps?flash_error=Ennek+az+alkalmaz%C3%A1snak+az+adatai+nem+f%C3%A1jlokban%2C+hanem+az+alkalmaz%C3%A1s+saj%C3%A1t+adatb%C3%A1zis%C3%A1ban+%C3%A9s+k%C3%B6teteiben+vannak+%E2%80%94+az+alkalmaz%C3%A1s+nem+%C3%A1llt+le.+Ezeket+a+mellette+l%C3%A9v%C5%91+%E2%80%9ETeljes+vissza%C3%A1ll%C3%ADt%C3%A1s+a+m%C3%A1solatb%C3%B3l%E2%80%9D+gombbal+tudod+visszahozni+ugyanerr%C5%91l+a+m%C3%A1solatr%C3%B3l.+Figyelem%3A+az+a+m%C5%B1velet+FEL%C3%9CL%C3%8DRJA+a+jelenlegi+adatokat%2C+m%C3%ADg+ez+a+gomb+csak+a+hi%C3%A1nyz%C3%B3+f%C3%A1jlokat+p%C3%B3tolja.
|
||||
|
||||
--- 9c. R-102/R-103 live: an app whose Tier-2 copy has NO unit is refused (Scenario G shape)
|
||||
(calibre-web's copy is on the SSD, state-only — checking what the surface offers per app)
|
||||
8
|
||||
^ apps offering the destructive action
|
||||
|
||||
--- 9d. destroy again, then the ORDINARY PRIMARY restore (POST /backup/restore)
|
||||
destroyed: 0 /var/lib/docker/volumes/docmost_docmost_postgres_data/_data
|
||||
302 https://127.0.0.1:443/backups/restore?flash=Vissza%C3%A1ll%C3%ADt%C3%A1s+elindult+%E2%80%94+az+%C3%A1llapot+itt+friss%C3%BCl.
|
||||
{"ok":true,"data":{"running":false,"op":"restore","stack":"docmost","started_at":"2026-08-31T10:05:33.081614501Z","last":{"op":"restore","stack":"docmost","ok":true,"message":"A(z) docmost: a beállítások visszaálltak — az alkalmazás újraindult. FIGYELEM: ez a mentés csak a beállításokat tartalmazta, adatot nem. Az alkalmazás adatai NEM álltak vissza ebből a mentésből.","finished_at":"2026-08-31T10:05:57.630738384Z"},"last_recent":true}}
|
||||
|
||||
--- 9e. which unit did the ORDINARY restore read?
|
||||
2026/08/31 10:02:28 restore_unit.go:313: [INFO] [backup] Restoring docmost from recovery unit /mnt/felhom-drives/hdd_1/backups/secondary/docmost/recovery-unit: images=3, secrets recovered=2/2, data_keys=0
|
||||
2026/08/31 10:02:57 restore_unit.go:391: [INFO] [backup] Restore-from-unit completed: docmost — 3 volume(s) of 3 listed, 1 database(s) of 1 listed
|
||||
2026/08/31 10:04:23 restore_unit.go:313: [INFO] [backup] Restoring docmost from recovery unit /mnt/felhom-drives/hdd_1/backups/secondary/docmost/recovery-unit: images=3, secrets recovered=2/2, data_keys=0
|
||||
2026/08/31 10:04:51 restore_unit.go:391: [INFO] [backup] Restore-from-unit completed: docmost — 3 volume(s) of 3 listed, 1 database(s) of 1 listed
|
||||
2026/08/31 10:05:33 restore_unit.go:313: [INFO] [backup] Restoring docmost from recovery unit /mnt/sys_drive/felhom-data/backups/primary/docmost: images=3, secrets recovered=2/2, data_keys=0
|
||||
2026/08/31 10:05:57 restore_unit.go:391: [INFO] [backup] Restore-from-unit completed: docmost — 0 volume(s) of 0 listed, 0 database(s) of 0 listed
|
||||
|
||||
--- 9f. final state
|
||||
docmost Up 14 seconds (healthy)
|
||||
docmost-postgres Up 24 seconds (healthy)
|
||||
docmost-redis Up 24 seconds (healthy)
|
||||
ERROR: relation "felhom_r102_discriminator" does not exist
|
||||
LINE 1: select id||chr(32)||chr(124)||chr(32)||note from felhom_r102...
|
||||
^
|
||||
accented file back from the PRIMARY unit: False
|
||||
docmost HTTP 200
|
||||
Reference in New Issue
Block a user