# Windows Snapshot Import ## Current Status As of 2026-09-16, source capture succeeded, but the target import failed during maintenance cleanup and was manually interrupted. The destination remains **failed, unmarked and stopped**, not verified-stopped or ready for normal run. The user then explicitly authorized removal of copied SQLite databases, backups, `found_secrets` outputs and archival files, preserving Windows originals. **The staged snapshot is now intentionally incomplete: `files.tar` was deleted.** Its retained manifest describes the original capture, not the reduced target. Do not rewrite the manifest, retry import, restart the retained container, or recapture/repopulate the removed copies automatically. The procedures below describe the original full-snapshot workflow, not a resume procedure for this pruned destination. Any future recovery needs a separately reviewed plan. | Execution evidence | Result | | --- | --- | | Source snapshot publication | Manifest published 2026-09-15T19:36:28.010915+00:00; source supervisor/PG stopped flags true | | Approved manifest SHA-256 | `08344147133c37d4b6f404cf4fac3e59d58f94917f1fa58a77cbb68c36db7e8a` | | Original capture inventory | 50,501 files, 34,851,776,467 bytes; 49,897 active and 604 archival files; 61 tables and 38 sequences | | Retained PostgreSQL dump | `database.dump`, 2,619,119,892 bytes; not deleted or modified by cleanup | | Failed import container | `63286fd554f832fd3a1f073e7c923977e209149e940b684cdbf35e4479ebd5ba`; exited 129, PID 0, restarts 0, restart policy `no` | | Pinned runtime/cleanup image | `sha256:ecf1ee044fd6e936359a5955e0a42b452b8098a3f9d822272ab373b697761de2` | | Cleanup verification | 635 original Windows files checked for presence/size and unchanged metadata; original PG control hashes unchanged; Windows `postgres.exe` count 0 | | Retained destination verification | Metadata of 49,866 remaining inventory files and 1,883 PG files unchanged; PG control/config hashes unchanged; initialized marker absent; application remains stopped | | Final verified-stopped acceptance | NOT ACHIEVED; cleanup does not repair the failed import | ### Authorized Copy Cleanup Only these copied locations were removed on 2026-09-16: | Copied location/family | Files | Bytes removed | | --- | ---: | ---: | | `/data/windows-archive` including old SQLite backups and archived configurations | 604 | 10,827,425,254 | | `/data/runtime-linux/results/scanner*.db` and associated WAL/SHM/journal files | 11 | 10,281,779,360 | | `/data/runtime-linux/results/found_secrets.*`, including generations, manifest and publication ledger | 20 | 5,584,368,562 | | Total from native `truf-docker_data` volume | 635 | 26,693,573,176 | | Completed staging directory's `files.tar` on Windows D: | 1 | 34,917,959,680 | Original `D:\truf`, `S:\postgres-data` and source bundles were not deleted or modified. Target PostgreSQL, its dump, translated configuration, credentials, proxies, queues, other result streams, bundles, caches and their required publication ledgers were retained. The one-off cleanup used a network-disabled utility container with only the verified native target volume writable; it did not run PostgreSQL, the importer, scanners, providers or application services. The volume gained approximately 26.69 GB of filesystem free space. Approximately 34.92 GB was freed on Windows D:. This did not compact the WSL VHDX on S: or return all newly free ext4 blocks to the Windows host; S: reported 30,467,690,496 bytes free after cleanup. No WSL/storage reconfiguration was done. Removing `found_secrets` files does not reset PostgreSQL projector cursors or pending append/rotation proofs. A future authorized startup must first address that projection state explicitly; removing files or their SQLite ledger alone is not a safe live-cursor reset. Do not erase PostgreSQL findings, counters or pipeline evidence to make the removed files appear consistent. Reported regression evidence, not rerun by this documentation change: selected Docker suite **663 passed, 7 Windows-only skipped**; synthetic snapshot tests **58 passed**; pure config tests **12 passed**; host importer tests **66 passed, 1 POSIX-only skipped**. None is proof of this snapshot's capture or import E2E. ## Scope And Paths The authorized operation copies the original logical database, proxies, secrets and reviewed durable files. It does not move/delete the originals, migrate the source schema, or execute providers, scanners, keycheckers or archived scripts. Only exclusive PostgreSQL maintenance is allowed during capture/import. Leave both the original supervisor and original PostgreSQL stopped after capture, and the destination stopped after import. Current private staging directory: - Windows: `D:\truf-docker\docker\imports\windows-20260915-59a1c0aa23ec411b86f25c5eb9d2a4d3` - WSL: `/mnt/d/truf-docker/docker/imports/windows-20260915-59a1c0aa23ec411b86f25c5eb9d2a4d3` - Container: the same directory bound read-only at `/import`. A completed snapshot contains exactly `manifest.json`, `files.tar` and `database.dump`. Do not add reports or other files inside it. Windows staging remains private to the capturing account and SYSTEM; preserve its ACLs rather than making it world-readable for Docker. `docker/imports/` is excluded from Git and the image build context. Never put dump/tar contents, credentials, proxy values, application data or raw logs in Git, images or terminal output. The directory listed above currently retains only `manifest.json` and `database.dump` after the authorized cleanup. It is not a completed import input. The destination is the base Compose native Linux volume `truf-docker_data`, mounted at `/data`, not a Windows bind mount. Paths in braces below are reviewed families; optional archival inputs are copied only when present. | Original source | Destination within `/data` | | --- | --- | | `S:\postgres-data` via a full PG16 logical dump | `/data/postgres-linux`, independently initialized native Linux PG16 | | `D:\truf\runtime\{results,queues,state,keychecks,postman_cache,result_spool}` | `/data/runtime-linux/{results,queues,state,keychecks,postman_cache,result_spool}` | | `D:\truf\runtime\proxy.txt` | `/data/runtime-linux/proxy.txt` | | `D:\truf\app\{secrets.yaml,trufflehog-custom-detectors.yaml}` | `/data/config/{secrets.yaml,trufflehog-custom-detectors.yaml}` | | `D:\truf\app\config.yaml` | `/data/windows-archive/app/config.yaml`; translated profile at `/data/config/windows-import.yaml` | | `S:\scanner-result-bundles\{tmp,ready,quarantine}` | `/data/scanner-result-bundles/{tmp,ready,quarantine}` | | Reviewed archival inputs under `D:\truf` | `/data/windows-archive/` with their original relative paths | Archival scope includes `D:\truf\state`, `runtime\backups`, `runtime\imports`, non-authority JSON reports from `runtime\control`, `app\.streamlit\config.toml`, `.env.postgres`, `docker-compose.postgres.yml`, `runner_state.json`, `runtime\keychecks.7z`, `runtime\orkey.txt`, `runtime\check-openrouter-keys.ps1`, `runtime\*.md`, root `checked_*.txt`/`todo_*.txt`, root/app `scanner.db*`, app `config.yaml.*`/`secrets.yaml.*`, root result projection families and `*.publication-ledger.sqlite3*`. The legacy copy tree is also archival: `D:\truf\runtime\keychecks \u2014 \u043a\u043e\u043f\u0438\u044f` (Unicode escapes describe the actual folder name, not a literal shell path). Within `runtime\state`, `scan_limiter*.db*`, `*.tmp*` and `janitor.cursor.json` are archival only, never active Linux authority/state. Excluded: physical PGDATA/WAL, Windows PostgreSQL binaries/logs, live control authority, locks/PIDs, `S:\scanner-work`, `runtime\downloads`, runtime git/traces/ freeze-diagnostics, `gharchive_cache`, `.git`, `.opencode`, tests and code caches. Ordinary logs are excluded outside retained result/keycheck projection families; `scan_errors.log*` is deliberately durable data, not a diagnostic to display. The manifest records the actual selected inventory and exclusion counts. The archived `.env.postgres` is never sourced or used for the target connection. Provision generates `/data/postgres-password`; Linux uses this new password and a different PG16 system identifier, not the source password or physical cluster. Archived Windows configs are not executable runtime profiles. ## Capture And Capacity Gates Main runs `D:\truf-docker\docker\windows_snapshot.py` using native PowerShell, not context-mode or another Windows Job wrapper: original PostgreSQL correctly refuses Job membership. The exporter temporarily starts only source maintenance PostgreSQL and must confirm its stop before publication. Do not rerun capture into the current attempt directory, reuse partial files, or kill a process that is retaining authority while stop remains unconfirmed. After capture exits 0 and publishes its final manifest, main records its digest from the trusted Windows path, then checks that the WSL-visible manifest has the same digest. Do not replace the approved pin with a newly computed digest merely to bypass a mismatch. Native PowerShell digest command: ```powershell (Get-FileHash -LiteralPath 'D:\truf-docker\docker\imports\windows-20260915-59a1c0aa23ec411b86f25c5eb9d2a4d3\manifest.json' -Algorithm SHA256).Hash.ToLowerInvariant() ``` Check Windows `D:` staging capacity and, independently, physical free space on `S:`, which backs the Docker/WSL VHDX, and free space on native Linux `/data`. Record the actual VHDX/daemon storage location; a large Linux `df` result does not prove the Windows host can grow the VHDX. Budget its anticipated growth while retaining at least **20 GiB physical free on S:**. The importer cannot measure or enforce this host-side reserve. The Linux preflight requires `file_bytes + database_bytes + 20 GiB` free, using source physical database size from manifest metadata. Older v1 metadata without that size uses `max(24 GiB, 4 * dump_bytes)` as the database estimate. At least **20 GiB must still be free on Linux after import**. Check both host and guest capacity during and after restoration; compressed dump size alone is not a capacity estimate. Do not delete original data to make space. ## Offline Procedure Run these steps separately in WSL Bash only after main approves the capture and capacity evidence. Use the existing Linux Docker daemon and already-built, importer-integrated `truf-local:runtime` image; no builds or pulls here. Keep the fixed project/directory below. Do not use the generic initialize/start procedure in `DOCKER_MIGRATION.md` for this full-schema snapshot. ```bash TRUF_WINDOWS_SNAPSHOT='/mnt/d/truf-docker/docker/imports/windows-20260915-59a1c0aa23ec411b86f25c5eb9d2a4d3' TRUF_WINDOWS_SNAPSHOT_SHA256='PENDING' dci() { if [[ ! "${TRUF_WINDOWS_SNAPSHOT_SHA256:-}" =~ ^[0-9a-f]{64}$ ]]; then printf '%s\n' 'STOP: set the approved lowercase manifest SHA-256.' >&2 return 1 fi sudo -n env \ TRUF_WINDOWS_SNAPSHOT="${TRUF_WINDOWS_SNAPSHOT:?Set the completed snapshot directory}" \ TRUF_WINDOWS_SNAPSHOT_SHA256="$TRUF_WINDOWS_SNAPSHOT_SHA256" \ docker compose \ --project-name truf-docker \ --project-directory /mnt/d/truf-docker \ --env-file /dev/null \ --file /mnt/d/truf-docker/compose.yaml \ --file /mnt/d/truf-docker/compose.snapshot-import.yaml \ "$@" } sha256sum "$TRUF_WINDOWS_SNAPSHOT/manifest.json" dci config --quiet sudo -n docker image inspect --format '{{.Id}}' truf-local:runtime sudo -n docker volume inspect --format '{{.Name}} {{.Driver}} {{.Mountpoint}}' truf-docker_data sudo -n docker container inspect --format '{{.State.Status}}' truf-docker-snapshot-import ``` Replace `PENDING` with the previously approved pin, not a credential. The `sudo -n env NAME=value ... docker compose` form explicitly passes the two non-secret interpolation inputs even when sudo strips shell exports. Do not use `sudo -E` or pass source connection/provider variables. `--env-file /dev/null` prevents implicit checkout `.env` loading, but does not sanitize shell exports; use a clean operator shell and no unreviewed Docker/Compose overrides. Main must separately confirm the image identity, **absence** of `truf-docker_data`, and absence of the retained import container name before provisioning. Only specific no-such-volume/no-such-container responses establish absence; daemon/permission errors do not. If either already exists, stop for review instead of adopting, overwriting or deleting it. ```bash dci run --rm --no-deps --pull never -T provision ``` Proceed only after successful provision. This unchanged base service creates the private layout, generated password and empty placeholders, not an application schema. **Do not call `initialize`, `import-secrets`, or normal `run` first.** The importer uses `initialize-empty` internally and restores the entire custom dump into a virgin schema before raw table/sequence comparison and permitted target-only recovery/migrations. ```bash dci run --detach --no-deps --pull never -T \ --name truf-docker-snapshot-import runtime ``` This is the single retained maintenance container: no `--rm`, no automatic restart, no dependency startup, no healthcheck and `network_mode: none`. The override preserves the base image/entrypoint, non-root UID/GID, read-only rootfs, capabilities/security policy, native `/data` volume, tmpfs and resource limits. It adds only read-only `/import`; `create_host_path: false` rejects a missing source directory rather than silently creating one. Import starts with `/opt/truf/app/config.linux.yaml` in both the environment and explicit `--config`. **Do not merge `compose.windows-import.yaml` here.** The translated private profile does not exist at initial preflight; the importer creates and selects it internally only after validating/extracting the snapshot. ## Stopped Verification ```bash sudo -n docker container wait truf-docker-snapshot-import sudo -n docker container inspect --format \ 'status={{.State.Status}} exit={{.State.ExitCode}} oom={{.State.OOMKilled}} restarts={{.RestartCount}} network={{.HostConfig.NetworkMode}} restart={{.HostConfig.RestartPolicy.Name}} auto_remove={{.HostConfig.AutoRemove}}' \ truf-docker-snapshot-import ``` Require `status=exited exit=0 oom=false restarts=0 network=none restart=no auto_remove=false`. `wait` prints the container exit code; the command's own exit status alone is not import success. Waiting may take hours or hold while maintenance stop is uncertain. Do not impose a timeout that kills the container. Do not display raw `docker logs`, Compose logs, database logs, full environment dumps or application data. Only structured importer numeric phase/count/byte events and allowlisted aggregate/hash evidence are suitable for progress. Phase 13 is emitted before final publication and is not success proof. Main must privately inspect the following evidence from the stopped retained container, for example with `docker cp` into a separate owner-only evidence directory outside `/import`. Do not start another runtime to inspect it; `health` and `status` are live readiness actions, not stopped-import verification. - `/data/config/windows-import-manifest.json`: its byte SHA-256 equals the approved staging manifest pin; source stopped flags are true. The raw evidence, report and initialized marker all carry that same `manifest_sha256`. Report archive/dump hashes match this manifest and the verified staging files. - `/data/config/windows-import-raw.json`: its SHA-256 matches report `raw_evidence_sha256`; `table_counts` equals manifest `database.table_counts`, `sequences_provided` is true and `sequences_verified` equals manifest `database.sequence_count`. The importer checks actual sequence values before transformations; this evidence records their verified count, not their values. Record only aggregate tables/rows/sequences. - `/data/config/windows-import.yaml`: hash bytes without displaying values; SHA-256 matches report `config_sha256`. - `/data/config/windows-import-report.json`: `status` is `verified-stopped`, `maintenance_stopped` is true, all `pipeline_after` counts are zero, final Linux reserve is at least 20 GiB, and cutover/migration/preserved-evidence checks succeeded. Record any reported fenced recovery or Postman rebasing; these may legitimately change final target counts or move incoming tmp/ready bundles after raw comparison. - `/data/initialized.json`: exists as the last publication, has format `truf-container-data-v1`, `pg_major` 16 and the approved `manifest_sha256`; `import_report_sha256` matches the actual report bytes. Its system identifier equals report `linux_system_identifier` and differs from the source identifier. - Confirm destination `/data/postgres-linux/postmaster.pid` is absent, original supervisor/PostgreSQL remain stopped, and host/guest space reserves still hold. Record all results in the PENDING table before declaring verified-stopped. ## Failure And Release Any nonzero exit, OOM, missing/mismatched evidence or uncertain stop is not verified-stopped. Retain the container, volume and private snapshot. A partial import is failed/unmarked, not automatically resumable; early rejection can leave no report. A report saying verified-stopped without a matching final initialized marker and clean container exit is still insufficient. Do not automatically retry/restart, overwrite, delete, remove locks/markers, initialize, prune, run `down --volumes`, or force-kill an authority-holding importer. Review the retained state first; any new attempt needs an explicit decision and separately approved fresh destination, not cleanup by this runbook. There is **no automatic normal run**. A future live run requires separate user authorization after verified-stopped acceptance. Only then use base `compose.yaml` plus `compose.windows-import.yaml`, without the snapshot override, so runtime, health and status all select `/data/config/windows-import.yaml`. Do not start either the original or destination supervisor as part of import.