oci-zero 0.3.0

A no-std, allocation-free streaming core for OCI registries
Documentation

oci-zero

Runnerless CI: slowest job

CI crates.io docs.rs

Bounded-memory Rust building blocks for reading and unpacking content from Open Container Initiative (OCI) registries. This repository publishes three crates:

Crate Purpose Docs
oci-zero OCI registry client core: references, manifests, distribution requests, digest verification, tar layers docs.rs
gzip-zero Streaming gzip decoder docs.rs
zstd-zero Streaming Zstandard decoder docs.rs

All three are no_std, forbid unsafe code, and are allocation-free with their default (empty) feature set. The decoders are published separately so they can be used without oci-zero.

[!WARNING] Experimental: The allocation-free APIs are usable but remain subject to change while the registry and layer conformance suites grow.

oci-zero

[dependencies]
oci-zero = "0.2"

With default features this uses sha2 for digests, base64ct for in-place Base64, and derive_more for error formatting. Enable the decoders and transport you need (the gzip and zstd features pull in gzip-zero and zstd-zero, so you do not need to add them yourself):

[dependencies]
oci-zero = { version = "0.2", features = ["gzip", "zstd"] }

The core APIs use borrowed values, caller-owned buffers, visitors, and streaming source/sink traits. They do not require a filesystem, executor, HTTP client, or TLS implementation. They can:

  • Serve a read-only OCI registry from borrowed memory through a transport-independent router, including catalog/tag pagination, manifests, blobs, and HEAD metadata.
  • Parse references, indexes, manifests, image configs, descriptors, tags, annotations, and referrers as lazy borrowed views over caller buffers.
  • Plan read-side OCI Distribution requests, including authentication challenges, redirects, retries, pagination, and the referrers-tag fallback.
  • Traverse selected platform manifests through a transport-independent fetcher and callback visitor without retaining descriptor collections.
  • Verify descriptor sizes, compressed SHA-256 digests, and image layer diff IDs while content streams.
  • Parse tar, ustar, per-entry PAX, and GNU long-path records into a transactional layer sink with safe paths and OCI whiteout events.

Features

Feature Adds
gzip gzip layer decoding via gzip-zero
zstd Zstandard layer decoding via zstd-zero
reqwless Streaming HTTP over embedded-io connections
tls Implies reqwless; adds a caller-configured MbedTLS connector
docker-credentials Host-side (std) provider for Docker CLI DOCKER_AUTH_CONFIG, credHelpers, credsStore, and inline auths

gzip, zstd, reqwless, and tls do not add a Rust allocator.

gzip-zero

[dependencies]
gzip-zero = "0.1"

Wraps the allocation-free core of miniz_oxide with incremental gzip header, trailer, checksum, size, and concatenated-member handling. The caller supplies the 32 KiB DEFLATE history buffer; input may be split at arbitrary byte boundaries and output is returned as borrowed slices. See gzip-zero/README.md.

zstd-zero

[dependencies]
zstd-zero = "0.2"

Decodes standard Zstandard frames with no dependencies. The caller supplies the history buffer (at least the frame's declared window), two block scratch buffers of up to 128 KiB, and the entropy-table buffers. Dictionary, legacy, and magicless frames are not supported. Output can be observed before a final content checksum fails, so callers that need atomic behaviour must use a transactional sink. See zstd-zero/README.md for the buffer requirements and a usage example.

Compatibility

  • oci-zero (core, gzip, zstd), gzip-zero, and zstd-zero require Rust 1.75 or newer and build for targets without the standard library. CI checks this on Rust 1.75 for thumbv7em-none-eabi.
  • docker-credentials requires std so it can read local files and run credential helpers.
  • The reqwless and tls features require Rust 1.91.

Examples

examples/registry-demo runs an in-memory registry locally or as a Cloudflare Worker, using the same no_std serving API. Explore tiny layered images, a multi-platform index, and repository files packaged as an OCI artifact with the web explorer's Open local demo button.

These examples run against digest-pinned public registry content.

Download and verify a public OCI index, then print a short summary:

cargo run --features docker-credentials --example download_metadata

Pass another reference to use the credentials from $DOCKER_CONFIG/config.json or ~/.docker/config.json:

docker login ghcr.io
cargo run --features docker-credentials --example download_metadata -- \
  oci://ghcr.io/OWNER/PRIVATE_IMAGE:TAG

No password or token is placed on the command line. The provider follows Docker CLI precedence (DOCKER_AUTH_CONFIG, then credHelpers, then credsStore, then inline auths), invokes helpers from PATH directly without a shell, and does not include credential values in authentication errors.

Run the public registry fixture set used by CI (Docker Hub, GHCR, registry.k8s.io, and install.datadoghq.com, covering Helm charts, OCI 1.1 artifacts and referrers, cross-registry redirects, and Docker Schema 2 media types):

cargo test --release --test metadata_smoke \
  inspects_public_registry_fixtures -- --ignored --nocapture

Stream and decode a Datadog tar+zstd layer, verifying its compressed and decompressed SHA-256 digests without buffering either stream:

cargo run --release -p zstd-zero --example decode_layer

Extract one file from that layer to standard output while it downloads:

cargo run --release --features zstd --example extract_file -- \
  application_monitoring.yaml.example > application_monitoring.yaml.example

Standard output is not transactional, so wait for a successful process exit before trusting or acting on the extracted bytes. This layer declares a 32 MiB history window, which the example allocates on the heap; the host-side HTTP adapter may also make small allocations.

no-std-extract does the same as a hosted no_std binary with no Rust allocator, using the reqwless, tls, and zstd features (Rust 1.91):

cargo build --release -p oci-zero-no-std-extract
target/release/oci-zero-no-std-extract \
  'https://install.datadoghq.com/v2/agent-package/blobs/sha256:bc219703080f03ad836d51bf7f72cc3ced34f5ba440a49f450d6a5dea98ceff4' \
  application_monitoring.yaml.example > application_monitoring.yaml.example

It also accepts a local blob path, or - for standard input. It is a narrow proof of concept: it checks the sizes and digests of that one known layer, trusts only the embedded DigiCert Global Root G2, resolves DNS through 8.8.8.8, and gives MbedTLS a fixed 4 MiB static arena. CI builds and runs it against the pinned blob.

Other parts of this repository

These are not published crates:

  • web — a WebAssembly browser page and Chrome extension for browsing registries, layers, and merged filesystems. A hosted build is at https://pawelchcki.github.io/oci-zero/; as an ordinary web page it can only reach registries that send CORS headers.
  • bench — a Linux harness that measures the end-to-end memory, CPU, and binary-size overhead of no-std-extract.
  • examples/esp32c3-ota — ESP32-C3 firmware intended to update itself from an OCI registry; the update path is not implemented yet.

Contributing

See AGENTS.md for the PR title convention, which is enforced in CI because it drives version bumps and changelogs, and for how releases are cut.

llm-cc complexity reports compare pull requests and rank the repository on main; their scores are advisory.

License

Licensed under either of

at your option.