Skip to content

apt proxies: index files are revalidated against the InRelease served with them - #893

Merged
schmitch merged 1 commit into
mainfrom
claude/798-apt-proxy-index-consistency
Nov 4, 2026
Merged

schmitch merged 1 commit into
mainfrom
claude/798-apt-proxy-index-consistency

Conversation

@schmitch

@schmitch schmitch commented Nov 3, 2026

Copy link
Copy Markdown
Member

Fixes #798

What changed

An apt proxy revalidated each file under dists/<distribution>/ on its own clock. If the upstream has no Acquire-By-Hash (reprepro, aptly, packagecloud, many vendor repos), apt fetches Packages at its own path, and while its lists are current it revalidates only InRelease. After an upstream publish, the proxy could then serve an InRelease and a Packages from two different publishes, and apt-get update failed with "Hash Sum mismatch".

  • Plugin API (crates/plugin-api): two new FormatPlugin methods, both defaulting to none.

    • file_releases(path) names the files a cached file is published with and listed in.
    • file_listed(release_path, release, path) reads the size and SHA-256 that one of those files lists for it (new type ListedFile).
  • apt (crates/formats/apt/src/proxy.rs): every non-by-hash file under dists/<distribution>/ returns InRelease, then Release. InRelease and Release each return the other. The listing comes from the existing cleartext and Release::parse. Pool and by-hash paths are not touched.

  • File proxy (crates/server/src/files/proxy.rs, fresh_file): besides the TTL (is_due), a cached file is now due when:

    • one of its release files was replaced (updated_at) after the file was last checked. This also covers Release.gpg drifting from Release.
    • the release file being served (the first one cached) lists it with a different size or SHA-256, and that release was checked after the file was.

    The other direction is also handled: if a file was stored after the served release was last checked and the two disagree, the release is revalidated first. This runs after a fresh fetch or a replacement too. Each check is throttled by timestamps, so an upstream that is itself inconsistent costs at most one conditional GET per change, not one per request.

    The release listing is read once per release content and cached locally. Reading it and revalidating the release both run in their own task (as fetch_stored_apart does), so request futures don't overflow the stack.

  • Upstreams with by-hash indexes (Debian, Ubuntu) behave as before: apt fetches their indexes by hash, and those copies are immutable.

  • Docs: docs/apt.md (proxy section) and docs/format-plugins.md (the two new methods).

No admin API changes, so no web UI screen is needed.

Tests

  • New e2e test proxies_serve_indexes_consistent_with_the_release (crates/server/tests/apt.rs) against the mock mirror, with a metadata TTL of one hour:
    1. Caches InRelease, Release, Release.gpg and Packages.gz.
    2. The upstream publishes, and only InRelease is made due.
    3. Checks that the Packages.gz served then matches what the new InRelease lists, and that Release and Release.gpg follow.
    4. Checks that once everything is consistent, the upstream is asked nothing more.
    5. Reverse case: only Packages.gz is made due, and the InRelease served afterwards lists the new one.
  • Without the server change the test fails on that check: the served Packages.gz is 479498e8…, 102 bytes, while the InRelease lists f79dff3d…, 104 bytes.
  • Unit tests for releases and listed_in in the apt crate.
  • Run locally, all passing:
    • cargo fmt --all --check
    • cargo clippy -p forge-server -p forge-plugin-api -p forge-format-apt -p forge-plugin --all-targets -- -D warnings
    • cargo test -p forge-plugin-api -p forge-format-apt -p forge-plugin
    • cargo test -p forge-server --lib
    • cargo test -p forge-server --test apt --test raw --test proxy_cache --test group_proxy --test apt_import
    • cargo xtask changelog --check

Changelog

docs/changes/2.2.0/798.fixed.md


Generated by Claude Code

@schmitch
schmitch force-pushed the claude/798-apt-proxy-index-consistency branch 2 times, most recently from 74b554d to 4306c74 Compare November 3, 2026 23:26
@schmitch
schmitch added this pull request to the merge queue Nov 4, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to a conflict with the base branch Nov 4, 2026
An apt proxy kept each file under dists/<distribution>/ on its own
re-validation clock. With an upstream that has no Acquire-By-Hash, apt
fetches Packages at its own path and re-validates only InRelease while
its lists are current, so the cached InRelease and Packages drifted
apart after an upstream publish and apt-get update failed with "Hash Sum
mismatch" for up to the metadata TTL.

A format now names the files a cached file is published together with
(FormatPlugin::file_releases: apt's InRelease and Release) and reads the
size and SHA-256 they list for it (file_listed). The file proxy serves a
cached file consistent with them:

- it is due once one of them was replaced after it was last checked
  (Release.gpg drifting from Release included);
- it is due when the one served lists it with another size or digest
  and was checked after it;
- when it was stored after the one served was last checked and they
  disagree, that one is re-validated first.

Upstreams with by-hash indexes (Debian, Ubuntu) are served as before.

Fixes #798

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012e6DJYtnmoYtz7o9Mp5FTy
@schmitch
schmitch force-pushed the claude/798-apt-proxy-index-consistency branch from 4306c74 to 84ad26c Compare November 4, 2026 06:31
@schmitch
schmitch added this pull request to the merge queue Nov 4, 2026
Merged via the queue into main with commit b0662b3 Nov 4, 2026
19 checks passed
@schmitch
schmitch deleted the claude/798-apt-proxy-index-consistency branch November 4, 2026 07:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants