Repository navigation
feat(upload, manifest): report the uploaded file's SHA-256 and record it on manifest entries (V2-1564) - #229
Nic-dorman wants to merge 2 commits into
Conversation
… it on manifest entries (V2-1564) Nothing below a service like Indelible produced a standard whole-file hash of uploaded content: the DataMap's hashes are per chunk, and the content address proves integrity only inside Autonomi. Fixity checks, receipts and outside tools expect SHA-256. Uploads: - The file read loop that feeds stream_encrypt now hashes each buffer, so the SHA-256 costs no extra pass over the file. It comes back with the DataMap from the encryption task. - FileUploadResult::sha256, PreparedUpload::sha256 (carried through every finalize path, including the resumable wave and merkle handles), and DataUploadResult::sha256 for in-memory data. Manifests (ADR-0006, amended; the owner approved the field): - ManifestEntry::sha256, an optional 32-byte bin between `size` and `source`. Optional within format version 1: older readers skip it, and entries without it encode exactly as before (golden fixture added). - ManifestBuilder records it for every file it uploads; entries added by address or with a fetched DataMap have none. - Extraction hashes each downloaded temporary file that has one before it takes its name; a mismatch fails that entry (ManifestError::Sha256Mismatch) and leaves nothing at the target. - Browser decodeManifest/parseManifestLink entries expose it as hex. CLI: `ant file upload` prints SHA-256 (and `--json` adds `sha256`) and records it in the upload history; `ant manifest show` prints each recorded hash and adds it to `--json`. ADR-0006 also gets the daemon-API wording its owner confirmed: none is part of the initial release (antd adds one in V2-1570). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
dirvine
left a comment
There was a problem hiding this comment.
Review of 2eace7374d0642376e799458a4fb9e63c2b7cc4c
No severe defect introduced by this PR was found in the completed review work. This result is provisional, not a formal approval.
Code checks
- The native uploader hashes the plaintext in the same read loop that feeds encryption. Read/encryption failures do not publish a successful hash/DataMap result.
- The digest is carried through prepared uploads, payment/finalisation and resume results, then exposed by the CLI and browser API.
- The manifest field is optional. Missing hashes retain the old version-1 encoding. Existing readers can skip the new field.
- Extraction checks the downloaded temporary file before rename. A mismatch fails that entry and removes the temporary file, rather than publishing unverified content.
Verification
I ran these commands locally at the reviewed head; each returned exit code 0:
cargo test -p ant-core --locked --lib sha256— 5 passed.cargo test -p ant-core --locked --lib data::manifest::— 54 passed.cargo test -p ant-core --locked --lib assemble_— 5 passed.cargo test -p ant-cli --locked— 31 passed.cargo check -p ant-core --locked --target wasm32-unknown-unknown --no-default-features --features browser-wasm— passed. This is a build check, not browser runtime execution.
Exact-head CI reports native unit, E2E and Merkle E2E success on Linux/macOS, plus Browser WASM and real Chromium/local-node success. I inspected the new hash-mismatch E2E test, but did not rerun network E2E locally.
Separate CI failure
Security Audit is still failing. It reports rustls 0.23.45 advisories RUSTSEC-2026-0346, -0347, -0348 and -0349. That version is also present in the base commit 225a6768589ecbb6a0c77d70df329f343b22776f; this PR's only lockfile change adds the already-locked sha2 dependency to ant-core. This is an inherited dependency issue, not a new SHA-256 implementation defect. It remains unresolved; CI is not fully green. The earlier PR-template failure has a successful later run.
Independent review participation
Two distinct rounds were requested from GLM 5.3/OpenRouter (z-ai/glm-5.3), local Qwen 3.8 (qwen38-27b) and local DS4 (deepseek-v4-flash):
- Correctness: GLM and Qwen completed with no blocker; DS4 timed out.
- Adversarial security/lifecycle: Qwen completed with no blocker; GLM returned an incomplete termination; DS4 timed out.
3/6 role reviews completed, from 2/3 named models. The completed opinions agree; there is no substantive dissent in those opinions. Missing reviews are not agreement. Full team consensus and the full review gate are not established.
Before formal approval, complete the missing review roles. The inherited Security Audit failure also needs resolution or a separate authorised decision. No approval or merge action is taken by this comment.
dirvine
left a comment
There was a problem hiding this comment.
APPROVE — reviewed head 2eace7374d0642376e799458a4fb9e63c2b7cc4c
No severe defect introduced by this PR was found in the completed code checks, tests or independent reviews. The live PR head and base are unchanged from the reviewed revisions.
Authority and scope: Benjamin Pierce explicitly authorised Hermes in the review thread to submit a formal verdict on this PR based on the reviews that completed. This supersedes the earlier hold on submitting a formal verdict for this PR only. It does not mean the full review panel completed, does not waive CI requirements, and does not authorise a merge or release.
Evidence supporting this verdict
- The native uploader hashes the plaintext in the same read loop that feeds encryption. Read/encryption errors do not publish a successful hash/DataMap result.
- The digest passes through prepared uploads, payment/finalisation and resume results to the CLI and browser API.
- The manifest hash is optional and preserves the old version-1 encoding when absent. Extraction verifies the temporary file before publishing it; a mismatch fails the entry and removes the temporary file.
- Previously executed local checks at this unchanged head all returned exit code 0:
cargo test -p ant-core --locked --lib sha256,cargo test -p ant-core --locked --lib data::manifest::,cargo test -p ant-core --locked --lib assemble_,cargo test -p ant-cli --locked, and thebrowser-wasmtarget build check. The latter is a build check, not local browser runtime execution. - Current exact-head CI reports successful native unit, E2E and Merkle E2E runs on Linux/macOS, Browser WASM, and real Chromium/local-node tests. Network E2E was not rerun locally. The earlier PR-template failure has a later successful run.
Review limits and unresolved CI
3/6 whole-role reviews completed, from 2/3 named models. The panel assessment remains provisional; full team consensus is not established.
- GLM 5.3/OpenRouter (
z-ai/glm-5.3): correctness review completed with APPROVE; adversarial extraction-scope retry also completed with APPROVE, but the upload scope did not complete. The scoped retry does not complete the whole adversarial role. - Qwen 3.8 (
qwen38-27b): correctness and adversarial reviews completed with APPROVE. - DS4 Flash (
deepseek-v4-flash): no substantive completed review; requests timed out, including retries.
No material dissent or introduced blocker was reported by the completed reviews. Missing responses are not agreement.
Security Audit still fails on rustls 0.23.45 advisories RUSTSEC-2026-0346, -0347, -0348 and -0349. The same version is present in base 225a6768589ecbb6a0c77d70df329f343b22776f; this PR's lockfile delta only adds the already-locked sha2 dependency to ant-core. This is an inherited dependency issue, not an introduced hash implementation defect. This approval does not resolve or waive that failure, or declare the PR ready to merge.
Submitted by Hermes through the configured GitHub account, under Benjamin Pierce's PR-specific authorisation. No merge or repository change was performed.
Brings up #325's final ant-cli-v0.3.11 repin via #326 and #346. antd/Cargo.toml and antd/Cargo.lock conflicted on the ant-core and ant-protocol pins; both keep this branch's side, the DEV PIN on WithAutonomi/ant-client#229 with its ant-protocol [patch], because the SHA-256 change needs #229 and ships with it. No other conflicts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Uploads now report the uploaded file's SHA-256, and ADR-0006 manifest entries can record it. Extraction checks every download against it.
What
Uploads
stream_encrypthashes each 8 KiB buffer. The SHA-256 comes back with the DataMap from the encryption task, so it costs no extra pass over the file.FileUploadResult::sha256andPreparedUpload::sha256, both#[non_exhaustive], so these are additive. The value is carried through every finalize path, including the resumable wave and merkle handles;DataUploadResult::sha256for in-memory data.Manifests (ADR-0006 amendment; the ADR owner approved the field)
ManifestEntry::sha256, an optional 32-bytebinwritten betweensizeandsource. Under the format's forward-compatibility rules it is optional within version 1:ManifestBuilderrecords it for every file it uploads. Entries added by address, or with a DataMap fetched from the network, have none: the creator never read their bytes.ManifestError::Sha256Mismatch) and leaves nothing at the target.decodeManifest/parseManifestLinkentries exposesha256as hex.CLI
ant file uploadprintsSHA-256:,--jsonaddssha256, and the hash is recorded in the upload history.ant manifest showprints each recorded hash under its entry and adds it to--json.ADR-0006 is amended in the data model, normative schema, creation, history, extraction, surface, consequences and validation sections. It also gets the daemon-API wording its owner confirmed: no daemon API is part of the initial release. WithAutonomi/ant-sdk adds one under V2-1570.
Why
The DataMap's hashes are per chunk, and the content address proves integrity only inside Autonomi. Fixity (V2-440), signed receipts (V2-454), Indelible's end-to-end ingest check (V2-1427) and outside tools all expect a standard whole-file hash. Computing it in the core means every client gets it: CLI, antd and the SDKs.
Linear issue
Closes V2-1564
Risk tier
Nothing on the wire or on the network changes. The manifest is a client-side file format that is never stored on the network, and the new field is optional within its version.
Compatibility
Wire: none.
Storage: none on the network. The
.antformat gains an optional entry field within version 1:API: additive fields on
FileUploadResultandPreparedUpload(both#[non_exhaustive]). Three changes are breaking for consumers that use struct literals or exhaustive patterns:DataUploadResult;ManifestEntry;ManifestError::Sha256Mismatchvariant.CLI output gains a line in
ant file uploadandant manifest show, and asha256key in both--jsonoutputs.Semver impact
It's breaking for
ant-coreRust consumers only via struct literals or exhaustive matches, as listed above. On the 0.x line this is a minor bump.Test evidence
sha256;cargo test -p ant-core --lib: 830/834 pass. The other 4 arenode::daemon::forward::runnertiming tests, which this PR doesn't touch; they pass 11/11 when run on their own, both parallel and single-threaded.cargo test -p ant-cli: all pass.e2e_manifest(7/7), including the newuploads_record_sha256_and_extraction_checks_it. A built manifest records the right hash for every uploaded file, and it extracts. A tampered hash fails only that entry, leaving no file and no temporary file behind;e2e_file: the direct upload, and the external-signer prepare →finalize_uploadpath, both report the right hash.cargo clippy --all-targets --all-features -- -D warnings(native) and thewasm32-unknown-unknownbrowser-wasmclippy: clean;cargo fmt --check: clean;scripts/adr-governance.py: passed.New dependency
sha20.10, as a direct dependency ofant-core. It was already in the lockfile transitively, so no new crate enters the graph.ADR
ADR-0006 (Proposed) is amended in this PR:
docs/adr/ADR-0006-file-manifests-and-links.md. Not required for T1.Mitigation / rollback
Revert the PR. Manifests written with
sha256stay readable by every client, because the field is optional and ignored by readers that don't know it.🤖 Generated with Claude Code