Skip to main content

Stream HuggingFace models directly into OCI registries as multi-layer images

Project description

oci-modelcar

CI PyPI

Stream HuggingFace models directly into OCI registries as multi-layer images, suitable for KServe with native OCI image volumes (KEP-4639).

Why

Pushing a HuggingFace model to an OCI registry typically means:

  1. Triple-trip: HF -> local cache -> registry
  2. One huge layer: no cross-repo blob mount possible
  3. No resume: a 5 GB shard failing at 4.5 GB starts over

oci-modelcar streams in pure Python:

  • HF -> registry directly, no disk persistence
  • One uncompressed tar layer per file (digest == diff_id)
  • Three-level resume: HF Range request, OCI session resync, file-level state file
  • Memory bounded to ~16 MiB per worker

Install

pip install oci-modelcar

Quick start

export HF_TOKEN=hf_...
export OCI_USERNAME=...
export OCI_PASSWORD=...

oci-modelcar push \
  --hf-repo Qwen/Qwen3-30B-A3B \
  --registry registry.example.com \
  --target-repo models/qwen3-30b

The image tag defaults to the first 12 characters of the resolved HF commit SHA (e.g. a3f47b09c8d2).

Authentication

HuggingFace (aligned with huggingface-cli):

  • HF_TOKEN env var (recommended)
  • ~/.cache/huggingface/token (created by huggingface-cli login)

OCI registry:

  • OCI_USERNAME + OCI_PASSWORD env vars (recommended for CI)
  • ~/.docker/config.json (docker login writes here)
  • $XDG_RUNTIME_DIR/containers/auth.json (podman login)

Local registries (hostnames localhost, 127.x.x.x, ::1) automatically use plain HTTP. Pass an explicit http:// or https:// prefix on --registry to override.

Common options

Option Default Description
--hf-revision main Branch, tag, or 40-char SHA
--target-tag <sha[:12]> Image tag
--also-tag CSV of alias tags
--workers 1 Parallel layers (cap 8)
--chunk-mib 8 PATCH chunk size
--state-file ~/.local/state/oci-modelcar/state.json JSON resume state
--fail-fast / --continue-on-error fail-fast Failure policy
--log-style auto text or azure
--dry-run List files, don't push

Full list: oci-modelcar push --help.

Resume after failure

State is automatically saved per file. If a push is killed (kill, OOM, network loss), re-running the same command resumes:

# First run, killed mid-way
oci-modelcar push --hf-repo X --registry Y --target-repo Z
# ^C

# Re-run: skips files already pushed
oci-modelcar push --hf-repo X --registry Y --target-repo Z

Force a full re-push with --force.

OCI compliance

Compliant with OCI Distribution v1.1 and OCI Image Spec v1.1:

  • Chunked PATCH uploads with Content-Range: N-M (inclusive, no bytes prefix per OCI spec)
  • Resume via GET /v2/<repo>/blobs/uploads/<id> and the Range: 0-N header
  • 416 Range Not Satisfiable is treated as "ask the server, sync, retry"
  • HEAD validation cross-checks Docker-Content-Digest
  • Layers use application/vnd.oci.image.layer.v1.tar (uncompressed) so layer.digest == diff_id by construction

Signing & verification

oci-modelcar itself does not sign artifacts — signature is delegated to cosign, the canonical OCI signing tool. Each push exposes the canonical digest reference for direct piping into cosign:

oci-modelcar push --hf-repo ... --registry ... --target-repo ...
# IMAGEREFDIGEST=registry.example.com/models/qwen3-30b@sha256:...

# Sign keyless (CI with OIDC, e.g. GitHub Actions with id-token: write)
cosign sign $IMAGEREFDIGEST

# Sign with a static key (offline / regulated environments)
cosign generate-key-pair                  # one-time, produces cosign.key + cosign.pub
cosign sign --key cosign.key $IMAGEREFDIGEST

# Verify (consumer side, e.g. KServe operator)
cosign verify $IMAGEREFDIGEST \
    --certificate-identity-regexp '^https://github\.com/your-org/' \
    --certificate-oidc-issuer 'https://token.actions.githubusercontent.com'

# Or with the static public key
cosign verify --key cosign.pub $IMAGEREFDIGEST

The signature is stored as an additional artifact in the same OCI registry, attached to the manifest by digest (referrers API for OCI Distribution v1.1+, or :sha256-<digest>.sig tag for legacy registries — cosign auto-detects).

PyPI artifact

The oci-modelcar PyPI distribution is signed with PEP 740 digital attestations generated by GitHub Actions in keyless OIDC mode. Verify with:

pip install pypi-attestations
DIST_DIR=$(mktemp -d)
pip download --no-deps --no-build-isolation -d "$DIST_DIR" oci-modelcar >/dev/null
python -m pypi_attestations verify pypi \
    --repository codanael/oci-modelcar \
    "$DIST_DIR"/oci_modelcar-*.whl

Releasing (maintainers)

  1. Bump version in pyproject.toml and update CHANGELOG.md.
  2. Tag: git tag v0.1.0 && git push --tags.
  3. The release.yml workflow builds, publishes to PyPI via Trusted Publishing, and creates a GitHub Release.

PyPI trusted publisher must be configured once: on pypi.org -> Project Settings -> Publishing -> Add publisher with:

  • Owner: codanael
  • Repo: oci-modelcar
  • Workflow: release.yml
  • Environment: pypi

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

oci_modelcar-0.2.0.tar.gz (98.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

oci_modelcar-0.2.0-py3-none-any.whl (25.6 kB view details)

Uploaded Python 3

File details

Details for the file oci_modelcar-0.2.0.tar.gz.

File metadata

  • Download URL: oci_modelcar-0.2.0.tar.gz
  • Upload date:
  • Size: 98.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for oci_modelcar-0.2.0.tar.gz
Algorithm Hash digest
SHA256 f2f2e7b1a2d955948aca5a41e13562fbaa0c4e6a2cd69f4d592cfcac509dae54
MD5 e2e85cf285cedd5af8db8fa543d6dda8
BLAKE2b-256 580f1a7049c490e629c01da22b5bc37265ef579a5a3c216143755495563956fd

See more details on using hashes here.

Provenance

The following attestation bundles were made for oci_modelcar-0.2.0.tar.gz:

Publisher: release.yml on codanael/oci-modelcar

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file oci_modelcar-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: oci_modelcar-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 25.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for oci_modelcar-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 efc932fe6f4bda51c639452d79e3662ebd51662f70a52323bf2bf144f9a7fa70
MD5 7028ef38339d0c473f752f4cc3299b9d
BLAKE2b-256 fc314667b53ac004c9dcdc220ba71d1f4c37dcb580d3f25ce6ff403e98d651b5

See more details on using hashes here.

Provenance

The following attestation bundles were made for oci_modelcar-0.2.0-py3-none-any.whl:

Publisher: release.yml on codanael/oci-modelcar

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page