feat(remote): step 14 part 1 — stream digests in the fetch pipeline's cache-fill tee

What

Add the format-agnostic streaming-digest tee to internal/remote: the cache-fill tee now computes SHA-1 and SHA-512 always and MD5 unless FIPS mode is on, over exactly the bytes the caller receives, and hands the sums to the widened CacheEntry at upsert — no re-read of the committed blob.

Carries the two fill-consumed fetch-surface additions the Maven fill (next parts) needs:

  • FetchResult.ContentLength — the upstream's declared length for a verbatim identity-coded stream, -1 when unknown so fakes never read as a 0-byte body.
  • the marked-transport-failure arm in FallbackEligible, so error mapping classifies a mid-transfer failure as fallback-eligible.

Context

Part 1 of the S14 Maven remote artifact-proxy step. This is the S13-territory seam surface the Maven fill consumes; the fill itself and the stale-revalidation arm land in the stacked parts above.

Spec coverage

Spec: docs/specs/S14-maven-remote.md

Part 1 of Step 14: the plan Amendment's streaming-digest tee in the S13 fetch pipeline, plus the two fill-consumed fetch-surface additions. The Maven wiring — fill handler, digest-column row write, header composition, response mapping — lands in the stacked next parts; this slice ends at the seams the fill consumes. Criteria beyond this slice are not listed.

Acceptance criteria

# Criterion (this slice) Tests
AC-1 / AC-7 (computation half) Fill computes SHA-1, SHA-512, and (non-FIPS) MD5 in the same pass over exactly the bytes the caller receives; every fill recomputes from the stream, no re-read of the committed blob Tee vectors and byte-identity: TestDigestTee_KnownVectors, TestDigestTee_Property_MatchesDirectHashing; fill pipeline: TestCacheFillTee_FillDigestFingerprint, TestCacheFillTee_FillDigestFingerprint_FIPSMode; widened CacheEntry payload end to end: digest asserts in TestFetcher_Fetch_Miss200_TeesIntoCacheAndStreams
AC-1 (FIPS half) FIPS-on fills leave MD5 uncomputed (the NULL column write is the Maven fill's) TestDigestTee_FIPSOn_OmitsMD5, TestCacheFillTee_FillDigestFingerprint_FIPSMode (nil MD5 under FIPS)
AC-3 (slice) Cold-fill header subset: upstream Content-Length when present Seam only — FetchResult.ContentLength carries resp.ContentLength on a hit, and NewFetchResult plus the error/304 paths declare -1 so fakes never read as a 0-byte body. The header composition and the -1-omits-header rule land with the Maven fill

Error cases

# Condition (this slice) Tests
E-1 A mid-transfer body-read failure is marked and classified fallback-eligible (S13 cache-fallback rule) TestFallbackEligible_MarkedTransport: marked unexpected EOF, marked plain read failure, marked truncation through coalescing
E-2 Fail closed: an unmarked error, or a marked exclusion-arm sentinel, never becomes eligible TestFallbackEligible_MarkedTransport: unmarked unexpected EOF; marked cache fill, header injection, caller cancellation, leader canceled, transform rejection (the fallbackEligible-parity exclusion)
E-3 Cap-breach classification stays split: the exported predicate keeps marked cap sentinels eligible (spec-aligned seam), the standalone predicate excludes them (plan research correction) marked response-size cap stays eligible (TestFallbackEligible_MarkedTransport); TestFetcher_Fetch_BodyCapBreach_IsNotFallbackEligible (unchanged)

Security considerations

# Concern Tests
S-1 FIPS control: MD5 suppression under FIPS; posture captured once at construction (Maven handler parity) so the fill path reads a field, not the live global TestDigestTee_FIPSOn_OmitsMD5, TestCacheFillTee_FillDigestFingerprint_FIPSMode
S-2 Fallback eligibility fail-closed: only marked transport failures reach the eligible class; the exclusion arm resolves before the marker arm E-2 rows above
S-3 No new SQL: seam-side change only; persistence statements stay in the slice datastore packages (ADR-023) No datastore surface touched
S-4 No client-visible surface change: ContentLength never travels through SafeHeaders; fakes declare no body length NewFetchResult -1 wiring, field doc in operations.go

docs/testing/ (Guardrail 13)

No e2e scenario is added or affected: the Maven remote scenario catalog lands with the fill handler in the stacked next parts, and this slice changes only the internal fetch-pipeline seam — it is not reachable from the hosted surface. Unit-level coverage for the new behavior lives in internal/remote (digest tee vectors, fill fingerprint, fallback tables).

Related to #286 (closed)

Edited by Moaz Khalifa

Merge request reports

Loading
Loading