No release, deploy or publish. Off-site is OFF for Tester4 and the owner passphrase is needed from the operator (bind blocked after 2 of 5 tries).
5.1 KiB
C1 reviewer 1 — restore and rollback (fresh, read-only; clones at controller 7ca59e2 / v0.336.0)
Raw candidates as returned by the reviewer, condensed; each is UNVERIFIED until C2 marks it. File:line are in the controller unless named.
| # | Sev guess | harm | mechanism |
|---|---|---|---|
| R1 | P2 high | A local / second-drive unit restore that fails halfway restarts the app on half-replaced data, no undo copy; the next night's capture then overwrites the only restore point with the broken state | internal/backup/restore_unit.go:429-431 failed stop only warns; restore.go:217-218 docker volume rm -f error ignored before untar (10-min limit :237-241); restore_unit.go:466-486 DB replay failure → app started anyway; one restore point per app, promoted in place (atomicPromoteTar) |
| R2 | P2 high | A power cut / controller restart during a restore starts the app on a mixed state; the undo copy is never used at boot | offbox_reconstitute.go:942 marker → appstop_marker.go:307 StartStack with no restore check; offbox_restore_undo.go:137 undo dir only dropped, never read at start; unit restore and .fab import set no marker → bootrecon/bootrecon.go:210-213,269 starts the app; updates have a journal (stacks/update.go:1420-1489), restores do not |
| R3 | P2 high | A cut-off shared-folder restore can be „placed" and reported as restored while files are missing; scratch deleted | shares_restore.go:138 readiness = rErr == nil && len(entries) > 0 (R-358 class, not fixed for shares); 30-min ctx web/offbox_handlers.go:716; place reports N files and deletes scratch :301; free-space gate fails open and fixed 2 GiB :107 |
| R4 | P2 high | A .fab export silently drops a whole drive folder that contains a symlink; import never sets owners |
appexport/fabplan.go:292 tar.FileInfoHeader(info,"") then os.Open+io.Copy on a symlink; export.go:714 warn-and-continue; assertBundleDataComplete refuses only when no data at all; restore.go:1157-1176 extracts only dirs/regular files, no chown |
| R5 | P2 med-high | Second-drive whole restore swaps files, then refuses the DB half, leaves the app STOPPED and says „the app is untouched" | tier2_whole.go stops + merges first, then RestoreFromRecoveryUnitAtWith whose refusals (restore_unit.go:366-371, 386-389, 413-416, 420-423) assume nothing happened; en.json:2667 „the restore did not start. The app is untouched" |
| R6 | P2 med | .fab import into a STOPPED app merges into existing volumes; the default export (stop box ticked) has no SQL dump |
appexport/restore.go:361-374 volumes removed only if running; docker cp merge :721; templates/app_data.html:221 stop default; export.go:262 stop before dumpDatabase :366; appbackup/dbdump.go:142 discovers only running containers |
| R7 | P2/P3 high | .fab import has no concurrency guard, no undo, no record |
restore.go:181-186; web/handler_export.go:312-353 no restoreOpBlocked(); RemoveStackVolumes before anything else, no copy |
| R8 | P2 med | .fab import drops the family gate, sign-up lock and version pin → a family-only app opens to anyone |
cmd/controller/main.go:3517-3530 bare AppConfig; stacks/family_gate.go:173-190 removes the gate file when FamilyGate == nil; R-773 fix only on the unit path (stacks/deploy.go:840-870) |
| R9 | P3 med | .fab import trusts the bundle's HDD_PATH; empty → relative path; older userdata overwrites newer |
restore.go:589-631 |
| R10 | P3 high | .fab DB import: plain psql without ON_ERROR_STOP; postgis not classified |
restore.go:1052-1053, :928-937 |
| R11 | P3 med | volume replace never checks the old volume is gone | restore.go:218 |
| R12 | P3 high | interrupted second-drive whole restore leaves <dst>.felhom-restoring (O_EXCL) → every retry fails „file exists" |
tier2_whole.go:86 |
| R13 | P3 design | full off-site download capped at 30 min per press; household not told to retry | web/offbox_handlers.go:455 |
| R14 | P4 med | place/full restore re-read the LATEST snapshot instead of the downloaded one | offbox_restore.go:675-679, offbox_reconstitute.go:657-661 |
Inventory drift (July → now): three more customer restore entries exist: the Tier-2 unit restore (R-102), the Tier-2 whole restore (decision 26, tier2_whole.go), the kept-data Load (kept_load.go).
Checked SAFE (reviewer): .fab path confinement (appexport/restore.go:1104-1107,1152-1155; validate.go:28-66; web/handler_export.go:385-394); off-site placement map refuses ../outside/backups/ (offbox_restore.go:603-640); R-358 marker after restic success; full off-site restore fit/safety-dump/undo checks before stop, fail closed (offbox_reconstitute.go:719-818, 909-927); Tier-1 refusals before change; main loader ON_ERROR_STOP=1 --single-transaction (appbackup/dbdump.go:775-784); Tier-2 file restore rsync --ignore-existing, never --delete; whole restore newer-wins + Lchown; NFC/NFD: no normalisation anywhere — every lane compares bytes, so the missing-only compare sees TWO names (consistent on one filesystem); agent RestoreLXC force only when asked. Not reviewed: kept_load.go, hub operator restore paths.