CEP 49 - Package-URLs (PURLs) for conda packages
| Title | Package-URLs (PURLs) for conda packages |
| Status | Accepted |
| Author(s) | Cheng H. Lee <clee@anaconda.com> |
| Created | 2026-04-02 |
| Updated | 2026-09-23 |
| Discussion | https://github.com/conda/ceps/pull/159, https://github.com/package-url/purl-spec/issues/868 |
| Implementation | NA |
Abstract
This CEP describes the Package-URL type definition for conda packages in a way that conforms to other relevant standards, including other CEPs and ECMA-427.
Motivation
Package-URLs (PURLs) provide "a standardized, URL-based syntax that uniquely identifies software packages, independent of their ecosystem or distribution channel". Users may want or need to use PURLs to identify conda packages in various contexts, including but not limited to, software bills of materials (SBOMs), vulnerability reporting, and cross-ecosystem compatibility.
However, the existing (v1.0.1) conda PURL definition fails to properly capture the multi-stakeholder nature of the conda ecosystem, various commonly-understood concepts within the ecosystem, and critically, the standards specified in other accepted CEPs. This CEP seeks to address such shortcomings with an ecosystem-approved specification for the conda PURL definition, that once approved, will be submitted for review to the PURL standard.
For an example, this CEP proposes changes to the conda PURL definition so that the PURL for
conda-forge's zlib=1.3.2=h25fd6f3_2 package, which currently would have to be written
pkg:conda/zlib@1.3.2?build=h25fd6f3_2&channel=conda-forge&repository_url=https:%2F%2Fconda.anaconda.org,
could be more succinctly and understandably written as
pkg:conda/conda-forge/zlib@1.3.2?build=h25fd6f3_2.
(In fact, a strict reading of the existing conda PURL definition would not allow a conda-forge or
other non-Anaconda channels, as it does not provide for a repository_url qualifier.)
Specification
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 (RFC2119, RFC8174) when, and only when, they appear in all capitals, as shown here.
For the purposes of this CEP, the phrase "existing conda PURL definition" refers to the
v1.0.1 revision of types/conda-definition.json, which is canonically available
in the https://github.com/package-url/purl-spec repository.
For the purposes of this CEP, references to sections of ECMA-427 are relative to the 1st Edition (December 2025) of that standard.
ECMA-427 PURL components
ECMA-427 Clause 5 ("Package-URL specification") states:
A PURL is a URL composed of seven components: scheme:type/namespace/name@version?qualifiers#subpath
For conda PURLs, these seven components are defined as follows:
scheme component
The scheme component for a conda PURL is the ASCII string literal "pkg". Systems processing
conda PURLs MUST follow all of the rules specified in ECMA-427 Clause 5.6.1 ("Scheme"), with the
key word "shall" in ECMA-427 Clause 5.6.1 being interpreted as its BCP 14 equivalent "SHALL".
type component
The canonical form for conda PURL's type component is the ASCII string literal "conda". Systems
processing conda PURLs MUST follow all of the rules specified in ECMA-427 Clause 5.6.2 ("Type"),
with the key word "shall" in ECMA-427 Clause 5.6.2 being interpreted as its BCP 14 equivalent
"SHALL".
namespace component
Roughly speaking, the namespace component of a conda PURL corresponds to the path and label name
components of the channel base URL defined in CEP 26 and/or the
concept most users have when using the channel configuration option supported by most
conda-compatible tools.
For conda PURLs, the ECMA-427 Clause 6.5.1 requirement property for the namespace component is
optional. However, the use or non-use of the namespace component within a conda PURL MUST
conform to the following rules:
- If the
namespacecomponent is omitted from a conda PURL, then therepository_urlqualifier MUST be explicitly provided in the PURL, and its value MUST be a valid channel base URL, as defined in CEP 26. - If the
namespacecomponent is provided, each ECMA-427 Clause 5.6.3namespacesegment MUST be a valid channel base URL path component, as defined in CEP 26. All other rules for thenamespacecomponent specified in ECMA-427 Clause 5.6.3 apply to conda PURLs, including the requirement that segments within a PURLnamespaceMUST be separated by a single unencoded ASCII "/" (slash) character. - If the
namespacecomponent is provided, then the concatenation of therepository_urlqualifier, a single unencoded ASCII "/" (slash) character, and thenamespacecomponent MUST be a valid channel base URL, as defined in CEP 26. - The string literals "defaults" and "local" MUST NOT be used as the entire value of the
namespacecomponent. This prohibition exists to minimize potential confusion with the reserved, user-configurable (multi-)channel names supported by many conda-compatible clients. - The final path component of
namespaceSHOULD NOT match any channel subdir name, as this can lead to ambiguous PURLs and channel base URLs, as noted in CEP 26. If platform information is needed to properly identify the package(s), then thesubdirqualifier (defined below) should be used instead. (Note that this rule uses the "SHOULD NOT" rather than "MUST NOT" key words to match the language in CEP 26.)
An important implication of these rules is that a valid conda PURL must provide at least one of the
namespace component or the repository_url qualifier, as a conda PURL that both omits the
namespace component and assumes the default value for the repository_url qualifier (defined
below) specifies a channel base URL that is insufficient to properly locate the package.
Further, the prohibition on using "defaults" as the namespace component value means that PURLs
for Anaconda-built packages must use "main", "r", or "msys2" for their namespace component.
name component
The name component MUST be the package name of the
identified artifact, as defined in CEP 26.
Note that while both distributable and virtual package names are acceptable as PURL name
components, this CEP leaves open the question of how to properly identify conda virtual packages
using PURLs, as described in the "Future Work" section. Until this question is resolved, systems
processing conda PURLs should exercise caution when handling virtual packages.
version component
The version component, when percent decoded, MUST be the package version string, as defined in
CEP 26 and CEP 33.
The version component MUST comply with all character encoding rules specified in ECMA-427 Section
5.4 ("Character Encoding"). In particular, if they are used, the epoch segment delimiter !
(exclamation point) and the local version segment delimiter + (plus) must be percent encoded as
%21 and %2B, respectively.
For conda PURLs to support the desired level of specificity, the version component MUST be
treated as an exact equality, with any necessary .0 padding, when used for package version
comparisons. In other words, the version component MUST be treated as equivalent to a
==version MatchSpec expression, as described in CEP 29. As an example, a
version component with the value 1.2 matches versions 1.2 and 1.2.0 of the named
package but not versions 1.2.0.1 and 1.2.3.
If fuzzy equality or other, more complex version matching is needed, the
version component MUST be omitted and the vers qualifier used
instead. The version component and vers qualifier are mutually exclusive and MUST NOT be
used together in a conda PURL; in cases where both the version component and vers qualifier
are provided, the corresponding conda PURL MUST be treated as an invalid or erroneous PURL.
qualifiers component
The following qualifiers are defined for conda PURLs:
-
repository_url: Optional qualifier, with a default value defined in the "repositoryproperty" section of this CEP. Its value, when combined with anynamespacecomponent as described in the "namespacecomponent" section, MUST specify the channel base URL of the identified package(s), as defined in CEP 26. -
build: Optional qualifier, with no default value. If provided, its value MUST be the build string, as defined in CEP 26, of the identified package(s). -
build_number: Optional qualifier, with no default value. If provided, its value MUST be the build number, as defined in CEP 34, of the identified package(s). -
subdir: Optional qualifier, with no default value. If provided, its value MUST be the channel subdir name, as defined in CEP 26, of the identified package(s). -
type: Optional qualifier, with no default value. If provided, its value MUST be exactly one of currently-recognized artifact extensions or its versioned synonym, as defined in CEP 26. At the time of this CEP's adoption, the accepted values aretar.bz2(versioned synonym:v1), orconda(versioned synonym:v2). -
vers: Optional qualifier, with no default value. If provided, its value MUST be a valid Version Range Specifier (VERS), whoseversion-schemeisconda; the specification of thecondaVERS and its relationship to concepts like the MatchSpec query language are considered out of scope for this CEP and will be discussed elsewhere. This qualifier and theversioncomponent are mutually exclusive and MUST NOT be used together in a conda PURL; in cases where both theversioncomponent andversqualifier are provided, the corresponding conda PURL MUST be treated as an invalid or erroneous PURL. -
checksum: Optional qualifier, with no default value. If provided, its value MUST consist of one or more checksum specifications, with consecutive checksum specifications separated by a single, unencoded ASCII "," (comma) character. Each checksum specification is defined as the concatenation of the lowercase checksum algorithm name (e.g., "md5", "sha256"); a single, unencoded ":" (colon) character; and the lowercase, hexadecimal checksum value.The lowercase checksum algorithm name MUST only contain the ASCII lowercase characters
a-z, digits0-9, and hyphen (minus)-. The recommended way to specify checksum algorithm names is to lowercase the name, replace any non-alphanumeric characters with hyphens (-), and remove as many hyphens as possible without creating potential ambiguities; for example,sha256,sha512-224, andsha3-512are the recommended ways of specifying the SHA-256, SHA-512/224, and SHA3-512 algorithms, respectively.The lowercase hexadecimal checksum value MUST only contain the ASCII lowercase characters
a-fand digits0-9. The checksum value is explicitly understood to be hexadecimal, so commonly-used prefixes and postfixes (e.g., "0x" and "_16", respectively) MUST NOT be included as part of the checksum value.Historically, the conda ecosystem has used the MD5 and SHA-256 algorithms for checksumming packages; see, e.g., CEP 16, CEP 32, and CEP 36. Given this and the sub-optimal security properties of MD5, the RECOMMENDED algorithm for the
checksumqualifier is SHA-256. -
download_url: Optional qualifier, with no default value. If provided, its value MUST be the absolute URL that completely specifies where the identified package can be directly downloaded; in this context, "completely specifies" means no processing of other PURL components is needed to derive the direct download URL, and in particular, that the URL value MUST end with a path component representing the package artifact's filename. The value of this qualifier SHALL take precedence over all other methods for deriving a download URL from other PURL components.Note that this CEP purposely does not prescribe how systems processing conda PURLs should handle cases where information derived from a provided
download_urlseemingly conflicts with information provided in the other PURL components. Resolution of such conflicts is left to the discretion of the processing system and/or specifications in other CEPs.
Qualifiers in conda PURLs SHALL conform with all the rules specified ECMA-427 Clause 5.6.6
("Qualifiers"), with the key words "shall", "shall not", and "may" in ECMA-427 Clause 5.6.6 being
interpreted as their BCP 14 equivalents "SHALL", "SHALL NOT", and "MAY", respectively.
Importantly, this means that the entirety of any repository_url value, including colons (:) and
slashes (/), must be percent-encoded in accordance with ECMA-427 Clause 5.4.
Since existing CEPs do not define a strict relationship between a package artifact's build string
and build number, this CEP does not require that the build and build_number qualifiers in a
conda PURL be mutually exclusive. Further, this CEP does not specify which of these two qualifiers
takes precedence in cases where their simultaneous use in a conda PURL creates a conflict; e.g., a
qualifier component ?build=1234abcd_1&build_number=42 is allowed under this specification, even
though the build qualifier suggests the corresponding PURL refers to build number 1 of the
identified package while the build_number qualifier suggests the PURL refers to build number 42.
Producers of conda PURLs must take care that their use of these qualifiers does not create a
conflict, and consumers of conda PURLs are RECOMMENDED to treat conflicting use of these qualifiers
as an invalid or erroneous PURL.
This CEP explicitly undefines the channel qualifier found in the existing conda PURL
definition. The channel qualifier corresponds to a weakly-defined concept not
properly captured in any existing CEP, and its presumed purpose in the existing definition is
better handled by the combination of the namespace component and repository_url qualifier, as
described in this CEP. Upon acceptance of this CEP, systems processing conda PURLs MUST treat
PURLs containing a channel qualifier as invalid.
To avoid the need for potentially complex precedence rules in this CEP, the commonly-used
qualifier filename for other PURL types is explicitly not defined for conda
PURLs. In other words, the filename qualifier MUST NOT be accepted when processing conda PURLs.
Should such a value be needed to handle a conda PURL, it must be derived from the other components
and qualifiers defined in this CEP and the rules defined in CEP 26.
subpath component
The optional subpath component of a conda PURL is used to specify a path within the identified
package(s), subject to the following:
- The
subpathcomponent SHALL conform to all rules specified in ECMA-427 Clause 5.6.7 ("Subpath"), with the key words "shall", "shall not", and "may" in ECMA-427 Clause 5.6.7 being interpreted as their BCP 14 equivalents "SHALL", "SHALL NOT", and "MAY", respectively. - The
subpathcomponent MUST be treated as case-sensitive. - If the
subpathcomponent identifies a symbolic link with the package(s), thesubpathcomponent MUST be interpreted as referring to the symbolic link file itself and not as a reference to the target path after the symbolic link has been partially or fully dereferenced. - When applied to
tar.bz2-/v1-format packages, "relative to the root of the package" in ECMA-427 Clause 5.6.7 MUST be interpreted as the path to an archive member within the package tarball, or equivalently, as a relative path from a root directory into which the package tarball was extracted. - When applied to
conda-/v2-format packages, "relative to the root of the package" in ECMA-427 Clause 5.6.7 MUST be interpreted as a relative path from a common root directory into which both inner archives (i.e.,info-{name}-{version}-{build}.tar.zstandpkg-{name}-{version}-{build}.tar.zst) have been extracted. - The
subpathcomponent MUST be interpreted as a reference to a path as it exists in the package artifact(s), and not as a reference to a path after any modifications that may have occurred when the package artifact(s) are processed by conda-compatible clients (e.g., prefix replacement, pre- or post-link scripts, etc.).
The last three rules are intended to ensure consistent interpretation of the subpath component,
regardless of the actual package format and processing system. Note that these rules imply that
conda PURLs cannot be used to directly identify the metadata.json nor the inner .tar.zst
archives within conda-/v2-format packages.
Other ECMA-427 root object properties
ECMA-427 Clause 6 ("Package-URL Type Definition Schema") specifies other required and optional properties for a PURL type definition. For conda PURLs, these properties are defined as follows:
type name property
The ECMA-427 Clause 6.2 type name property of the conda PURL type definition SHALL be "Conda".
description property
The ECMA-427 Clause 6.3 description property of the conda PURL type definition SHALL be
"Conda packages".
repository property
The ECMA-427 Clause 6.4 repository property is REQUIRED for conda PURLs. Its use and
interpretation is defined in the "namespace component" and "qualifier component" sections.
To maintain compatibility with the default behavior of existing conda-compatible clients, the
default public repository (i.e., default_repository_url) for conda packages SHALL be
https://conda.anaconda.org.
examples property
The ECMA-427 Clause 6.10 PURL examples property of the conda PURL type definition SHALL consist
of the PURLs listed in the "Normative Examples" section of this CEP.
reference_urls property
The ECMA-427 Clause 6.12 reference_urls property for the conda PURL type definition SHALL be a
list consisting of:
- The canonical URL of this CEP
Note that ECMA-427 Clause 6.12 specifies that the URLs listed in the reference_urls PURL type
definition are informative. However, to ensure standardized implementations, this CEP MUST be
treated as normative and not just informative.
Examples
Normative Examples
pkg:conda/main/python: Identifies all versions and builds, for all platforms (i.e., operating systems and CPU architecture), of Python in the Anacondamainchannel.pkg:conda/conda-forge/python@3.13.12: Identifies all builds, for all platforms, of Python 3.13.12 in theconda-forgechannel.pkg:conda/conda-forge/python@3.13.12?build=hc97d973_100_cp313&subdir=linux-64: Identifies a specific build of Python 3.13.12, for x86_64 Linux, in theconda-forgechannel.pkg:conda/numpy@2.5.0?subdir=win-64&repository_url=https%3A%2F%2Frepo.anaconda.cloud%2Frepo%2Fmain: Identifies all builds of NumPy 2.5.0, for x86_64 Windows, in Anaconda's commercialmainrepository, using therepository_urlqualifier to set the channel base URLpkg:conda/conda-forge/numpy@2.5.2?repository_url=https%3A%2F%2Fprefix.dev: Identifies all builds of NumPy 2.5.2, for all platforms, in theconda-forgechannel hosted by prefix.dev
Backwards Compatibility
This CEP intentionally breaks backwards-compatibility for PURLs based on the existing conda PURL definition in the following ways:
- The
default_repository_urlhas been changed fromhttps://repo.anaconda.comtohttps://conda.anaconda.org. - The requirement property of the
namespacecomponent has changed from "prohibited" to "optional", with additional rules as described above. channelis no longer an accepted qualifier for conda PURLs.
These breaking changes are considered acceptable by the author(s) of this CEP, as the existing conda PURL definition fails to properly capture existing standards (i.e., CEPs) and commonly-used patterns in the conda ecosystem. Among the motivations for these breaking changes:
- PURLs like
pkg:conda/pythonandpkg:conda/python?channel=conda-forgeare accepted under the existing conda PURL definition but cannot be used to identify actual package artifacts, as that definition's default values produces non-existent channel base URLs (https://repo.anaconda.com/andhttps://repo.anaconda.com/conda-forge, respectively). The existing (v1.0.1) conda PURL definition effectively requires that every conda PURL explicitly provides arepository_urlqualifier, possibly combined with achannelqualifier in an unspecified way, to produce a URL that would correspond to actual package artifacts. - The prohibition of a
namespacecomponent in the existing conda PURL definition means commonly-used patterns in the conda ecosystem (e.g.,channel::package=version) cannot be translated to "intuitive"-/similar-looking PURLs (e.g.,pkg:conda/channel/package@version). Coupled with the previously-noted choice of existing default values, this creates an unwanted UX burden when using conda PURLs.
Implementation Notes
Upon approval of this CEP by conda steering council, the CEP author(s) will submit the necessary
changes to the Github package-url/purl-spec
repository needed to make its conda PURL type definition and any other related files conformant
with this standard.
Future Work
- As noted in the
versqualifier definition, one or more CEPs will be needed to define VERS for conda packages and as a likely prerequisite, provide more normative specifications for the MatchSpec query language. - ECMA-427 allows for the specification of case-sensitivity and normalization rules as properties of various PURL components (e.g., namespaces, names, versions). This CEP intentionally leaves such properties ambiguous or undefined, as no accepted standards within the conda ecosystem unambiguously state what the values of such ECMA-427 properties should be. The conda PURL type definition should be updated appropriately if/when the conda ecosystem standardizes on such case-sensitivity and normalization rules.
- CEP 26 allows
file://URLs to be channel base URLs, and the diversity of file systems and their behaviors may lead to conflicts with the requirements in this CEP and/or ECMA-427. To avoid making this CEP overly complicated, the author(s) have intentionally chosen to defer any work needed to properly handle certain types offile://-based channels, especially since it is unclear if PURLs are meaningfully useful for such channels. - The specifications for the
subpathcomponent may not be fully complete with respect to every type of path (i.e., file or directory) that could possibly be included in conda packages. Updates to this CEP or additional CEPs may be needed if any gaps are identified in thesubpathcomponent specifications. - This CEP provides mechanisms for using the
namespacecomponent and/orrepository_urlqualifier to identify a conda package as it exists at a single location. However, this CEP does not define ways to identify conda packages that exist across multiple locations; e.g., a single PURL that can simultaneously identify a conda-forge package mirrored on bothconda.anaconda.org/conda-forgeandprefix.dev/conda-forge, or a PURL that can identify all packages namedpython(regardless of contents) across all channels in the ecosystem. Supporting such PURLs is a desired feature but would require additional work, including formalizing the concept of "mirrors" for conda packages and channels, as well as specifying which PURL components or qualifiers should be used to identify the set of locations for a given package. (Due to the fairly portable nature of conda packages, the combination of shortened channel name, package name, version string, and build string may not be sufficient to uniquely identify across all possible repositories.) - This CEP recognizes the utility of PURLs to identify conda virtual packages, which do not exist
on any channel. However, this CEP defers specifying PURLs for such packages, as doing so would
introduce additional complexity to how the
namespacecomponent andrepository_urlqualifier need to be processed and may require changes to the upstream PURL spec/ECMA-427 itself.
References
Normative References
- RFC2119: Key words for use in RFCs to Indicate Requirement Levels
- RFC8174: Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words
- ECMA-427: Package-URL (PURL) specification, 1st edition, December 2025
- CEP 26: Identifying Packages and Channels in the conda Ecosystem
- CEP 33: Version literals and their ordering
- CEP 34: Contents of conda packages
- CEP 35: Distributable package artifacts file formats
- VERS specification: "VErsion Range Specifier" specification
Informative References
- PURL Qualifiers Guidance
- CEP 16: Sharded Repodata
- CEP 29: The
MatchSpecquery language - CEP 32: Management and structure of conda environments
- CEP 36: Package metadata files served by conda channels
Changelog
- 2026-09-23: CEP was accepted.
Copyright
All CEPs are explicitly CC0 1.0 Universal.