CEP 36 - Package metadata files served by conda channels
| Title | Package metadata files served by conda channels |
| Status | Accepted |
| Author(s) | Jaime Rodríguez-Guerra <jaime.rogue@gmail.com> |
| Created | Sep 30, 2025 |
| Updated | Mar 4, 2026 |
| Discussion | https://github.com/conda/ceps/pull/135 |
| Implementation | N/A |
| Requires | CEP 34 |
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 RFC2119 when, and only when, they appear in all capitals, as shown here.
Abstract
This CEP standardizes the schema for the package metadata (repodata) files served in conda channels. Namely, repodata.json and its variants.
Motivation
The motivation of this CEP is merely informative. It describes the schema of existing metadata files already in wide use.
Specification
As per CEP 26, a conda channel is defined as a location that MUST serve a noarch/repodata.json path. It MAY also serve additional, platform-specific repodata.json paths under other subdirectories of the same depth, which MUST follow the subdir naming conventions described in CEP 26.
Note that there are no requirements for these paths to be backed by a proper filesystem; the contents of these locations can also be provided by API endpoints.
repodata.json documents are subdir-specific JSON dictionaries that aggregate the index.json metadata of the included conda artifacts (see CEP 34), and extend them with details only known when the compressed artifact has been generated (such as size, or checksums).
Schema
Each repodata.json MUST represent a dictionary with the keys listed below. All of them are optional. Additional top-level keys MUST be allowed but they MUST be ignored if not recognized. An empty file MUST be considered equivalent to an empty dictionary.
info: dict[str, dict]. Metadata about therepodata.jsonfile itself. See info metadata.packages: dict[str, dict]. This entry maps*.tar.bz2filenames to their package record metadata.packages.conda: dict[str, dict]. This entry maps*.condafilenames to package record metadata.removed: list[str]. List of filenames that were once included in eitherpackagesorpackages.conda, but are now removed. The corresponding artifacts SHOULD still be accessible via their direct URL.
A signatures: dict[str, dict] key MAY be present, but SHOULD be ignored. This key was introduced as a proprietary extension by Anaconda, but it is not part of the repodata v1 specification.
info metadata
This dictionary stores information about the repodata file. It MUST follow this schema:
arch: str. Deprecated. Same meaning as in CEP 34'sindex.jsonkey.base_url: str. Optional. See CEP 15.platform: str. Deprecated. Same meaning as in CEP 34'sindex.jsonkey.repodata_version: int. Optional. Version of therepodata.jsonschema. In its absence, tools MUST assume its value is1. See CEP 15 forrepodata_version = 2.subdir: str. Recommended. The channel subdirectory thisrepodata.jsonbelongs to. In its absence, its value MAY be inferred from the parent component of therepodata.jsonpath.
Additional keys SHOULD NOT be present and SHOULD be ignored.
Package record metadata
Each entry in packages and packages.conda MUST map the corresponding filename (see above) to a dictionary that:
- MUST follow the
index.jsonschema (see CEP 34). - SHOULD report the same values as the artifact's
info/index.jsonmetadata. Small modifications MAY be introduced to apply metadata fixes (e.g. correct the constraints of a requirement in thedependsfield) without needing to rebuild the artifact. - MUST additionally include the following keys:
md5: str | None. Hexadecimal string of the MD5 checksum of the compressed artifact.sha256: str | None. Hexadecimal string of the SHA256 checksum of the compressed artifact.size: int. Size, in bytes, of the compressed artifact.
- If the entry corresponds to a
.tar.bz2package that was transmuted to.conda, it SHOULD include these keys:legacy_bz2_md5: str: Hexadecimal string of the MD5 checksum of the original.tar.bz2artifact.legacy_bz2_size: int: Size, in bytes, of the original.tar.bz2artifact.
Additional keys SHOULD NOT be present and SHOULD be ignored.
Repodata variants
A conda channel MAY serve additional repodata.json documents in each subdir. Their name SHOULD match the glob *repodata*.json, and their contents MUST follow the repodata.json schema.
Common variants include current_repodata.json, which aggregates a subset of the full repodata document, focusing on the latest versions of each package plus their necessary dependencies.
Channels SHOULD serve compressed versions of every repodata file. The following compression schemes are recognized:
- BZ2: MUST append the
.bz2extension; e.g.repodata.json.bz2. Deprecated. - ZSTD: MUST append the
.zstextension; e.g.repodata.json.zst. Recommended.
Examples
A minimal conda channel only needs a single, empty file:
./noarch/repodata.json
A conda channel with a Linux x64 specific subdirectory:
./noarch/repodata.json
./linux-64/repodata.json
References
Appendices
Appendix A: signatures section
This dictionary maps conda package filenames (with extension) to a signature metadata dictionary. Each subdictionary then maps the signing key identifier to the signature value. This value is expressed as a dictionary with a key signature that maps to the actual signature of the corresponding package record. See example:
"packages": {
...
},
"packages.conda": {
...
},
"signatures": {
"_anaconda_depends-2018.12-py27_0.tar.bz2": {
"4a044c3445b9d8bc5429a2b1d7d42bdb4d8404285b76322e8eacdfdae8b0e4cd": { // signing key id
"signature": "a0ffab3f954c3dc64373ba16bee5e9ba9683a625fa3e4a6c4263d9de550bcafd233c2522789c9b31b40c35a87775d6f8fa2498a3bec3647c36c0a2f5cd2eb10c" // signature value
}
},
"zstd-1.3.7-h0b5b093_0.conda": {
"4a044c3445b9d8bc5429a2b1d7d42bdb4d8404285b76322e8eacdfdae8b0e4cd": {
"signature": "ea1f11a74c081298fe243c6982f676d9838bfee81e74a24bef6474f3be1243b4624f6d12dc8196f8db909cf049e9e344151e44c5b950cbab8583641c7b661a0d"
}
}
}
Copyright
All CEPs are explicitly CC0 1.0 Universal.