# Evidence transport profiles

## Declared USTAR collection

`CollectionSpec.collect_tar(bytes, limits~, max_bytes~)` / `collect-tar COLLECTION.json FILE.tar` collect exact declared regular XML files using the same full-plan semantics as ZIP collection. Missing reports/cases remain incomplete; malformed sources, duplicate/extra/wrong-case paths and unsupported USTAR features refuse the entire operation. The specification supplies all identities and policy. The same binary/source/count budgets apply; no extraction directory is created, no ancillary members are ignored, and no compressed tar/PAX/GNU/sparse support is implied.

Independent GNU tar fixtures are bundled with exact generation commands, hashes and licenses in [examples/transport](../examples/transport/README.md). Run `moon run --target wasm cmd/moontestledger -- collect-tar examples/upstream/collection.json examples/transport/jest-reports.tar` (creation 0), save UTF-8 stdout, then `verify` that file (expected 1). Missing and denied-recovery scenarios are also documented there. E2E evaluation needs only MoonBit and the repository, without an archiver installation or private service.

## Declared ZIP collection

`CollectionSpec.collect_zip(bytes, limits~, max_bytes~)` / `collect-zip COLLECTION.json FILE.zip` ingest raw XML archives directly without extracting files. The existing spec supplies the complete run/revision/report/case inventory and recovery policy; archive paths supply no identities. Missing declared files stay incomplete, present empty/partial reports are replayed against their declared testcases, and malformed XML refuses the whole operation. Collect returns an archive independently of its test gate (CLI creation 0); run `verify` on that output for 0/1/3.

Archive collection accepts only exact declared regular-file paths, including nested paths. Unknown files, wrong casing, case aliases, duplicate members, unsafe names and all unsupported ZIP profile features are refused with actionable diagnostics. Unlike directory collection's selected-path traversal, this archive API has a closed input set and does not ignore ancillary files. Prepare an artifact ZIP containing only the explicitly declared XML files. The same transport/source budgets apply. It is not a generic ZIP importer and does not support every producer's ZIP extensions.

For the bundled real Jest report, create a reference with PowerShell `Compress-Archive -LiteralPath examples/upstream/jest-junit.xml -DestinationPath output/jest-reports.zip`, then run `moon run --target wasm cmd/moontestledger -- collect-zip examples/upstream/collection.json output/jest-reports.zip`. Save stdout to a new UTF-8 JSON file and run `verify` on it (expected 1). The E2E script generates archives itself, verifies identical evidence against directory collection, preserves missing reports, rejects extras/malformed bytes, recovers after correction and retains denied recovery for the synthetic retry workflow; it requires no CI account or private infrastructure.

## USTAR

`Evidence.tar_pack(limits~, max_bytes~)` / `decode_tar_pack(bytes, limits~, max_bytes~)` and `tar-pack` / `restore-tar` / `verify-tar` provide the same sealed full-plan workflow in an uncompressed POSIX USTAR carrier. For the real demo: `moon run --target wasm cmd/moontestledger -- tar-pack output/jest-evidence.json output/jest.tar`, then `restore-tar output/jest.tar` or `verify-tar output/jest.tar`. Use a new file. Creation returns 0, real Jest verification returns 1, and malformed packs return 2. Library calls support all four targets; CLI supports Wasm/native.

Headers must have POSIX USTAR magic/version, valid checksum, nonnegative octal numbers, ordinary-file type, empty link target/device metadata, supported header bytes and portable ASCII paths. The reader supports USTAR prefix/name fields, checks every member and padding block, requires two zero end blocks, and accepts additional whole zero tail blocks. It refuses directories, links, PAX/GNU/sparse extensions, base-256/negative numbers, duplicate names, nonzero padding/trailers and truncation. The same byte/source/count budgets apply as ZIP; writer checks total output size before construction. Exports use deterministic order, mode 0644, zero UID/GID/mtime and no owner names. Filesystem metadata is carrier data, not test evidence; this is not a lossless general tar converter. No gzip/compression support is implied.

USTAR fields were implemented from format facts, informed by [Go archive/tar's format documentation](https://github.com/golang/go/blob/master/src/archive/tar/format.go). No Go or existing MoonBit tar implementation/tests were copied; the code/tests are project-authored Apache-2.0. Existing MoonBit tar helpers are recorded in [carrier boundary review](carrier-ecosystem.md).

## ZIP

ZIP carries the existing sealed file pack: `index.json` and exactly its content-addressed XML members. It preserves the full expected plan and recovery policy. Absent expected reports remain incomplete; missing accepted XML, extra members and changed content are invalid. No general filesystem extraction or archive-conversion API is exported.

```text
moon run scripts/render-demo.mbtx wasm
moon run --target wasm cmd/moontestledger -- zip-pack output/jest-evidence.json output/jest.zip
moon run --target wasm cmd/moontestledger -- restore-zip output/jest.zip
moon run --target wasm cmd/moontestledger -- verify-zip output/jest.zip
```

Use a new destination; overwrite is refused. Creation/restore return 0. The real six-test Jest verification returns 1 (four failures); verification preserves the original 0/1/3 gate. Invalid input returns 2 with JSON diagnostics on stderr. Native accepts the same commands. CLI binary reads are capped at 16 MiB. Library `Evidence.zip_pack(limits~, max_bytes~)` / `decode_zip_pack(bytes, limits~, max_bytes~)` default to 16 MiB and allow a positive cap up to 256 MiB. Existing source/plan/JSON budgets still apply.

The initial ZIP profile accepts classic single-disk ZIP, STORED/DEFLATE, ASCII portable relative names, UTF-8 marker, compression hints and signed/unsigned classic data descriptors. It requires matching local/central records, exact expanded sizes, CRC-32, contiguous nonoverlapping local records and a closed pack inventory. Before expansion, per-member bytes are capped at four times `max_source_units`, aggregate bytes at four times `max_total_source_units + max_source_units`, and count at `max_reports + 1`. Index and XML decoding are strict UTF-8. Index validation precedes XML expansion; SHA-256 receipts and complete XML replay follow container checks.

Encryption, ZIP64, split archives, extra fields, comments, links, directory entries, special files, executable prefixes, gaps and hidden records are refused. This constrained profile does not accept arbitrary GitHub artifact ZIPs. Timestamps/permissions are carrier metadata rather than test evidence: exports use fixed zero timestamp and no filesystem attributes; restore returns evidence, not reconstructed filesystem metadata. No signing, sender authentication, compression performance or hostile-filesystem transaction claim is made. Trust the index independently where required.

Compression, decompression and CRC reuse [hustcer/fzip 0.8.8](https://github.com/hustcer/fzip), Apache-2.0. Container guards, evidence adapters and tests are project-authored Apache-2.0. [PKWARE APPNOTE 6.3.10](https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT) is a format reference, not redistributed text. [GitHub artifact download API](https://docs.github.com/en/rest/actions/artifacts#download-an-artifact) documents the ZIP delivery workflow. The real XML retains its MIT notice.
