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.0

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.0.tar.gz (109.6 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.0-py3-none-any.whl (28.4 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: oci_modelcar-0.4.0.tar.gz
  • Upload date:
  • Size: 109.6 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.0.tar.gz
Algorithm Hash digest
SHA256 32167eafb9af09b8b76bd029cc954446e0d6d5564a486ced581cc6939b1c6bfb
MD5 84dda393388b6403dec714d0150ae40e
BLAKE2b-256 f34c1b863700e56aacbc6c0084d25144ffc28c0314ab93f3a8cc42a5849a58aa

See more details on using hashes here.

Provenance

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

File metadata

  • Download URL: oci_modelcar-0.4.0-py3-none-any.whl
  • Upload date:
  • Size: 28.4 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.0-py3-none-any.whl
Algorithm Hash digest
SHA256 784b45b5ea411173c0935d4720037e15dcbf85c4a48f48e6cebbc95b38364efb
MD5 b6c5b23e284014891a2cc0b7f4b723b4
BLAKE2b-256 f2ddff0529342127aaa38e70018f8ac45cb53712529ea4c25f632b83dd0979ee

See more details on using hashes here.

Provenance

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