Repository navigation
[Content Addressable] Ruby ABI hardening across indexes, APIs, events and admin - #870
Merged
jenshenny merged 6 commits intoAug 11, 2026
Conversation
Multiple Ruby ABI variants of a gem share a number and platform, so they produced duplicate rows in specs.4.8.gz and latest_specs.4.8.gz. Legacy clients also cannot install them: the required rubygems version floor rejects them and their download path is content-addressed rather than platform-based, so resolving the legacy index entry would 404.
jenshenny
force-pushed
the
jenshenny/ruby-abi-hardening
branch
from
August 11, 2026 21:27
bcb214a to
3f64d7e
Compare
Multiple Ruby ABI variants of a version share a number and platform, so API consumers and webhook receivers could not tell them apart. Include ruby_abi in the version payload (/api/v1/versions and the v2 version endpoint) and in the rubygem payload, which feeds /api/v1/gems and the push and yank webhooks.
ruby_abi is derived server-side so the push path can only produce X.Y values, but nothing stopped console sessions or backfills from persisting arbitrary strings that would flow into the compact index and version identities. Validate the format like sha256 and content_address.
Rubygem#find_public_version resolved by number and platform only, so the v2 version and contents endpoints returned an arbitrary variant when multiple Ruby ABI builds of a gem coexist. Accept a ruby_abi param, scoped to nil by default so existing lookups are unchanged, matching the deletions API.
The yank, unyank and yank-forbidden events carry the Ruby ABI so variants sharing a number and platform can be told apart, but the pushed event only had the sha256 to distinguish them. Record the ABI there too for symmetric auditing.
Version event rows link to the version and render to_title, which includes the Ruby ABI, but the fallback text for hard-deleted versions was built from number and platform only, making yanked ABI variants indistinguishable in the gem history. Include the ABI in the fallback, and surface ruby_abi and content_address on the Avo version resource.
jenshenny
force-pushed
the
jenshenny/ruby-abi-hardening
branch
from
August 11, 2026 21:33
3f64d7e to
723bcd8
Compare
jenshenny
merged commit Aug 11, 2026
0710e08
into
ho/feature-branch-ca-server-changes
14 checks passed
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.
rubygems#6674
Follow-ups from the deep review of the content-addressable feature, one concern per commit:
Exclude content-addressable versions from the legacy Marshal indexes
Multiple Ruby ABI variants share a number and platform, so
specs.4.8.gz/latest_specs.4.8.gzgained duplicate(name, number, platform)rows (worse in latest, where every variant is marked latest). Legacy clients can't install these gems anyway — therequired_rubygems_versionfloor rejects them, and resolving the legacy entry constructs a platform-based download path while the file is stored content-addressed → 404 mid-install. ABI versions are now excluded from all three legacy index queries.Expose
ruby_abiin the version and rubygem payloadsAPI consumers and webhook receivers could not distinguish ABI variants — they appeared as identical entries differing only in
sha. Addsruby_abitoVersion#payload(/api/v1/versions, the v2 version endpoint) and toRubygem#payload, which feeds/api/v1/gemsand the push/yank webhooks. Additive JSON/XML field.Validate the
ruby_abiformatThe push path derives
ruby_abiserver-side and can only produceX.Y, but console sessions and backfills could persist arbitrary strings that would flow into the compact index and version identities. Same defense-in-depth as thesha256andcontent_addressformat validations.Allow resolving a specific Ruby ABI variant in the v2 API
Rubygem#find_public_versionresolved by number and platform only, so the v2 version and contents endpoints returned an arbitrary variant when multiple ABI builds coexist (contents especially matters — variants have different files). Accepts aruby_abiparam, nil-scoped by default so existing lookups are unchanged, matching the deletions API semantics.Record the Ruby ABI on the pushed version event
The yank/unyank/yank-forbidden events carry the ABI for auditing; the pushed event only had
sha256to distinguish variants. Records it there too for symmetric auditing.Display the Ruby ABI for deleted versions in events and Avo
Event rows render
to_title(already ABI-aware) when the version exists, but the fallback text for hard-deleted versions was number+platform only, making yanked ABI variants indistinguishable in the gem history. The fallback now includes the ABI, and the Avo Version resource surfacesruby_abiandcontent_address(Deletion already shows its ABI).Tophat
Pushes two ABI variants and a multi-ABI version through the real pipeline, then verifies every change in this PR: legacy index exclusion, v1/v2 payloads, v2 variant resolution, pushed events, and format validation.
Run from the repo root with the server on
:3000— paste the whole block into your console:Tophat script (single paste)
Output (14 passed, 0 failed)
Avo display is a manual check:
/admin/resources/versionsshows the Ruby ABI and content address columns for the tophat gem's versions.