docs: document content verification and typed errors
CI / Check (push) Successful in 2m34s
CI / Format (push) Successful in 17s
CI / Clippy (push) Successful in 2m38s
CI / Test (push) Successful in 3m7s

Describe what downloads are verified against (registry digests for
OCI, published checksums for distro CDNs) and the degradation policy;
document the ecr::Error variants and the Result alias for library
consumers.
This commit is contained in:
2026-10-04 21:21:04 +02:00
parent 593ce20f4f
commit 8f67b362b4
2 changed files with 41 additions and 2 deletions
+2 -2
View File
@@ -18,7 +18,7 @@ ecr fedora # any Docker Hub image
## Library
`ecr` is a cargo workspace: `crates/ecr` is the library crate (package `ecr`), `crates/ecr-cli` the thin command-line front-end. The library never writes to the terminal: diagnostics go through the `log` crate, and downloads, extraction and initramfs creation report progress through a caller-supplied `Progress` sink (pass `&NoProgress` for silence, or implement the trait to render your own UI). Rust consumers can use the library directly — resolve an image, download it through the content cache, and run commands with a fully caller-controlled environment, bind targets and architecture:
`ecr` is a cargo workspace: `crates/ecr` is the library crate (package `ecr`), `crates/ecr-cli` the thin command-line front-end. The library never writes to the terminal: diagnostics go through the `log` crate, and downloads, extraction and initramfs creation report progress through a caller-supplied `Progress` sink (pass `&NoProgress` for silence, or implement the trait to render your own UI). Public functions return `Result<T, ecr::Error>`: match on the `Error` variants for host-readiness failures (user namespaces disabled, binfmt_misc unregistered, missing subuid/subgid entry, architecture the registry does not serve) — everything else arrives as `Error::Other` with the full context chain. Rust consumers can use the library directly — resolve an image, download it through the content cache, and run commands with a fully caller-controlled environment, bind targets and architecture:
```toml
[dependencies]
@@ -159,7 +159,7 @@ Requirements:
## Cache
Downloaded images are cached in `~/.cache/ecr/`. For `latest` OCI tags the registry manifest digest is checked on each run — the image is only re-downloaded when it has actually changed. Use `--no-cache` to force a fresh pull regardless. The library's `PreparedRootfs::persist` can replace a cache entry with a provisioned rootfs (see *Provisioned-rootfs caching* above).
Downloaded images are cached in `~/.cache/ecr/`. Downloads are verified before they enter the cache: OCI layers and manifests against the registry's content digests, direct distro tarballs against the checksums Alpine and Ubuntu publish next to them (a missing checksum artifact degrades to an unverified download with a warning). For `latest` OCI tags the registry manifest digest is checked on each run — the image is only re-downloaded when it has actually changed. Use `--no-cache` to force a fresh pull regardless. The library's `PreparedRootfs::persist` can replace a cache entry with a provisioned rootfs (see *Provisioned-rootfs caching* above).
## Requirements
+39
View File
@@ -20,6 +20,28 @@ spinners (indicatif) — is owned by `crates/ecr-cli`.
## Library API
### Errors (`ecr::error`)
Public functions return `Result<T, ecr::Error>` so consumers can match on
the failure modes that call for different handling. anyhow remains the
internal error type; semantic variants are raised at the sites that detect
the conditions, everything else converts to:
- `Error::Other` — any other failure, transparently wrapping the anyhow
context chain (`source()` walks it; `Display` shows the top message).
- `Error::UserNamespacesUnavailable` — unprivileged user namespaces are
disabled; `remediation` names the sysctl to set.
- `Error::BinfmtNotRegistered` — binfmt_misc missing for the target
architecture (foreign-arch namespace mode).
- `Error::ArchNotAvailable` — the registry serves no linux manifest for
the requested architecture; `available` lists what it does serve.
- `Error::MissingSubidRange` — `/etc/subuid` or `/etc/subgid` has no
usable entry for the user.
Errors raised inside the namespace child (mount setup, resolv.conf,
chroot exec) cross the process boundary as formatted chains and surface
as `Error::Other`.
### Rootfs lifecycle (`ecr::rootfs`)
- `RootfsCache::new(dir)` / `RootfsCache::default_dir()` — the image tarball
@@ -195,6 +217,23 @@ If architecture is not available in manifest list, error with available architec
Error: No manifest found for architecture 'riscv64'. Available: amd64, arm64, ppc64le, s390x
```
### Content Verification
Downloaded content is hashed (sha256 and sha512 in parallel) while
streaming, and verified before it enters the cache — a mismatch fails the
download and nothing partial is kept:
- OCI architecture manifests requested by digest are checked against that
digest (the layer digests read from them are trusted next).
- OCI layer blobs are checked against the digest the manifest names. A
digest algorithm other than sha256/sha512 cannot be checked: warn and
accept.
- Direct distro tarballs are checked against the checksum each distro
publishes next to them (Alpine: per-file `.sha256`; Ubuntu: `SHA256SUMS`
in the release directory). A missing or unfetchable checksum artifact
degrades to an unverified download with a warning, mirroring the
freshness-check policy; a checksum mismatch always fails.
### Layer Extraction
`extract::extract_tarball` handles plain rootfs tarballs and OCI layer