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

# Replace with the version you want to verify
VERSION=0.4.1

python -m pypi_attestations verify pypi \
    --repository https://github.com/codanael/oci-modelcar \
    pypi:oci_modelcar-${VERSION}-py3-none-any.whl
python -m pypi_attestations verify pypi \
    --repository https://github.com/codanael/oci-modelcar \
    pypi:oci_modelcar-${VERSION}.tar.gz

pypi-attestations fetches the artifact and its provenance from PyPI on its own when you pass pypi:<filename>, so no separate pip download step is needed. --repository expects the full GitHub/GitLab URL of the publishing repo; an OK: <filename> line per artifact (exit 0) means the provenance chains correctly through Sigstore (Fulcio cert + Rekor transparency log).

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.4.1.tar.gz (111.9 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.4.1-py3-none-any.whl (29.3 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: oci_modelcar-0.4.1.tar.gz
  • Upload date:
  • Size: 111.9 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.4.1.tar.gz
Algorithm Hash digest
SHA256 cf85401b75ce1eebfe95ef61436bc8387c43a15080d23d22bfd5f37d5fc03f4d
MD5 7446908d29db46b2441d40176c507303
BLAKE2b-256 526c1c9e078a87cccb5ec9c210eafe19d13da0c22b6814b59532ab7304d6d5db

See more details on using hashes here.

Provenance

The following attestation bundles were made for oci_modelcar-0.4.1.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.4.1-py3-none-any.whl.

File metadata

  • Download URL: oci_modelcar-0.4.1-py3-none-any.whl
  • Upload date:
  • Size: 29.3 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.4.1-py3-none-any.whl
Algorithm Hash digest
SHA256 9d8e51bc6ac092cf10dc3ff725f281f62a225a45a4246c68e28e7348a11c42a3
MD5 2d7537ea15060615bfa3014f7cedc6f1
BLAKE2b-256 09f3713d4357d04ff0e78489a5fba4add3c217859183e3b7126673658c3c3792

See more details on using hashes here.

Provenance

The following attestation bundles were made for oci_modelcar-0.4.1-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