# Phase 0.9D off-device backup contract Date: 2026-07-18 Status: `DESIGN_ONLY`; no readback route currently satisfies this contract. ## Invariants A future host workflow shall process one component per separately authorized session. It shall contain no upload, create-on-device, rename, unlink, delete, chmod, chown, launch, process/service action, mount action, configuration change, `/autoload_status`, automatic resume, or automatic retry. The host must: 1. record an approved literal source path without inference; 2. record non-secret device identity, session identity, route, raw request/response metadata, and exact client source commit; 3. create a new local temporary output exclusively and refuse overwrite; 4. receive in binary mode while counting every byte; 5. reject timeout, protocol error, unexpected EOF, short transfer, surplus bytes, missing expected length, or any partial output as `TRANSFER_INCOMPLETE` then `INVALID`; 6. close the file, reopen it, record its exact size, and compute SHA-256; 7. start a new connection and session for a second new exclusive local temporary file, with no reused partial bytes; 8. close, reopen, size, and hash the second file; 9. compare exact sizes, SHA-256 values, and every byte; 10. make no automatic third attempt when the copies differ. If a transport cannot provide a trustworthy expected byte count, the host may save an exploratory copy only as `SOURCE_MAPPING_PARTIAL`; it cannot classify the transfer as byte-exact source attestation. The current stack has the stronger blocker that it does not return arbitrary file bytes at all. ## State machine ```mermaid stateDiagram-v2 [*] --> TRANSFER_NOT_STARTED TRANSFER_NOT_STARTED --> TRANSFER_INCOMPLETE: first byte accepted TRANSFER_INCOMPLETE --> INVALID: timeout / short read / framing error TRANSFER_INCOMPLETE --> HOST_COPY_RECEIVED: exact complete response HOST_COPY_RECEIVED --> HOST_COPY_REOPENED: close + reopen HOST_COPY_REOPENED --> HOST_COPY_HASHED: size + SHA-256 HOST_COPY_HASHED --> SECOND_COPY_CREATED: new session/output SECOND_COPY_CREATED --> SECOND_COPY_REOPENED: exact receive + close/reopen SECOND_COPY_REOPENED --> SECOND_COPY_HASHED: size + SHA-256 SECOND_COPY_HASHED --> INVALID: size/hash/byte mismatch SECOND_COPY_HASHED --> COPIES_MATCH: size/hash/all bytes equal COPIES_MATCH --> SOURCE_MAPPING_PARTIAL: path/object identity incomplete COPIES_MATCH --> SOURCE_MAPPING_VERIFIED: independent source mapping proven ``` Required status vocabulary: - `TRANSFER_NOT_STARTED` - `TRANSFER_INCOMPLETE` - `HOST_COPY_RECEIVED` - `HOST_COPY_REOPENED` - `HOST_COPY_HASHED` - `SECOND_COPY_CREATED` - `SECOND_COPY_REOPENED` - `SECOND_COPY_HASHED` - `COPIES_MATCH` - `SOURCE_MAPPING_PARTIAL` - `SOURCE_MAPPING_VERIFIED` - `INVALID` ## Classification boundaries `OFF_DEVICE_BACKUP_VALID` is allowed only after the two closed-and-reopened local copies have identical sizes, SHA-256 values, and bytes. It says nothing about the current live source path unless source mapping was independently proven. `RECOVERY_PROVEN` is forbidden in this phase. A matching host backup is not proof that any actor can safely restore it, survive power loss, retain the old live file, or recover without the component being replaced. The contract is intentionally descriptive. Phase 0.9D creates no client, script, device command, backup, or transfer package.