feat(upstreams): route by priority and package-name globs, not by alphabet - #79
Merged
Merged
Conversation
|
Warning Review limit reachedNext included review available in 29 minutes. View limit detailsLimit details: You’ve used the included review currently available. You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Review configuration: ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (11)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
BirknerAlex
force-pushed
the
feat/upstream-priority-and-package-patterns
branch
from
September 9, 2026 15:29
a2a4271 to
ecbbcbc
Compare
…habet Two upstreams of one format were tried in name order, and every one of them was asked about every package. That is fine for interchangeable mirrors and wrong for anything else -- a vendor registry holding one scope next to a public registry holding the rest. It fails quietly rather than loudly. A vendor registry typically answers for public names too, by proxying or 302-redirecting to the public registry, and reqwest follows redirects: so the first upstream by name wins every package, the packages are cached through *its* entry, and the whole repo ends up attributed to it in `silo list`. A production instance with `fontawesome` and `npmjs` configured had 69,393 synced rows against npmjs's 7,098, and 118 of 124 stored packages -- @babel/core, @eslint/*, @nuxt/image -- recorded as coming from the vendor registry, which had in fact redirected every one of them to npmjs. Two columns, both defaulting to today's behaviour: - `priority`, tried highest first, ties broken by name, so the order is explicit instead of an accident of the alphabet and reordering does not mean renaming. - `package_patterns`, globs (`*`, `?`) the package name must match for an upstream to be consulted at all. Empty means no restriction, so nothing changes until an operator opts in. Repeatable, so one upstream can hold several scopes. The filter is applied everywhere the package name is known: the artifact miss path, npm's lazy per-name sync, and the index merge -- an upstream must not advertise a name it is not allowed to serve, or a client would ask for it and be turned away after the fact. `update_upstream` takes its settings as a struct rather than a tenth positional argument. Verified against a running server with a registry that 302s unknown names to npmjs, exactly as the vendor one does: without a pattern, `left-pad` and `ms` are stored as `upstream:vendor`; with `--package-pattern '@vendor/*'` they are `upstream:npmjs` and the vendor registry is not asked about them at all. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
BirknerAlex
force-pushed
the
feat/upstream-priority-and-package-patterns
branch
from
September 9, 2026 15:59
ecbbcbc to
3b4b02d
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.
Two upstreams of one format were tried in name order, and every one of them was asked about every package. That is fine for interchangeable mirrors and wrong for anything else — a vendor registry holding one scope next to a public registry holding the rest.
It fails quietly rather than loudly. A vendor registry typically answers for public names too, by proxying or 302-redirecting to the public registry, and reqwest follows redirects. So the first upstream by name wins every package, the packages are cached through its entry, and the whole repo ends up attributed to it in
silo list.What this looked like in production
A silo with
fontawesomeandnpmjsconfigured on one channel:118 of 124 stored npm packages —
@babel/core,@eslint/eslintrc,@nuxt/image,vue— recorded asupstream:fontawesome.fontawesomesorts beforenpmjs, andhttps://npm.fontawesome.com/<public-package>answers302 → registry.npmjs.com, so it won every name and silo cached npmjs's packages through the vendor entry. The operator'sno_cachesetting onnpmjswas bypassed as a side effect, because everything arrived through the cache-mode entry instead.(The vendor credential is not leaked doing this — reqwest strips
Authorizationon a cross-host redirect.)Change
Two columns on
upstreams, both defaulting to today's behaviour so an existing deployment is unaffected until someone opts in:priority— tried highest first, ties broken by name. The order becomes explicit instead of an accident of the alphabet, and reordering no longer means renaming.package_patterns— globs (*,?) the package name must match for an upstream to be consulted at all. Empty means no restriction. Repeatable, so one upstream can hold several scopes.silo repo list-upstreamsnow shows both, in the order upstreams are actually tried:The pattern filter is applied everywhere the package name is known: the artifact miss path, npm's lazy per-name sync, and the index merge — an upstream must not advertise a name it is not allowed to serve, or a client would ask for it and be turned away after the fact.
update_upstreamtakes its settings as a struct rather than a tenth positional argument.The glob matcher is hand-rolled (~20 lines, two metacharacters, no new dependency).
*spans/so@acme/*covers a whole scope; the match is against the whole name, not a prefix.Tests
upstream_serves, including the case this exists for:@fortawesome/*must not match@babel/core.upstreams_are_listed_highest_priority_first_then_by_nameandrouting_settings_round_trip_and_default_to_unrestrictedat the DB layer.an_upstream_scoped_to_a_pattern_is_not_consulted_for_other_namesbuilds the production shape — a vendor registry that also answers for public names, given the higher priority and the earlier name, so only the pattern keeps it away — and asserts each package'sorigin_upstream_idplus that the vendor registry was never asked. Without the filter it fails withlodash was served by the wrong upstream: left: "vendor", right: "public".Verified against a running server with a registry that 302s unknown names to npmjs: without a pattern,
left-padandmsare stored asupstream:vendor; with--package-pattern '@vendor/*'they areupstream:npmjsand the vendor registry is not asked about them at all.cargo test --workspace(484 tests),cargo clippy --workspace --all-targets -- -D warnings,cargo fmt --checkandci/check-chart.pyare clean.Not included
The underlying reason a redirect can silently re-route a request — silo follows cross-host redirects on metadata fetches and still attributes the result to the upstream it started from — is untouched. A pattern now prevents it for anyone who sets one; without one, the misattribution is still possible.
The wiki has no page covering pull-through at all (
Introduction.mdstill lists "mirroring or upstream proxying" as out of scope), so there is nowhere to put these two flags. Worth a page of its own rather than a line squeezed in here.🤖 Generated with Claude Code