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.1.tar.gz (100.4 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.1-py3-none-any.whl (25.6 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: oci_modelcar-0.2.1.tar.gz
  • Upload date:
  • Size: 100.4 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.1.tar.gz
Algorithm Hash digest
SHA256 3c3f0e211ae9262fbca341ab498a0dc68f98cf0cac09f20be6df6630fdc12d93
MD5 4bdeaeb0c4f7eea36e14046fc54f66f2
BLAKE2b-256 5aada5d6bf09dc1fae88a53a866bc8bec84d2aca724a09bc050d5741ed8571b2

See more details on using hashes here.

Provenance

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

File metadata

  • Download URL: oci_modelcar-0.2.1-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.1-py3-none-any.whl
Algorithm Hash digest
SHA256 04d370b1d6698cbda6adca7ab3a4f3f97adb4f61bbedaf119856a27bf29993db
MD5 3b21de9179b1a365f4ea6b6a55327903
BLAKE2b-256 759f2750f89903ec634d77b9ffd4f35103c138fc41d13d59062b18fd5e4767c8

See more details on using hashes here.

Provenance

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