Package resolution rules
A package resolution rule changes how one repository resolves the PyPI, npm, or Maven packages the rule matches. Use rules to:
- Reserve a naming space. Keep names such as
acme-*,@acme/*, orcom.acme:*to your own repositories, so a public registry can never supply them. - Limit a package to reviewed sources. Allow only the upstreams you select.
- Combine versions from several sources. Let each source add the versions that earlier sources do not have, with Version-level resolution.
An upstream is a source connected to a repository on its Upstreams tab: another private Ravenstash repository, or a private mirror of a public registry or custom package source. The app calls public registries and custom package sources remote sources.
Packages that no rule matches keep the default behavior: the repository’s own packages come first, then the first upstream that has the package supplies all of its versions. See How upstream package resolution works for the source order and for what “shadowed” means.
Before you start
Section titled “Before you start”- Where: open the repository in the Ravenstash app and select the Rules tab, next to Upstreams.
- Formats: PyPI, npm, and Maven. The tab appears when the repository has at least one of them. Container images and Helm charts do not use rules.
- Who can view rules: everyone who can read the repository.
- Who can change rules: the same people who manage the repository’s upstreams. In an organization, that is the repository Maintainer or Admin role, organization administrators, and namespace administrators in their namespaces. The owner of a personal account can change everything. See Roles and grants.
- Tools: rules are managed in the web app. The
rvsCLI and the Developer API do not create or change rules.
If Add rule is not shown on the Rules tab, ask an organization administrator to add the rule or to review your access.
A rule belongs to one repository and one format. It never changes another repository, and it does not change who can read or publish packages.
Add a rule
Section titled “Add a rule”- On the Rules tab, select Add rule.
- Choose the Format when the repository has more than one.
- Enter the Match: a package name, or a name followed by
*to match every name that starts with it. Below the field, the dialog shows how it reads your entry, as Exact match or Prefix match, and how many known packages a prefix matches. - Choose the Mode: Package-level or Version-level.
- Choose the Upstreams that matching packages can use. For Selected upstreams, tick the sources in the list.
- Review the rule summary, any warning, and the Impact on known packages. When a warning asks for it, tick the confirmation.
- Select Add rule.
You can also start from a package: open the package in the repository and select Add rule for this package in its Resolution panel. The dialog opens with the package’s format and exact name filled in.
The parts of a rule are described under Reference. The sections below walk through the most common rules.
Reserve a naming space you own
Section titled “Reserve a naming space you own”Without a rule, a name your repository does not contain can still be supplied by a connected public registry. That includes names you have not published yet and misspelled names. Someone who registers one of your names on the public registry could then have their package installed in your builds.
Two upstream choices close that gap. With either one, a public package that uses one of your names is never served, an unknown name answers “not found,” and Ravenstash does not ask the public registry about those names at all.
| Your packages are published | Choose |
|---|---|
| Only in this repository | This repository only |
| In this repository and in other private repositories it connects to, such as a shared platform repository | Ravenstash repositories only |
To reserve a naming space:
-
Select Add rule and enter the beginning of your names followed by
*:- PyPI:
acme-*coversacme-utils,acme-billing-client, and every other name that starts withacme-. - npm:
@acme/*covers every package in the@acmescope. - Maven:
com.acme:*covers every artifact in thecom.acmegroup.
- PyPI:
-
Under Upstreams, choose This repository only, or Ravenstash repositories only with Package-level mode.
-
Check the Impact on known packages, then select Add rule.
A rule can match names that do not exist yet, so you can reserve a naming space before you publish anything in it.
Serve patched versions of a public package
Section titled “Serve patched versions of a public package”You maintain a few patched builds of a public package and still want the public versions. By default this does not work: once your repository contains the package, it supplies every version, and the public versions are shadowed.
A Version-level rule lets both sources supply versions:
-
Publish your patched versions to the repository. Give each one a version number the public registry does not use. For example:
- PyPI: the public
example-http-clienthas1.4.1,1.4.2, and1.5.0. You publish1.4.2.post1. - npm: the public
example-queuehas2.8.0,2.8.1, and2.9.0. You publish2.8.1-acme.1. - Maven: the public
org.example:parserhas3.2.0,3.2.1, and3.3.0. You publish3.2.1-acme-1.
- PyPI: the public
-
Select Add rule and enter the exact package name:
example-http-client,example-queue, ororg.example:parser. -
Choose Version-level.
-
Under Upstreams, choose Selected upstreams and tick the mirror of the public registry. Choose All upstreams only when every connected source, including ones you connect later, should supply versions.
-
Read the Dependency confusion risk warning, tick the confirmation under This broadens the trusted supply chain, and select Add rule.
The repository now serves your patched version together with the public
versions. In the PyPI example, 1.4.1, 1.4.2, and 1.5.0 come from the
public registry and 1.4.2.post1 comes from your repository. Keep these points
in mind:
- Your repository comes first. If the public registry later publishes a version number you already use, your version keeps being served.
- Pin the patched version. A version range can select a newer public version. Request your build by its exact version, and keep a lockfile.
- npm
latest. Alatesttag in your repository wins over the public one, so a plainnpm install <package>can install your patched build. See Tags andlatest. - Use an exact name. Do not use a prefix for this rule unless every matching name is a public package you intend to mix. Read Dependency confusion and public registries first.
Limit a package to a selected upstream
Section titled “Limit a package to a selected upstream”Some packages must come from one reviewed source, for example a vendor’s artifacts that should never be taken from a public registry.
- Select Add rule and enter the name or naming space, such as
com.vendor:*,@vendor/*, orvendor-sdk. - Choose Package-level.
- Under Upstreams, choose Selected upstreams and tick the sources that may supply these packages.
- Check the Impact on known packages, then select Add rule.
Matching packages now resolve only from this repository and the upstreams you ticked, in the order they have on the Upstreams tab. The other upstreams stay connected for every other package. An upstream you connect later is not included until you edit the rule.
Combine versions across your own repositories
Section titled “Combine versions across your own repositories”Several of your repositories publish different versions of the same package, for example a shared platform repository and a team repository.
- Select Add rule and enter the name or naming space.
- Choose Version-level. The dialog selects Ravenstash repositories only for you.
- Tick the confirmation under This broadens the trusted supply chain, then select Add rule.
Each version now comes from the first of your repositories that has it, and no public registry or custom package source can add versions.
See which source serves a version
Section titled “See which source serves a version”After you add a rule, the app shows the result in three places.
Contents list. In the Packages & paths view, the Sources column shows how a package governed by a rule resolves:
| Label | Meaning |
|---|---|
| Mixed by version | More than one source serves versions of the package. |
| Version-level · upstream #2, or another single source | A Version-level rule applies, and one source serves every known version. |
| Version-level · no version served | A Version-level rule applies, and no source has a version that can be served. |
| Package-level or This repository only | A rule with that setting applies. |
| 2 sources contribute, 3 shadowed versions, 1 excluded source | How many sources serve versions, how many copies an earlier source already supplies, and how many upstreams the rule leaves out. |
| Rule: and a match | The rule that applies. Select it to open that rule on the Rules tab. |
Package page. The Resolution panel states the mode, which sources serve the package, and the rule that applies. Each source in the version list has a state:
| State | Meaning |
|---|---|
| Serving | The only source that serves versions of the package here. |
| Contributing | One of several sources that serve versions. The row says how many versions it serves and how many of its copies are shadowed. |
| Shadowed | Earlier sources supply every version this source has. |
| Excluded | The rule does not use this source. Its versions stay listed for reference. |
| No versions | The source has nothing to serve for this package. |
Version page. A statement at the top says whether the copy you are viewing is served:
| Statement | Why |
|---|---|
| Served here. | This source is the first one that has the version. |
| Shadowed: not served here. | An earlier source has the same version, or has it in its trash. |
| Excluded: not served here. | The rule that applies does not use this source. |
In version lists, a copy that is not served is marked Shadowed, Reserved (an earlier source has the version in its trash), or Excluded. Select the mark for an explanation and, when a rule applies, a link to it.
Known versions only means the result is based on the versions seen so far. An upstream can hold versions that have not been requested through this repository yet.
Change, rename, or remove a rule
Section titled “Change, rename, or remove a rule”Use Edit rule or Remove rule in the rule’s row.
- Edit changes the match, mode, or upstreams. A rule’s format cannot be changed; remove the rule and add one for the other format.
- Rename a rule by editing its match. Packages that only the previous match covered go back to the rule that otherwise applies, or to the default. The dialog names the previous match under Which rule governs matching packages.
- Remove returns matching packages to the rule that otherwise applies, or to the default. Removing a rule is how you undo it; nothing else is changed or deleted.
Before you save, Ravenstash checks what the change does:
| The change | What you see |
|---|---|
| Lets more sources supply versions: Version-level mode, a wider upstream choice, more selected upstreams, or removing or renaming a rule that limited sources | This broadens the trusted supply chain, naming the sources that become eligible where it can. Tick the confirmation to continue. |
| Anything else, such as Package-level mode, fewer upstreams, or This repository only | Impact on known packages: how many known packages are affected, and how many versions will no longer be installable or will become installable. |
A change can do both, for example removing a Version-level rule that allowed only Ravenstash repositories. The dialog then shows the warning and, when known packages are affected, the impact.
Known packages are the ones Ravenstash has already seen: packages in this
repository, in connected Ravenstash repositories, and versions from other
sources that were already requested through this repository. A count ending in
+ means there are more than the dialog counts.
A lockfile that pins a version that stops being installable will fail to install. Check the impact before you narrow a rule that builds depend on.
If someone else changes the repository’s upstreams or rules while you are editing, Ravenstash asks you to review the rule and save again.
Dependency confusion and public registries
Section titled “Dependency confusion and public registries”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 a package with one of those names on that registry can add new version numbers, and your repository will serve them. A package manager that asks for the newest matching version may then install the outside version instead of yours.
This is why Version-level mode starts with Ravenstash repositories only selected: versions can then only come from repositories your organization controls.
When a rule would include such a source, the dialog shows a Dependency confusion risk warning that names each one before you save. In the rule list, the rule is marked Includes a remote source. To stay safe:
- Prefer Ravenstash repositories only for names your organization owns.
- Include a public registry in a Version-level rule only for a package that genuinely exists there, such as a public package for which you publish patched versions privately. Prefer an exact name over a prefix.
- Keep a minimum package age on the connection and pin versions in lockfiles.
Such a rule also means Ravenstash asks that registry about each matching name that is requested, even when your repository already has the package. Those names become visible to the registry’s operator.
Reference
Section titled “Reference”Rule list
Section titled “Rule list”The Package resolution list shows one row per rule:
| Column | What it shows |
|---|---|
| Format | PyPI, npm, or Maven. A rule belongs to one format. |
| Match | The exact package name, or Starts with and the beginning of a name. |
| Mode | Package-level or Version-level. A dash for This repository only. |
| Upstreams | The upstream choice, or the number of selected upstreams. A Version-level rule that allows a public registry or custom package source is marked Includes a remote source. |
| Known packages | How many packages Ravenstash already knows that the rule matches. |
| Last change | When the rule last changed. |
Filter the list by format, or search it by the text of a rule’s match. To find the rule that applies to one package, open the package: its Resolution panel names the rule and links to it.
A match is either one exact package name or the beginning of a name followed by
a single *.
| Format | Examples |
|---|---|
| PyPI | Exact name: acme-utils. Starts with: acme-*. |
| npm | Exact name: acme-ui or @acme/ui. Starts with: @acme/* for a whole scope, or @acme/ui-* for part of it. |
| Maven | Exact name: com.acme:utils, written as groupId:artifactId. Starts with: com.acme:* for one group, com.acme.* for every group below it, or com.acme:util-* for part of a group. |
- The
*is allowed once, at the end. A*anywhere else, or more than one, is refused. No other wildcards are supported. - At least one character must come before the
*, so a rule can never match every package. - The name must be valid for the format. A Maven exact name needs both parts,
groupId:artifactId, and an npm scope needs a package name after it:@acme/uior@acme/*, not@acme. - Matching ignores letter case. For PyPI,
-,_, and.are also the same character, soAcme_Utilsandacme-utilsare one name andacme-*covers both. - A prefix is plain text.
acme*also matchesacmecorp-tools, and@acme*also matches the scope@acme-labs. End the prefix at a separator, as inacme-*,@acme/*,com.acme:*, orcom.acme.*, to match only your own naming space. - For Maven,
com.acme:*covers the groupcom.acmeonly, andcom.acme.*covers the groups below it, such ascom.acme.billing. Add both rules to cover the group and everything below it. - A match can be up to 255 characters, including the
*.
| Mode | What it does |
|---|---|
| Package-level | The first allowed source that has the package supplies all of its versions. This is how packages without a rule resolve. |
| Version-level | Every allowed source adds the versions that no earlier source already provides. |
Package-level with All upstreams is the default for every package, so it is not offered as a rule. This repository only has no mode, because matching packages come from one place.
With a Version-level rule, each version comes from the first allowed source, in upstream order, that has that version:
- Different versions of one package can come from different sources.
- One version, with all of its files, always comes from exactly one source.
- When two sources have the same version, the earlier source wins and the later copy is shadowed.
Version-level resolution is not a fallback. Once a source has a version, that version comes from that source or not at all:
- A version that is held back by package age, quarantined, or blocked is not served from a later source instead.
- 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 in the trash keeps its version number until it is permanently removed, in this repository and in every connected private repository the rule allows. Restore it or publish a new version if you need it back.
- If an allowed source cannot be checked, the request fails instead of being answered without that source.
For a worked example and for npm tags and latest, see
Version-level resolution.
Upstreams
Section titled “Upstreams”| Choice | What matching packages can use |
|---|---|
| All upstreams | This repository and every upstream of that format, including upstreams you connect later. Use it with Version-level mode, when you deliberately want versions from every source. |
| Ravenstash repositories only | This repository and every connected private Ravenstash repository, including ones you connect later. Never a public registry or a custom package source. Use it for names your organization owns that are published across several of your repositories. |
| Selected upstreams | This repository and only the upstreams you tick. Upstreams you connect later are not included until you edit the rule. Use it for a fixed, reviewed set of sources. |
| This repository only | Only packages published directly to this repository. Use it for names your organization owns and publishes only here. |
This repository is always included and cannot be deselected. The choice filters the upstreams; it never changes their order. Selected upstreams keep the positions they have on the Upstreams tab, and reordering upstreams or changing their package age does not change a rule.
An upstream that a rule leaves out is not consulted at all for matching packages. It stays connected for every other package, and a problem with that upstream does not affect the packages it is left out of.
A connected private repository contributes only the packages published directly to it. Its own upstreams and its own rules are not used.
Which rule applies when several match
Section titled “Which rule applies when several match”Exactly one rule applies to a package:
- A rule for the exact name wins over any prefix.
- Among prefixes, the longest one wins.
With rules for @acme/*, @acme/ui-*, and @acme/ui-icons:
| Package | Rule that applies |
|---|---|
@acme/ui-icons |
@acme/ui-icons |
@acme/ui-forms |
@acme/ui-* |
@acme/cli |
@acme/* |
The whole winning rule applies. Nothing is combined from the other rules, and there is no rule order to maintain.
A format has one rule per match. If you add a rule whose match already exists, the dialog says that it replaces the existing rule. Changing a rule’s match to one that another rule already uses is refused; edit that rule instead.
When another rule also covers the packages you are matching, the dialog shows Which rule governs matching packages. A package page names the rule that applies to it and links to that rule.
Remove an upstream that a rule selects
Section titled “Remove an upstream that a rule selects”You cannot remove an upstream while a Selected upstreams rule names it. On the Upstreams tab, Ravenstash keeps your unsaved changes and lists the rules under Rules still select an upstream you removed. Change or remove those rules on the Rules tab, then save the upstream change again.
An upstream that rules select shows a link such as 2 rules on the Upstreams tab.
A rule is never widened automatically. If a selected source is itself deleted for good, the rule loses that selection and keeps the others; a rule left with no selected upstream resolves from this repository only until you edit it.
All upstreams and Ravenstash repositories only rules do not block removing an upstream. They follow whatever is connected.
When changes take effect
Section titled “When 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. Refresh them when you test a rule change:
| Tool | What to do |
|---|---|
| pip | Add --no-cache-dir, or run pip cache purge. |
| uv | Add --refresh, or run uv cache clean. |
| npm | Add --prefer-online, or run npm cache clean --force. |
| Maven | Run the build with -U. Artifacts already in ~/.m2/repository are reused. |
A lockfile records the exact file it installed. If a rule change makes a different source supply a version number that a lockfile already pins, the file can differ and the package manager reports a checksum or integrity mismatch. Update the lockfile after you confirm the new source is the one you intend.
Limits
Section titled “Limits”- Up to 50 rules for each format in a repository.
- One rule per match in a format.
- A match can be up to 255 characters.
- Up to four upstreams for each format, so a rule can select at most four.
- Rules apply to PyPI, npm, and Maven. Container images and Helm charts do not use them.
- Rules are managed in the web app only.
Questions and troubleshooting
Section titled “Questions and troubleshooting”A version I expect is missing
Section titled “A version I expect is missing”- Open the package in the repository and find the version. A copy marked Shadowed, Reserved, or Excluded exists in a source but is not served through this repository; select the mark to see why.
- Excluded: the rule that applies leaves that upstream out. Edit the rule, or add a more specific rule for this package.
- Shadowed without a Version-level rule: an earlier source has the package and supplies all of its versions. Add a Version-level rule if several sources should supply versions.
- Shadowed or Reserved under a Version-level rule: an earlier source has the same version number, possibly in its trash. Restore that version or publish a new version number.
- Not listed at all: check the connection’s minimum and maximum package age on the Upstreams tab, and whether the version is quarantined or blocked on the Protection tab.
- Refresh the package manager’s cache and retry. See When changes take effect.
An unexpected version was installed
Section titled “An unexpected version was installed”Open the version in the repository to see which source serves it. If a public
registry supplies versions of a name you own, a Version-level rule includes that
registry, or no rule reserves the name. See
Reserve a naming space you own. For npm, also
check which source owns the latest tag.
A name I reserved answers “not found”
Section titled “A name I reserved answers “not found””That is the rule working: the name is not published in the sources the rule allows, and public registries are not asked. If the package lives in a connected Ravenstash repository, change the rule from This repository only to Ravenstash repositories only.
The dialog refuses my rule
Section titled “The dialog refuses my rule”| Message | What to do |
|---|---|
| Use * only once, at the end of the name. | Put a single * at the end. Patterns such as *-utils or acme-*-client are not supported. |
| Add at least one character before *. | A rule cannot match every package. Enter the beginning of a name. |
| This match isn’t valid. | Enter a name that is valid for the format; see Match. |
| A rule for this match already exists. Edit that rule instead. | Another rule already uses the match you are renaming this rule to. |
| Package-level over all upstreams is already the default. | Choose different upstreams, or Version-level mode. |
| Select at least one upstream. | Tick a source under Selected upstreams, or connect one on the Upstreams tab first. |
| This format has reached its rule limit. | A format holds up to 50 rules. Remove a rule, or replace several exact rules with one prefix. |
| A selected upstream is no longer attached. | The upstream was removed while you were editing. Review the selection and save again. |
| This repository’s upstreams or rules changed while you were editing. | Someone else saved a change. Review the rule and save again. |
I changed a rule and my build has not changed
Section titled “I changed a rule and my build has not changed”A rule change usually takes effect within a minute. After that, the package manager’s own cache or a lockfile is the usual cause. See When changes take effect.
Does a rule in a connected repository apply here?
Section titled “Does a rule in a connected repository apply here?”No. A connected private repository contributes only the packages published directly to it. Each repository uses its own rules.
Can I use rules for container images or Helm charts?
Section titled “Can I use rules for container images or Helm charts?”No. Rules apply to PyPI, npm, and Maven.

