Archive receipt reference#
This page describes the external receipt format written by
conda workspace archive --receipt and verified by
conda workspace unarchive --receipt.
Archive receipts are sidecar JSON documents. They use the in-toto Statement v1 envelope and a conda-workspaces predicate schema that binds a workspace archive to the manifest, lockfile, and package inventory it was created from.
Status#
Field |
Value |
|---|---|
Format |
Workspace archive receipt |
Format version |
|
JSON schema |
|
Source schema |
|
Statement type |
|
Predicate type |
|
Producer |
|
Consumer |
|
File naming#
When --receipt is passed without a path, conda-workspaces writes or
reads a sibling file named after the archive:
my-project.tar.zst
my-project.tar.zst.receipt.json
Pass an explicit path to store the receipt elsewhere:
conda workspace archive --receipt attestations/my-project.json -o my-project.tar.zst
conda workspace unarchive my-project.tar.zst --receipt attestations/my-project.json
The receipt path must be separate from the archive path.
Statement structure#
A receipt is a JSON object with the in-toto Statement fields
_type, subject, predicateType, and predicate.
{
"_type": "https://in-toto.io/Statement/v1",
"subject": [
{
"name": "my-project.tar.zst",
"digest": {
"sha256": "<archive sha256>"
}
},
{
"name": "conda.toml",
"digest": {
"sha256": "<manifest sha256>"
}
},
{
"name": "conda.lock",
"digest": {
"sha256": "<lockfile sha256>"
}
}
],
"predicateType": "https://conda-incubator.github.io/conda-workspaces/workspace-archive-receipt-1.schema.json",
"predicate": {
"archive": {
"formatVersion": 1,
"options": {}
},
"workspace": {
"manifest": "conda.toml",
"lockfile": "conda.lock"
},
"environments": []
}
}
Receipts are written as stable, sorted, UTF-8 JSON. The loader rejects duplicate JSON object keys because duplicate keys can make an integrity document ambiguous.
Subjects#
The subject array records SHA-256 digests for:
the archive file, using the archive basename as the subject name
the workspace manifest path as stored inside the archive
the
conda.lockpath as stored inside the archive
Receipt creation requires the selected archive filters to include the
workspace manifest and conda.lock. If [workspace.archive].include,
[workspace.archive].exclude, or --exclude would omit either file,
the archive command fails before writing the archive or receipt.
Predicate#
The predicate contains three sections.
Section |
Required fields |
Description |
|---|---|---|
|
|
Receipt format version. |
|
|
POSIX archive-relative paths to the manifest and lockfile that must verify after extraction. |
|
|
Lockfile-derived package inventory for each workspace environment. |
Environment records may include prefix. Prefixes inside the workspace
are stored as archive-relative POSIX paths such as
.conda/envs/default; external runtime prefixes remain absolute, using
POSIX or Windows syntax as appropriate.
Package records are normalized from conda.lock. Records may include:
Field |
Description |
|---|---|
|
Package name |
|
Package version |
|
Package build string |
|
Package build number |
|
Conda platform subdir |
|
Package channel URL |
|
Package artifact URL |
|
Package artifact filename |
|
Package artifact SHA-256 digest |
|
Package artifact MD5 digest |
Package URLs and channel URLs are redacted before they are written to a receipt. Embedded credentials, Anaconda tokens, query strings, and URL fragments are removed.
Verification#
conda workspace unarchive ARCHIVE --receipt [PATH] verifies in this
order:
Load the receipt JSON and reject duplicate object keys.
Validate the in-toto Statement type, predicate type, and receipt format version.
Verify the archive file’s SHA-256 digest before extraction.
Extract to a temporary staging directory under the target parent, using the same archive path traversal protections as regular extraction.
Verify the extracted manifest and lockfile SHA-256 digests.
Recompute the package inventory from the extracted
conda.lockand compare it with the receipt.Move the staged directory into the requested target.
Verified extraction refuses to use an existing symlink target, existing file target, or non-empty directory target. This prevents an attacker from satisfying receipt paths with pre-existing files outside the staged extraction.
Pass --require-sha256 with --receipt to require every compared
package record to include SHA-256. Without it, the receipt still
compares all package identity and digest fields that are present.
Trust model#
Archive receipts are integrity documents, not signatures. They detect whether the archive, extracted manifest, extracted lockfile, or lockfile package inventory differs from the receipt, but they do not prove who created the receipt.
For provenance-sensitive workflows, distribute the receipt through an
independent trusted channel or sign it with an external signing system.
For air-gapped workflows, pair --receipt with --bundle so the
archive carries the package artifacts and the receipt carries the
lockfile inventory that should describe them. unarchive only primes a
conda package cache from bundled packages after receipt verification.