e0618af438
Claude-Session: https://claude.ai/code/session_01GzammAMzsJTgpQHqxwM2bC
73 lines
5.0 KiB
Markdown
73 lines
5.0 KiB
Markdown
# REPORT — v0.125.0: .fab volume path-strand data loss FIXED (IA finding 1, HIGH)
|
|
|
|
**Date:** 2026-07-13 · **Version:** controller v0.125.0 (from v0.124.0) · **MinAgent:** 0.81.0 (UNCHANGED) · **Floor:** may advance to 0.125.0 on the next train (must NOT stop at >0.124.0 without this) · Controller-only. **Disposition:** closes the v0.124.0 REPORT's finding 1.
|
|
|
|
## §3 live probe (ran FIRST, before any code)
|
|
|
|
On the drill guest: scratch volume seeded with a subdir, a file, a symlink, an empty file and a
|
|
non-root owner (1234:1234) → `docker create -v fabprobe:/vol alpine true` → `docker cp
|
|
<cid>:/vol/. -` → tar (5120 B) → volume wiped → `docker cp - <cid>:/vol` → **everything
|
|
byte-identical, symlink and uid/gid preserved**. Zero surprises; transcript in the session
|
|
record. Green light for implementation.
|
|
|
|
## What shipped (commit `466f427`)
|
|
|
|
1. **Streaming legs** — `dockerExec` seam + `withVolumeHelper` (create → fn → ALWAYS `rm -f`,
|
|
error paths included) + `exportVolumeTar` (`cp <cid>:/vol/. -` → file) and `importVolumeTar`
|
|
(file → `cp - <cid>:/vol`). Zero shared paths: correct on bare metal AND containerized.
|
|
2. **Scenario B** — volume-export failure is FATAL; `assertBundleDataComplete` blocks packaging
|
|
any bundle whose manifest claims a missing/empty tar (volumes + HDD; the HDD leg also stopped
|
|
pre-claiming subdirs before the tar succeeds).
|
|
3. **Scenario C** — `validateBundleData` runs in import step 0, BEFORE the app is stopped or any
|
|
volume removed; hollow bundles are refused with copy naming the ≤0.124.0-containerized-export
|
|
cause and stating the app is untouched. Missing-tar soft-skips in both restore legs are hard
|
|
errors now.
|
|
4. **Scenario D** — `scripts/docker_run_volume_path_gate.py`: every `"-v"` arg in non-test Go
|
|
needs an allowlist entry WITH ITS WHY. Gate output: 7 allowlisted usages, 0 violations.
|
|
**Tier-1/Tier-2 mechanism, proven by inspection:** `backup.go` (dump) and `backup/restore.go`
|
|
mount `dumpDir` — always a registered-drive namespace path (`/mnt/**`, `/opt/docker/**`),
|
|
which the golden deployment bind-mounts into the controller container at IDENTICAL paths
|
|
(container-inspect verified), so the daemon resolves them correctly; their volume-side mounts
|
|
are named volumes (daemon-side). They are NOT in the strand class; unchanged.
|
|
|
|
## Tests + the three red-proofs (run → FAIL → restored)
|
|
|
|
- **B:** `TestExport_HollowVolumeTarAbortsExport` (cp "succeeds" writing nothing → export fails
|
|
naming vol1, no .fab). Red-proof: assertion disabled → "a hollow volume tar must FAIL the
|
|
export — got success".
|
|
- **C:** `TestImport_HollowBundleRefusedBeforeDestroy` (handcrafted hollow bundle → refused;
|
|
asserts stopped=0, removedVolumes=0, zero docker calls, app not started). Red-proof: pre-flight
|
|
disabled → **`refusal happened AFTER destruction: removedVolumes=1`** — the exact pre-fix
|
|
disaster shape.
|
|
- **D:** violating `docker run -v /tmp/x:/out` line added → gate exit 1; restored → 0.
|
|
- Plus command-shape tests (create/cp/rm arg vectors pinned to the §3-probed shapes; import cp
|
|
streams stdin; helper removed on cp failure; no partial tar left). Full green gate + all seven
|
|
gates green.
|
|
|
|
## §13 live validation (0.125.0 on drill + demo, both healthy)
|
|
|
|
- **Supervised round-trip (Viktor GO), the EXACT leg that failed yesterday:** demo ActualBudget
|
|
export → browser download (`actualbudget_20260713-085217.fab`, 3762 B — the bundle now carries
|
|
the **66048-byte volume tar**; yesterday's was 2097 B with an EMPTY data/volumes) → sha256-
|
|
identical after placement on felhom-usb/exports → import → "Importálás kész" → **volume
|
|
fingerprint after == before: `ec8ea6cb…` (2 files)** → app Up (healthy) → zero leaked alpine
|
|
helpers → bundle cleaned from the drive.
|
|
- **Scenario B live:** a scratch stack whose compose declared the engine-invalid volume
|
|
`"bad vol"` (docker auto-creates *nonexistent* names, so engine-invalid is the honest live
|
|
trigger) → export failed LOUD: "Kötet mentése sikertelen: volume scratchvol_bad vol export
|
|
failed … invalid characters …" — zero bundles staged, zero leftover containers; scratch stack
|
|
removed after.
|
|
|
|
## Observations
|
|
|
|
- **Hollow-bundle asymmetry (customer-facing docs line needed):** any .fab exported by a
|
|
CONTAINERIZED controller ≤0.124.0 has empty volume data while claiming it — treat as suspect
|
|
and RE-EXPORT on ≥0.125.0. The import guard now refuses them loudly (app untouched) instead of
|
|
silently importing emptiness. Bare-metal/felhotest exports were never affected; HDD-data apps
|
|
were never affected.
|
|
- Spec §7B's "point a volume at a nonexistent volume" live construction doesn't fail under
|
|
docker (named volumes auto-create; an empty volume exports as an honest small tar — correct
|
|
behavior) — the live loud-fail used an engine-invalid name instead; the missing/empty-tar
|
|
refusal is unit-pinned with red-proof.
|
|
- The one Windows-runner nuance: none — the docker seam keeps unit tests fully offline.
|