Encrypted backup restoration lab — synthetic fixtures only Observed: 2026-09-03 What was measured ----------------- This experiment uses restic 0.19.1, Python 3.12.10, and Windows 11 amd64. Twelve generated files cover UTF-8 text, JSON, CSV, XML, ZIP, WAV, and TOML. There are no personal documents, photographs, recordings, or real credentials. This is one local test design, not a comparison or general safety rating of backup products. Development runs and the final recorded run produced the same six rows; these are not independent tests on different machines. The repository format is 2. Compression is restic's default (auto), and cache use is disabled. The script obtains a full snapshot ID from each successful backup. Restores use that fixed ID and the synthetic /source subfolder, never the special identifier "latest". Each restore destination is newly created, empty, and below the fresh lab root; --overwrite never is also set. The twelve-file intended manifest is made before the S4 exclusion is applied. The S5 required-current manifest is made before the older fixture is saved. The S6 manifest describes the deliberately invalid source, including its failed JSON parsing test, before that source is backed up. Neither an incomplete snapshot nor its own file list is treated as the intended baseline. The checks answer separate questions: * restic check: does the repository pass the normal structural check? * restic check --read-data: does reading all stored data pass? * restic restore: does extraction finish with exit code 0? * Inventory: are all expected paths present, with no unexpected files? * SHA256: do all expected restored files match the prior intended manifest? * Parser: can the restored files be read by their specified standard parser? Measured results ---------------- Case check read-data restore paths SHA256 parsers (present files) S1 0 0 0 12/12 12/12 12/12 S2 12 12 12 0/12 0/12 not attempted (0 files) S3 0 1 1 11/12 11/12 11/11 S4 0 0 0 11/12 11/12 11/11 S5 0 0 0 12/12 11/12 12/12 S6 0 0 0 12/12 12/12 11/12 Exit codes: 0 is successful completion. In this run, 12 means the public dummy password was wrong; 1 reports a data/read/restore error. The JSON and command log preserve the separate operations and diagnostics. A zero exit code must not be promoted to a claim that the user's complete recovery need has been met. S1: Unmodified synthetic source. All expected paths, bytes, and parser tests passed. S2: An intentionally wrong public dummy password against a copy of S1's repository. No files were restored. This tests one incorrect password fixture, not the availability or absence of any other recovery key. S3: One ciphertext byte in one data blob was flipped in a disposable copy of S1's repository. The single blob for documents/records.json was located through its known content hash in restic's index; the byte after its 16-byte nonce was changed. The pack's size and its header were not changed. Normal check passed; full reading and restoration failed. Eleven files were recovered. The exact pack/blob IDs, byte offset, pack checksums, and missing file are in results.json. This outcome concerns this corruption fixture; normal check can detect other kinds of damage. S4: documents/contacts.csv was excluded from backup after the full intended manifest was captured. All restic operations passed, but the independent intended inventory and complete-file hash comparison failed at 11/12. S5: The older full snapshot was restored even though a newer one existed. documents/status.json contains revision 1 instead of required revision 2. All twelve files parse, but only eleven match the required-current manifest. All twelve match the separately retained old-source manifest; this is correct restoration of the wrong requested version, not damage introduced by restic. S6: documents/records.json contains invalid JSON before backup. That source parser failure is recorded in synthetic-manifests.json. All restored bytes match the source, but the same JSON parser still fails. Restoration did not cause the invalid contents. Meaning of the parser column ---------------------------- Only files actually present are attempted. Missing files are reported by the inventory column and must not be counted as successful parser checks. TXT uses strict UTF-8 decoding; JSON uses json.loads; CSV uses csv.reader(strict) and consistent row widths; XML uses ElementTree.parse; ZIP reads all members and checks CRCs; WAV reads all declared frames and checks their byte length; TOML uses tomllib.loads. These are actual parsing/reading operations, not extension checks. They do not establish that a chosen desktop application will render every feature, or that the contents meet a person's real needs. Public assets ------------- restore-lab.py Final reproduction source, standard library only. results.csv Compact numeric results. results.json Per-file results, snapshot IDs, binary provenance, script hash, and corruption-fixture details. synthetic-manifests.json Prior intended baselines and source parser results. command-log.json Actual commands, exit codes, stdout, and stderr. Lab paths, execution-account names, and timestamp fields are redacted; operational results are not. lab-readme.txt This methodology and interpretation record. Reproduction on Windows amd64 ----------------------------- Requirements: Python 3.11 or later, and the official ZIP listed below. The recorded run used Python 3.12.10. Save restore-lab.py in a new working folder. Download the official release ZIP and compare its SHA256 to the official SHA256SUMS file. The script enforces both the ZIP and executable hashes below. It does not download anything or require restic to be installed system-wide. Release: https://github.com/restic/restic/releases/tag/v0.19.1 ZIP: https://github.com/restic/restic/releases/download/v0.19.1/restic_0.19.1_windows_amd64.zip Official checksums: https://github.com/restic/restic/releases/download/v0.19.1/SHA256SUMS ZIP SHA256: da948ad707ed690426473aaba2046cd61f8f90f6f0e7dab6be0d5796531de67d Extracted executable SHA256: b0dd1fd21eea5d8fe1325f55f7118213c21f36de8a261e04c0624a5ab9fd7830 Measured version output: restic 0.19.1 compiled with go1.26.4 on windows/amd64 The ZIP hash was compared to the checksum retrieved from the same official HTTPS release. A separate release-signature verification was not performed. Run from your new working folder in PowerShell: python .\restore-lab.py --restic-zip .\restic_0.19.1_windows_amd64.zip The script creates a fresh c07-restore-lab-* directory inside that working folder and prints LAB_ROOT. It accepts no existing repository, source folder, or restore destination. It refuses symlinks and Windows reparse points, including junctions. The ZIP is read as bytes and only the named executable is extracted. Destructive test mutation is restricted to the newly created S3 repository copy. There is no automatic cleanup or operation on real data. The fixed PUBLIC-SYNTHETIC-LAB-ONLY... strings are non-secret test fixtures. Anyone can read them. Never use them to protect actual files. Child restic processes receive those fixtures instead of inheriting RESTIC_* settings. They use a temporary-file directory under the fresh lab root. No global environment variable or VPN configuration is changed. Results are written inside LAB_ROOT. No files are copied automatically into a publication folder. Each rerun creates another fresh root. Random repository keys, snapshot IDs, pack IDs, and timings differ across runs. The corrupted file is fixed by its known content hash. The published source is hashed at the start and checked again at completion, so changing the script during a run prevents that run from being finalized. Limits ------ This corpus is 2,330 bytes in the normal case and is deliberately small. The experiment does not measure large repositories, restore speed, disk failure rates, media longevity, offsite or offline resilience, cloud-account recovery, credential custody, full-operating-system recovery, permissions, access-control lists, alternate data streams, application rendering, or future file-format support. Parser success is limited to these fixtures and parsers. No guarantee about an individual's backup follows from this lab. Relevant official documentation ------------------------------ https://restic.readthedocs.io/en/stable/045_working_with_repos.html https://restic.readthedocs.io/en/stable/050_restore.html https://restic.readthedocs.io/en/stable/030_preparing_a_new_repo.html The published results are observations of version 0.19.1. The stable manual can change later; version output, release URL, checksums, commands, and source are preserved above for that reason. Additional teaching tools (2026-09-13) ------------------------------------- The new comparison script checks recorded results against the published reference. Read comparison-readme.txt for setup, outputs, limits, and figure generation. This technical supplement and the standalone figure use English.