This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user