# Photo browser adoption unit 0.3.7 — private review candidate

Adopt only the immutable archive and verify its outer SHA-256 plus FILE-MANIFEST.json. Never adopt these live development paths. The content identity in unit-content-manifest.json covers the runtime, policy, matching, writer adapters, independent verifiers, recovery, delivery and pinned zip.js files together. Generated unit-identity.js carries that identity; the outer file manifest also hashes it. Every approval and full-unit recovery identity includes it. Any source/runtime/verifier change requires a new content identity, fresh approval and an atomic adoption unit. This full unit intentionally retires rev2 approval/recovery identities. The separately frozen recovery-consent-v3.zip preserves rev2 identity and enables no writer.

## Public module contract

- startIntake(File[], options) -> {id,promise,cancel}: maximum4ZIPs/32MiB compressed/64MiB actual expanded/8MiB member/512entries/100:1ratio;30s disposable worker. result.manifest accounts every parsed member, or an explicit archive_held record when enumeration is unknowable. Original archives remain immutable. Stored/deflate only. Unsafe paths, symlinks/special files, encryption/multidisk, CRC/header and normalization collisions held. Source hashes and distinct part/index identities provided. Bounds limit admitted/retained data, not all browser heap allocation.
- matchSidecar({path,file},selection) -> planned_requires_approval or held. Exactly one of the two same-directory full-media-name suffixes .json or .supplemental-metadata.json, with exact media basename JSON title and string integer photoTakenTime timestamp,256KiB per JSON. Both suffixes present is held even if bytes/dates agree; repeated path/title and case/NFC collisions relevant to source/candidate paths are held. Titles are audited only in the exact folder (and ZIP part-prefixed container), up to512 total selection rows and1MiB JSON read per match; any same-folder JSON whose root title cannot be safely audited (malformed syntax, duplicated/escaped-equivalent root title keys or over256KiB) and total JSON discovery over1MiB holds the match instead of claiming title uniqueness. Title audit reads only the root title identity; duplicate date/non-title fields in a different JSON do not become a date source or invalidate an independently unique target title. The selected candidate still rejects every duplicate key at every nesting level. These files remain original inputs; no date is inferred from the audit. Wrong-folder, truncated, edited-alias and fuzzy remapping remain held. Edited-alias means a different filename is never mapped back to the original; no filename transformation occurs. Duplicate and escaped-equivalent JSON keys in the candidate reject at every nesting level. matchSidecar accepts an optional third {signal}; snapshots selection/record fields and checks cancellation around reads/hashes so late plans after cancellation are held. Date range1900–2100, UTC explicitly approved; filesystemmtime never used.
- approveRequest(file,{relativePath,timestamp,provenance,approved}) -> descriptor + operationId. Binds source path/bytes/hash, sidecar provenance, timestamp/UTC, policy and exact schema/engine/policy/matching/implementation/runtime/verifier identity. matchingVersion exact-two-sidecar-names-v2 is in every approved descriptor. Exact Takeout provenance includes this matchingVersion, classic_json or supplemental_metadata_json pattern, exact sidecar path/hash and media/sidecar origins. Direct origins bind paths and actual File names/relative paths; archive origins bind part/index/archiveHash/memberName/path, byte-verified intake records and distinct source/sidecar indices. Mixed/wrong part/archive origins reject. Part-prefixed ZIP logical paths without intake records cannot be downgraded to direct-file provenance; a real directory selection with matching nonempty webkitRelativePath remains direct. Factory approval rechecks the exact provenance shape/path/pattern; there is no renaming adapter or implicit approval. validateRequest rejects mismatched frozen identity fields before recomputing the entire descriptor in each worker. startProcess snapshots the caller request before processing.
- startProcess(source,request,{onProgress,preparationTimeout=90000,writeTimeout=45000,verifyTimeout=15000,totalTimeout=150000,runtimeTimeout=60000,runtimeIdleTimeout=15000,runtimeIntegrityTimeout=15000}) -> {id,promise,cancel}. Separate disposable writer and independent verifier workers. Writer preparation (module bootstrap plus bounded runtime download/integrity) has90s maximum; the45s processing timer starts exactly once on runtime_ready, after the full raw runtime has passed both hashes/size checks and before VM instantiation/media reading/writing. Fresh independent verification retains15s. A non-resetting150s overall deadline starts before worker creation and spans all stages. Every option is a positive integer and may only lower its stated maximum. Repeated phase messages cannot extend processing/overall clocks; unready candidates are held. onProgress receives runtime_preparing, runtime_download with monotonic loadedBytes/totalBytes8963962, runtime_integrity and runtime_ready, then existing processing/verification phases. Cancel terminates the current worker and settles once; late messages cannot update cancelled or subsequent operation IDs. Final verified result carries immutable outputBlob, approved request identity/policy/metadata changes and independent proof; held/review_unchanged carry no exportable candidate. Source-bound SHA256 check, fixed internal input name and whitelisted tags only. Cancel settles and terminates current worker; caller must invalidate its own job generation/queue and release prior staged outputs. The test harness demonstrates allqueue cancellation; startProcess itself owns one member.
- identity(files,settings,{guard?}), checkpoint(operation,index,blob,status,access), checkpointVerified(operation,index,blob,proof,access), resume(files,settings,expectedId,access). access requires live isAllowed predicate; AbortSignal recommended for immediate transaction abortion on revoke/reset/reselection/navigation. Checks occur before/after hashes and storage boundaries. Blob+verifiedproof commit together in one IndexedDB transaction. Full-unit settings must include approvedRequestIds in source-file order for verified checkpoints. Recovery schema2 identities bind unit, normalized original logical path and selection origin (directory_picker or file_picker derived from the File). For extracted ZIP members settings.selectionOrigins must be a source-ordered array of exact {kind:"archive_member",archiveSha256,partIndex,memberPath}; memberPath is the exact approved logical path (including the intake part prefix when used); it is normalized and archive hash/part identity is bound. Supply it again on resume; it does not restore approval. Identities bind unit and selected relative paths; approved request IDs bind provenance/timezone/date and source paths. Resumption requires exact source reselection, settings and current unit; cached output/proof hashes checked. Verified proof schema/status/verifier, exact frozen approved descriptor, expected approved request digest, logical path/source bytes/hash, output hash and whitelisted metadata changes must agree. Every cached verified Blob is then independently reverified against the reselected source in a fresh 15s disposable verifier worker before verified status is returned. Cancellation/consent is checked around this boundary. Stored proof labels are never sufficient. Missing cache fails without creating a new checkpoint store. No silent approval restoration, pruning, migration, eviction resistance or nativecrash guarantee. Limits64Files/32MiB total/8MiB perFile and percheckpoint.
- resultZip(files,results,{signal}) includes all selected originals, verified outputs only after shared exact approval/proof checks and fresh bounded15s independent verification, every-input hash/status/proof manifest.64Files/32MiB originals/32MiB staged/65MiB ZIP maximum. archiveResults(archives,intakeResult,memberResults,{signal}) includes all original ZIPs byte-for-byte, every known member/whole-held-archive accounting and verified member outputs.4archives/32MiB originals/64processed members/32MiB outputs/1MiB manifest/66MiB ZIP maximum. Duplicate output identities rejected; no unverified output permitted. Both delivery entrypoints require each verified result.operationId to equal the proof approvedRequestId, validate exact frozen descriptor/proof schema/status/source path/bytes/hash/output hash/metadata changes, and freshly reverify actual output bytes against the selected source. Direct selections bind normalized webkitRelativePath or filename. archiveResults re-extracts actual selected archive bytes through the existing30s disposable bounded intake worker and requires the supplied manifest to equal its new manifest; caller-provided source labels/Blobs are not trusted. Inputs/proofs are snapshotted. AbortSignal stops intake/verifier workers and ZIP writes, and guards before/after final ZIP close/hash prohibit a successful result after cancellation. Caller must also guard its own final download click and selection revision, because these APIs return Blobs and never initiate downloads. Cached verified records must be mapped with operationId equal to the current explicitly approved request ID before delivery; no implicit approval restoration. Browser-controlled download does not prove durable save, a no-clobber destination, source/output directory separation or a native atomic directory transaction.

Caller owns one immutable selection/approval revision, explicit per-job staging budget, consent lifetime and actual downloaded Blob lifecycle. Do not leave multiple 32MiB results retained across jobs. No original is written/deleted by these APIs. Browser file handles cannot reveal or guarantee the final OS download destination; disclose that limitation.

## Exactly proven supported variants

Chrome154.0.8037.93 only; Edge absent, Safari/actual folder picker execution untested. API presence does not prove execution. File input and Blobdownload are the default; no hostfilesystem writes or serverupload. Every selected item outside the supported subset is held or preserved unchanged, with manifest reason.

JPEG: baseline8bit JPEG without existing EXIF/XMP APP1 or IPTC APP13. Existing metadata variants held. Independent TIFF parser allows only exact capturedate/+00:00 plus narrowly validated default additions; no unknowntags/overlaps/thumbnailIFD. All original nonEXIF segments, encoded scan, ICC/JFIF/orientation-related data and decoded RGBA preserved.16MP/8192side limit before decode. Small32px and generated2048x1536/3,689,224B noisy fixtures tested.

HEIC: exactly one primary8bit/threechannel hvc1 item, no existingExif/XMP/grid/auxiliary/references, bounded supported properties and one in-file extent per item. Every original item identity/payload/config/color/rotation/property association stays byte-identical; only exact addedExif TIFF and one cdsc reference accepted; all mdat extents accounted. Browser decoder not used; proof is independent structural/encoded-data preservation. The32px single-item OS-generated fixture passed; native OS decoder comparison independently passed. Large/other HEIC variants unproven.

MP4/MOV: conventional nonfragmented singlemdat, one or two vide/soun tracks, avc1/mp4a only, self-contained datareferences, narrow sampletable/box inventory, zero/absent supported integercreationdates, no existingKeys capturedate and narrow retainedencoder metadata. All media bytes, track/codec/rotation/timing boxes and complete sample-size/chunk/time inventories preserved; only exact offset relocation and added UTC Keys date accepted. Fragmented/encrypted/unsupported tracks/containers/nonzero ambiguous QuickTime creationdates held. Encoded/config invariants establish preservation; this is not a decoder or media-repair tool. Tiny H.264/AAC fixtures tested, including native packet/decodedvideo/audio comparison. Realistic largevideo unproven.

These are bounded variant claims, not universal format support. Status verified means both metadata policy and independent preservation checks passed for that admitted subset; admission failure never becomes a success count.

## Runtime/provenance/license and remaining release gates

Source-built ExifTool13.42/Perl5.42.0 runtime:29,202,324B, gzip8,963,962B;40MiB initial/256MiB hard linear-memory maximum; SHA256 in runtime-proof.json and constant. Runtime fetch now uses only the fixed relative runtime-build/artifacts/zeroperl.wasm.gz asset: deterministic standard single-member gzip8,963,962B, compressedSHA25634c5fa8ae99652397600401eb0f3349163845693d37ef8c274ce0b0899fd514c. It enforces exact compressed size while streaming, checks compressed integrity, decompresses through browser DecompressionStream(gzip) with an exact29,202,324B expanded budget, and checks the existing rawSHA256 before compilation. It rejects malformed/truncated application gzip, unsupported decompression, redirects, actual application-byte size/hash mismatches, and cancellation. Content-Encoding and Content-Length are transport metadata rather than independent acceptance/rejection conditions: Fetch already handles HTTP content codings and the declared length may describe coded wire bytes. fetchRuntime({signal?,timeoutMs=60000,idleTimeoutMs=15000,integrityTimeoutMs=15000,onProgress?}) separates atmost60s total download from15s with no received positive bytes (including waiting for headers/first byte), then15s for compressed hash/application decompression/raw hash. Each positive byte chunk resets only the idle timer; progress never resets total download. Full body receipt clears download/idle timers and begins the independently bounded integrity clock. AbortSignal and timer guards remain around fetch/read/hash/decompression; empty stream chunks are rejected. Options can only lower their positive integer maxima. No cached or partial runtime is reused across disposable workers. The writer receives a single verified prepared Response and consumes it once; VM initialization and media work start under their own45s timer. No permissive raw fallback. The body exposed by Fetch must be the exact pinned application-gzip bytes, whether transported with identity encoding or an additional HTTP coding. Identity transport and HTTP gzip-of-gzip both pass when browser-decoded body size/hash agree. Transport that instead exposes raw WASM or wrong/truncated application bytes fails closed; no raw fallback. These caps bound Fetch-exposed application bytes, not an independently measurable wire-byte count or all HTTP decoder heap allocation. Actual hosting response/body verification is the webowner’s gate. The raw29,202,324B WASM stays in the private review archive as evidence, excluded from the deployable content inventory; gzip is in that inventory. Decompression read budgets bound admitted bytes, not all browser/internal decompressor heap allocation. Actual memory.grow beyond max fails;12repeated tinywrites pass.8MiB media/32MiB selected+staged bounds do not impose an absolute browser total-process heap cap. Realistic3MP JPEG passed; no largeHEIC/video resource-certification claim.

Exact upstream sourcecommit, official tools publisherdigests, official Debian base digest, apt .deb URIs/hashes/package versions, offline recipe patches/logs, component/source/license inventories are retained. Build used only taskowned2CPU/4GiB/512PID nonprivileged container, disconnected from network before compile. Container stopped afterward. No Docker settings/permissions/autostart changes or unrelatedcleanup. Initial32MiB linkerfailure retained;40MiB measuredrepair stayedunder256MiB cap. Deterministic rebuild byte equality is not independently proven.

Original Perl/ExifTool/zeroperl/zlib/bzip2 source archives and Artistic/GPL/MIT/zlib/bzip2 notices, official WASI/musl/cloudlibc/fts/LLVM runtime notices and wrapper/zip notices included for review. zeroperl-ts exact npm/upstream commit metadata declaresApache2.0; full declared text, exact author/commit attribution and prominent modification notices in both adapted wrappers are included. Missing separate LICENSE/NOTICE alone does not establish absent permission; no upstream copyright/NOTICE text is invented and this is not legal certification. SOURCE-NOTICES-SHIPPING.md specifies required shipped materials. Their actual public source/notice availability arrangement is the webowner release gate.

Producer proof: allfour narrowformats, adversarial mutations, source/approval/cancel/reload/consent/quota checks, direct and complete2ZIP actual downloads. Loopbacknetwork captures GET assets only/no requestbodies; hostedproductionasset/privacy review stillrequired. Quota test uses actual IndexedDB failure under isolatedDevTools quota override, not physicaldiskexhaustion. No destructive originals, accounts, telemetry, credentials, backendprocessing, Libraryreplacement, canonical app edits or publicpublication.

Do not publish the testharness main.js/index.html: it applies a fixed synthetic1960 fixture policy. Deploy only reviewed module assets through the mainwebowner's real approval flow. Independent adoption review, actualhostprivacy/network capture, browsercompatibility/supportedvariant disclosure and publication-tool approval remain required. This unit is a concrete processing handoff, not a publicbeta completion claim.

Deployment adapter note: serve the reviewed module/vendor/runtime tree under one immutable same-origin unit URL, preserving relative paths. Import the entry modules from that unit URL. Do not assume a bundler will rewrite variable stage-worker URLs, and do not scatter the runtime or worker files into unrelated asset paths. The solewebowner should copy the checked static tree atomically, validate FILE-MANIFEST and contentidentity, and then run actualhostnetwork/privacy/workflow checks before enabling processing. Test-only Playwright/harness/source-build material must stay out of the deployed asset set.

Storage revision0.3.1 scopes database reads to the exact operation with atmost65 returned records, validates64records/8MiB individual/32MiB aggregate/schema/identity before any cached Blob hashing, and enforces32MiB aggregate checkpoint budget in the same readwrite transaction as put. Historicaloperations are not loaded. Versions0.3.0,0.3.1,0.3.2,0.3.3,0.3.4,0.3.5 and0.3.6 remain immutable superseded review evidence. Version0.3.2 fixed matching, frozen identity, selection origin and cached-proof validation. Version0.3.3 added only the requested delivery-boundary validation/reverification and cancellation fixes. Version0.3.4 introduced standard gzip runtime transport and ordinary complete source distributions. Version0.3.5 removed only the unconditional Content-Encoding/Content-Length header preconditions after hosted evidence showed exact pinned application bytes through HTTP gzip-of-gzip. All byte limits, compressed/raw hashes, decompression, deadline/cancellation, memory cap and output verification remain. No formats/features or hosting routes change.

Corresponding-source packaging0.3.7: photo-engine-source-notices-0.3.7 is a local complete material directory with index.html and FILE-MANIFEST.json, ordinary original Perl/ExifTool/zlib/bzip2/zeroperl source archives, all declared terms/attributions/notices, modified wrapper/engine sources, exact input/build recipes/receipts. No binary fragmentation or component omission. Largest material is the unchanged20,846,166B original Perl archive. Exact Sites object limits are not established by this unit; deployment owner must verify its supported source/notice publication arrangement without changing approved hosting routes. The raw WASM and previous34MB monolithic source ZIP must not be added to the runtime deployment asset set.

Transport reference: WHATWG Fetch HTTP-network fetch handles content codings before exposed body bytes and explicitly notes Content-Length can become unreliable: https://fetch.spec.whatwg.org/#http-network-fetch . Local regression receipts cover actual HTTP identity, HTTP gzip-of-gzip, transformed-to-raw rejection, malformed/truncated application bytes and four-format processing through actual double compression. They do not prove a new deployed version or clear the separate hosted platform-challenge/privacy gate.

First-use reliability revision0.3.6: only runtime preparation/download timing and progress separate from media processing. Gzip/raw bytes and their exact hashes, format admissions/date policy,256MiB memory maximum, independent verification and verified delivery are unchanged. Given hosted diagnostic HTTP200/cacheHIT transfers cancelled at15s, a synthetic positive-progress18s download must now pass while stalls, wrong bytes and late cancelled returns remain held. No throughput guarantee or new public deployment is inferred. Errors identify total runtime download (runtime_deadline), idle download (runtime_idle_deadline), integrity (runtime_integrity_deadline), stage timeout and non-resetting whole operation (operation_deadline). Deployment owner must adopt the exact complete unit, fresh approval/checkpoint identity and map progress to human-readable first-use download/integrity/processing text and an immediate cancel action. Other copy/UI owners’ canonical source is untouched here.

Exact supplemental-name revision0.3.7 changes matching/approval identity only. Date policy missing-date-explicit-utc-v1, supported media variants, runtime gzip/raw hashes,256MiB memory and0.3.6 download/idle/integrity/processing/verification/whole-operation budgets are unchanged. Fresh explicit approval and recovery identity are required;0.3.6 approvals/cache are rejected before reuse. Existing writing, independent media/date verification, cache re-verification and final delivery verification remain mandatory. Producer fixtures prove only this exact suffix and schema, not complete Takeout naming coverage or user demand. The supplied ExifTool forum URL https://exiftool.org/forum/index.php?topic=17536.0 returned403 during research; no discussion contents or full-Takeout support claim is inferred. No UI/hosting/canonical edits or public publication are part of this candidate.
