Skip to main content

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 namespace component is omitted from a conda PURL, then the repository_url qualifier MUST be explicitly provided in the PURL, and its value MUST be a valid channel base URL, as defined in CEP 26.
  • If the namespace component is provided, each ECMA-427 Clause 5.6.3 namespace segment MUST be a valid channel base URL path component, as defined in CEP 26. All other rules for the namespace component specified in ECMA-427 Clause 5.6.3 apply to conda PURLs, including the requirement that segments within a PURL namespace MUST be separated by a single unencoded ASCII "/" (slash) character.
  • If the namespace component is provided, then the concatenation of the repository_url qualifier, a single unencoded ASCII "/" (slash) character, and the namespace component 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 namespace component. 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 namespace SHOULD 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 the subdir qualifier (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 "repository property" section of this CEP. Its value, when combined with any namespace component as described in the "namespace component" 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 are tar.bz2 (versioned synonym: v1), or conda (versioned synonym: v2).

  • vers: Optional qualifier, with no default value. If provided, its value MUST be a valid Version Range Specifier (VERS), whose version-scheme is conda; the specification of the conda VERS 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 the version component 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.

  • 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, digits 0-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, and sha3-512 are 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-f and digits 0-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 checksum qualifier 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_url seemingly 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 subpath component 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 subpath component MUST be treated as case-sensitive.
  • If the subpath component identifies a symbolic link with the package(s), the subpath component 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.zst and pkg-{name}-{version}-{build}.tar.zst) have been extracted.
  • The subpath component 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 Anaconda main channel.
  • pkg:conda/conda-forge/python@3.13.12: Identifies all builds, for all platforms, of Python 3.13.12 in the conda-forge channel.
  • 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 the conda-forge channel.
  • 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 commercial main repository, using the repository_url qualifier to set the channel base URL
  • pkg: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 the conda-forge channel 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_url has been changed from https://repo.anaconda.com to https://conda.anaconda.org.
  • The requirement property of the namespace component has changed from "prohibited" to "optional", with additional rules as described above.
  • channel is 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/python and pkg:conda/python?channel=conda-forge are 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/ and https://repo.anaconda.com/conda-forge, respectively). The existing (v1.0.1) conda PURL definition effectively requires that every conda PURL explicitly provides a repository_url qualifier, possibly combined with a channel qualifier in an unspecified way, to produce a URL that would correspond to actual package artifacts.
  • The prohibition of a namespace component 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 vers qualifier 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 of file://-based channels, especially since it is unclear if PURLs are meaningfully useful for such channels.
  • The specifications for the subpath component 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 the subpath component specifications.
  • This CEP provides mechanisms for using the namespace component and/or repository_url qualifier 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 both conda.anaconda.org/conda-forge and prefix.dev/conda-forge, or a PURL that can identify all packages named python (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 namespace component and repository_url qualifier 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​

Changelog​

  • 2026-09-23: CEP was accepted.

All CEPs are explicitly CC0 1.0 Universal.