Repository navigation
apt: control files are capped at 64 KiB and indexes are rendered one list at a time - #895
Merged
Merged
Conversation
schmitch
force-pushed
the
claude/797-apt-index-memory-bound
branch
from
November 4, 2026 00:18
b53c757 to
017a61a
Compare
github-merge-queue
Bot
removed this pull request from the merge queue due to a conflict with the base branch
Nov 4, 2026
A package's control file could be 1 MiB, and an index generation held every list's text, a copy and a by-hash clone at once: a writer's tiny packages with large descriptions OOM-killed the instance on every index request. - deb.rs refuses a control file over 64 KiB (400 on upload and import). - FormatPlugin::index hands its files to a sink one at a time; the host signs, checks and stores each as it comes, so a generation holds one list and its copies, not the whole index. - IndexInput::max_bytes: apt checks what its Packages lists will hold before it makes any. - A package stored before with control data over twice the cap is left out of the indexes instead of failing them. Fixes #797 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012e6DJYtQDk1mTgJAn2noU2
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012e6DJYtQDk1mTgJAn2noU2
schmitch
force-pushed
the
claude/797-apt-index-memory-bound
branch
from
November 4, 2026 06:13
017a61a to
23d9d19
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #797
What changed
MAX_CONTROL_BYTES(crates/formats/apt/src/control.rs) is 64 KiB instead of 1 MiB.deb.rsnow refuses a largercontrolfile with "the package's control file is larger than 64 KiB", and uploads and imports turn that into a400(both read packages throughparse_package).FormatPlugin::indexused to return aVec<IndexFile>. It now hands each file to a sink (&mut dyn FnMut(IndexFile) -> Result<(), FormatError>). The host (crates/server/src/indexes/mod.rs) signs, checks and stores each file in the object store as it arrives, then drops its content, and records the generation in one transaction as before. apt renders one(component, architecture)list at a time: it builds the text, compresses it, hands over the plain, gz and xz files with their by-hash copies, and drops them before starting the next list. Memory now holds each stanza once plus one list's files, not every list's text, a copy and a by-hash clone at once.MAX_INDEX_FILESandMAX_INDEX_BYTESthatsign_and_checkused to check once everything was in memory are now checked file by file (Bounds). An overrun stops the plugin through the sink's error.IndexInputhas a new field,max_bytes, which the host sets toMAX_INDEX_BYTES. Before making any list, apt adds up the stanza bytes of every list and doubles it, because eachPackagesis stored by name and by hash. If that is overmax_bytes, it refuses without compressing anything.MAX_LISTED_CONTROL_BYTES(2 × the cap), apt leaves it out of the indexes instead of failing or bloating every generation. It stays in the pool (and in browse) until it is deleted. The limit is twice the cap because the stored control data is a canonical respelling that can be a few bytes longer than the uploaded file, so no package accepted under the new cap is ever left out. apt is new in 2.2.0, so only deployments of the 2.2.0 development line can have such packages. The format crates have no logging, so nothing is logged when a package is left out.docs/apt.mddocuments the rule.Tests
crates/server/tests/apt.rs(hosted_repositories_publish_signed_indexes): an upload whose control file is just over 64 KiB answers400with "larger than 64 KiB". With the cap set back to 1 MiB it answers201and the test fails.crates/formats/apt/src/deb.rs,a_control_file_over_the_cap_is_refused: a control file of exactly the cap is read, and one byte more is refused. It fails with the cap back at 1 MiB.crates/formats/apt/src/index.rs:an_index_over_the_byte_cap_fails_before_anything_is_compressed: the sink receives no file and the error names the cap. With the size check disabled,renderreturnsOkand the test fails.lists_are_handed_over_one_at_a_time: a sink error after the first list's six files stops rendering before the next list.a_package_stored_over_the_cap_is_left_out: a package with control data over the listing limit is not listed, and one at the upload cap is.control.rskeeps its 1 MiB input as a local constant, since index text can be that large.cargo fmt --all --check;cargo clippy -p forge-format-apt -p forge-plugin-api -p forge-server --all-targets -- -D warnings;cargo test -p forge-format-apt(69 passed);cargo test -p forge-plugin-api;cargo test -p forge-server --test apt --test apt_import(13 and 3 passed);cargo xtask changelog --check.Changelog
docs/changes/2.2.0/797.fixed.md.docs/apt.md(Uploading packages) lists the new 64 KiB limit and says what happens to older, larger packages.docs/format-plugins.md(Signed indexes) describes the streamingindexcontract andmax_bytes.Not fixed here
MAX_INDEX_BYTES(1 GiB), and it is held about twice while it is compressed and handed over. Reaching that takes some 8,000 packages at the cap in one list. apt: a writer can break a hosted repository's indexes for everyone with packages of many components and architectures #761 bounds the number of index files, not their size; a per-list bound would be a separate change.MAX_INDEX_FILESfiles.🤖 Generated with Claude Code
https://claude.ai/code/session_012e6DJYtQDk1mTgJAn2noU2