# Safety Contract PhotoDock treats source media as irreplaceable. These invariants block every release: 1. Source capabilities expose enumerate, stat, and read-only open only. 2. Source traversal uses directory descriptors and no-follow opens; symlinks and non-regular files are never imported. Stable-inode filesystems bind each record to device, inode, size, modification time, and a bounded content sample. Known unstable-inode source filesystems such as exFAT bind to device, size, modification time, and that sample instead; the opened descriptor is sampled again before any preview or import read. 3. A confirmed plan is immutable and bound to a source generation and hash. 4. Destination bytes first enter an exclusive, tokenized hidden temporary file. 5. Final publication uses Linux `renameat2(RENAME_NOREPLACE)`; an existing final is never replaced. 6. Strong mode syncs and fully reads back the temp, comparing SHA-256 before publication. Basic and Off remain explicitly labeled. 7. Only equal size and full SHA-256 is a confirmed duplicate. 8. Backup reads from the committed primary, verifies those bytes against the primary hash recorded by the import, repeats the Strong transaction with that expected hash, and is independent only when a different physical device is proven. 9. Cancel and backup failure never remove a committed primary. 10. Every failed transaction attempts ownership-proven temp cleanup. If that cleanup cannot be proven complete, the job remains recovery-required; recovery deletes only an exact recorded temp name whose device and inode still prove ownership. Unknown or replaced paths are preserved. 11. Primary, verification, backup, cancel, and eject are separate outcome fields. 12. UDisks unmount options never request force; mount/eject are always explicit. 13. The runtime has no root helper, policy override, network, telemetry, runtime download, source delete, format, rename, or metadata-write surface. 14. Media parsers receive only a bounded private cache copy in a restricted transient service; helper stdout and stderr are bounded while the helper runs, and QML receives only a stripped bounded cache preview. 15. Job admission serializes plan revalidation, durable journal setup, active job reservation, and worker launch. At most one import job can run. 16. Source traversal has per-directory and global entry/directory limits. Filenames that cannot be represented as strict UTF-8 are rejected with a redacted skipped-entry label and never reach the protocol or path hashes. Synthetic tests prove these code-level properties. Filesystem, disconnect, reader, and eject support additionally requires a dated disposable-hardware report in [supported-hardware.md](supported-hardware.md).