How upstream package resolution works
When a PyPI, npm, or Maven repository has connected sources, Ravenstash chooses one source for each package name. It makes that choice before looking for a version or file.
This package-level behavior is the default. It keeps resolution predictable and prevents a higher version in a lower-priority source from replacing a package your organization intended to use.
You can change the behavior for specific package names with package resolution rules: limit which sources may supply a name, or let several sources each supply different versions of it with Version-level resolution. Packages that no rule matches always resolve as described in the next sections.
Container images and Helm charts do not use upstreams or package resolution rules.
| I want to | Read |
|---|---|
| Understand why a package or version comes from one source | Source order and What a shadowed package means |
| Keep the names I own away from public registries | Reserve a naming space you own |
| Serve my patched versions next to the public ones | Serve patched versions of a public package |
| Find out why a version is missing | Troubleshoot an unexpected result |
Source order
Section titled “Source order”Each package format has its own ordered list:
- Packages published directly to the repository always come first.
- Connected sources follow in their configured order, from position 1 through position 4.
A connected source can be another private repository or a private mirror for an official or custom package source. It must use the same package format as the destination repository.
When another private repository is connected as a source, only packages published directly to that repository are included. Ravenstash does not follow that repository’s own connected sources. This prevents hidden or circular source chains.
How Ravenstash selects a source
Section titled “How Ravenstash selects a source”For each requested package name, Ravenstash:
- Applies the package format’s normal name rules.
- Checks whether the destination repository contains that package.
- If it does not, checks connected sources in position order.
- Selects the first source where the package exists.
- Resolves the package’s metadata, versions, and files only from that selected source.
Unless a Version-level rule matches the package, Ravenstash does not combine
versions from several sources. If the selected
source contains version 1.8.0 and a later source contains version 2.4.0, only
1.8.0 is available through that repository. Asking for exactly 2.4.0 does
not make Ravenstash switch to the later source.
A version that is missing, too new, or blocked in the selected source does not send the request on to a later source.
A rule changes how the packages it matches are resolved in one repository. You manage rules on the repository’s Rules tab, per package format. A rule has three parts:
- Match: one exact package name, or the beginning of a name followed by
*, such asacme-*,@acme/*, orcom.acme:*. - Upstreams: which connected sources may supply those packages.
- Mode: Package-level or Version-level.
| Upstreams choice | Sources that may supply matching packages |
|---|---|
| All upstreams | This repository and every connected source. Offered with Version-level mode; with Package-level mode it is the default and needs no rule. |
| Ravenstash repositories only | This repository and connected private Ravenstash repositories. Never a public registry or custom package source. |
| Selected upstreams | This repository and the upstreams the rule names. |
| This repository only | Only packages published directly to this repository. |
A rule filters the sources. It never changes their order, and this repository always comes first.
When several rules match a package, exactly one applies: an exact name wins over any prefix, and among prefixes the longest one wins.
A source that a rule leaves out is not consulted at all for matching packages. It stays connected for every other package.
To add, change, or remove rules, with worked examples for PyPI, npm, and Maven, see Package resolution rules.
Version-level resolution
Section titled “Version-level resolution”With Package-level mode, the first allowed source that has the package supplies all of its versions, exactly as described above.
With Version-level mode, every allowed source can supply versions of the package. For each version, the first source in order that has that version supplies it:
- Different versions of one package can come from different sources.
- One version, with all of its files, always comes from exactly one source. Ravenstash never mixes files from two sources for the same version.
- When two sources have the same version, the earlier source wins and the later
copy is shadowed. For PyPI, equivalent spellings such as
1.0and1.0.0count as the same version. For npm and Maven, a version is the same only when it is written identically.
Suppose a repository connects two private repositories and an official mirror,
and does not contain @acme/lib itself:
| Source | Versions it has |
|---|---|
| Position 1: private repository A | 1.0, 1.1 |
| Position 2: private repository B | 1.1, 2.0 |
| Position 3: official npm mirror | 1.0, 3.0 |
Rule for @acme/* |
Versions available through the repository |
|---|---|
| No rule | 1.0 and 1.1 from A. |
| Package-level, Ravenstash repositories only | 1.0 and 1.1 from A. The mirror is never used, even for a name A and B do not have. |
| Version-level, Ravenstash repositories only | 1.0 and 1.1 from A, and 2.0 from B. B’s 1.1 is shadowed. The mirror is never used. |
| Version-level, All upstreams | The same, plus 3.0 from the mirror. The mirror’s 1.0 is shadowed. |
A connected private repository still contributes only the packages published directly to it. Its own connected sources and its own rules are not used.
There is no fallback
Section titled “There is no fallback”Version-level resolution is not a fallback. Once a source has a version, that version comes from that source or not at all:
- If the version is held back by package age, quarantined, blocked, or missing a file, Ravenstash does not serve the same version from a later source.
- A version that Ravenstash has already recorded from an earlier public registry or custom package source keeps its number there, even if that source later withdraws it.
- A version that is in the trash keeps its version number until it is permanently removed. This applies to the trash of this repository and of every connected private repository the rule allows. While it is in the trash, that version cannot be installed and a later source’s copy of it does not become available. Restore the version or publish a new one if you need it back.
- If a source the rule allows cannot be checked, the request fails instead of being answered without that source.
The risk of including a public registry
Section titled “The risk of including a public registry”A Version-level rule that allows a public registry or another source you do not control reopens a dependency-confusion risk for the names it matches: anyone who can publish one of those names on that registry can add version numbers that your repository will serve. Prefer Ravenstash repositories only for names your organization owns.
Ravenstash warns you before you save such a rule. See Dependency confusion and public registries for the warning and how to stay safe.
Tags and latest when sources mix
Section titled “Tags and latest when sources mix”For npm, tags such as latest and beta are chosen per tag name, in source
order:
- A tag belongs to the first source that has it. A tag set in this repository always wins over the same tag from a connected source.
- A tag is shown only when it points to a version that its own source supplies through this repository and that can be installed. Otherwise the tag is left out; it is not taken from a later source.
latestexists whenever at least one version can be installed. If the first source’slatestpoints to a version that cannot be installed, Ravenstash picks another installable version from that same source, preferring the highest stable version, and moves on to the next source only when that source has none.
This means a plain npm install <package> can install a lower version than the
highest one available. If a connected private repository says latest is
1.1 and a public registry later in the order supplies 3.0, latest stays
1.1; 3.0 is installed only when it is requested explicitly or by a version
range. Tags from connected sources are read-only; you can manage tags only for
packages published directly to the repository.
PyPI and Maven have no tags. The newest version shown for a package, and the
Maven latest and release values, are worked out from all the versions
available through the repository.
How fast changes take effect
Section titled “How fast changes take effect”| Change | Takes effect |
|---|---|
| Adding, changing, or removing a rule | Usually within a minute. |
| Publishing, deleting, restoring, or tagging a version in this repository or a connected private repository | Usually within a minute. |
| A new, removed, or re-tagged version at a public registry or custom package source | Up to about 20 minutes. |
Package managers also keep their own caches, which Ravenstash cannot clear:
- pip keeps downloaded files and built wheels in a local cache. Use
--no-cache-dirorpip cache purgewhen you test a rule change. - uv keeps its own cache. Use
--refreshoruv cache clean. - npm keeps package details and downloaded packages in a local cache. A
package that is already cached can still install from that cache, including
offline, after it stops being available through the repository. Use
--prefer-onlineornpm cache clean --forcewhen you test a rule change. - Maven keeps artifacts in
~/.m2/repositoryand, by default, checks for updated version lists once a day. Run the build with-Uto make it check again immediately. An artifact that is already in~/.m2is reused without asking the repository.
What a shadowed package means
Section titled “What a shadowed package means”A source is shadowed when it contains the same package name as a source that was selected earlier in the order.
Shadowed means:
- the source and its package have not been deleted;
- its versions are not available through this particular repository;
- its versions are not merged with versions from the selected source; and
- cached package details and security evidence can remain visible for review.
Shadowing is not a security verdict. It describes source priority. A shadowed package can still be available through another repository or through its mirror’s direct target when the user has access.
Under a Version-level rule, shadowing applies to single versions: a later source’s copy of a version is shadowed when an earlier source has that version, including when the earlier copy is in the trash. A source that a rule leaves out is shown as excluded by the rule rather than shadowed.
How the app shows it
Section titled “How the app shows it”In the repository’s Contents list, the Sources column names the sources that have a package, and a package with shadowed sources shows a note such as has 1 shadowed. On a package or version page, a copy that is not served through this repository stays listed for reference and carries one of these marks. Select the mark for an explanation.
| Mark | Meaning and what you can do |
|---|---|
| Shadowed | An earlier source has the package, or, under a Version-level rule, has this exact version. Nothing needs to change if the earlier source is the one you intend. To let several sources supply different versions, add a Version-level rule. |
| Reserved | Under a Version-level rule, an earlier source has this version in its trash. The version number stays taken until that copy is permanently deleted. Restore the version from the trash, or publish a new version number. |
| Excluded | A package resolution rule does not use this source for this package. Open the rule from the explanation and change it if the source should supply the package. |
See See which source serves a version for the labels a package governed by a rule shows.
Resolution example
Section titled “Resolution example”Suppose a repository has this source order for acme-utils:
| Order | Source and package | Result |
|---|---|---|
| First | This repository: absent | Continue. |
| Position 1 | Team foundations repository: 1.8.0 |
Selected. |
| Position 2 | Official mirror: 2.4.0 |
Shadowed. |
| Position 3 | Custom mirror: absent | Not used. |
An install through the destination repository can resolve acme-utils version
1.8.0. Version 2.4.0 remains unavailable there even though it is newer. This
is intentional: source order has priority over version order.
Why private package names take priority
Section titled “Why private package names take priority”If a package is published directly to the destination repository, it is selected before every connected source. All versions of the same package name in connected sources become shadowed.
This prevents a dependency-confusion attack in which someone publishes a public package with the same name and a higher version number. The public version does not replace or extend the private package.
A deleted package stays in the repository trash for seven days and keeps its name during that time. Once it is permanently deleted, the name is released and the first connected source that contains the package can become selected.
That protection covers names the repository already contains. To keep a public registry away from names you have not published yet, see Reserve a naming space you own.
Age and Protection rules do not change the source
Section titled “Age and Protection rules do not change the source”After Ravenstash selects a source, it applies the connection’s package-age rules and the current Protection decision to versions from that source.
A version can therefore be held back by minimum package age, excluded by a maximum age, quarantined, or blocked without causing Ravenstash to try the same version from a later source. A version from an attached private repository can also be blocked in this repository only; see Package Protection. These controls affect whether a version is available; they do not merge or reorder package sources.
Direct mirror access is separate. It uses the mirror’s own access and age settings rather than a repository’s connected-source order.
Changes that can affect selection
Section titled “Changes that can affect selection”The selected source can change when you:
- publish the package directly to the destination repository;
- permanently delete the package that currently takes priority;
- add, remove, or reorder a connected source;
- make the package available in an earlier source; or
- add, change, or remove a rule that matches the package.
Ravenstash checks earlier sources before relying on a previously observed lower-priority selection. Cached metadata and files improve repeat access, but they do not permanently override the configured order.
Troubleshoot an unexpected result
Section titled “Troubleshoot an unexpected result”- Open the repository’s Contents list and check the package’s Sources column.
- Open the package. Its Resolution panel names the source that serves it and the rule that applies, and versions that are not served are marked Shadowed, Reserved, or Excluded.
- Open the repository’s Upstreams tab and confirm the source positions for that package format.
- Open the Rules tab and check whether a rule matches the package name. An exact name wins over a prefix, and the longest prefix wins.
- Check whether the same package name was published directly to the repository or exists in an earlier connected source. Under a Version-level rule, check whether an earlier source has the same version, including in its trash.
- Confirm the requested version exists in the selected source and satisfies that connection’s age rules.
- If a source is temporarily unavailable, retry once it is reachable again. Under a Version-level rule, the request fails instead of being answered without that source.
- Clear or refresh the package manager’s local cache before testing a changed source order or rule; see How fast changes take effect.
For rule-specific questions, see Questions and troubleshooting.
For source setup, see Configure official private mirrors and Connect a custom package source. To change how specific packages resolve, see Package resolution rules.

