Stream HuggingFace models directly into OCI registries as multi-layer images
Project description
oci-modelcar
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:
- Triple-trip: HF -> local cache -> registry
- One huge layer: no cross-repo blob mount possible
- 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_TOKENenv var (recommended)~/.cache/huggingface/token(created byhuggingface-cli login)
OCI registry:
OCI_USERNAME+OCI_PASSWORDenv vars (recommended for CI)~/.docker/config.json(docker loginwrites 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, nobytesprefix per OCI spec) - Resume via
GET /v2/<repo>/blobs/uploads/<id>and theRange: 0-Nheader 416 Range Not Satisfiableis treated as "ask the server, sync, retry"- HEAD validation cross-checks
Docker-Content-Digest - Layers use
application/vnd.oci.image.layer.v1.tar(uncompressed) solayer.digest == diff_idby 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)
- Bump
versioninpyproject.tomland updateCHANGELOG.md. - Tag:
git tag v0.1.0 && git push --tags. - The
release.ymlworkflow 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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3c3f0e211ae9262fbca341ab498a0dc68f98cf0cac09f20be6df6630fdc12d93
|
|
| MD5 |
4bdeaeb0c4f7eea36e14046fc54f66f2
|
|
| BLAKE2b-256 |
5aada5d6bf09dc1fae88a53a866bc8bec84d2aca724a09bc050d5741ed8571b2
|
Provenance
The following attestation bundles were made for oci_modelcar-0.2.1.tar.gz:
Publisher:
release.yml on codanael/oci-modelcar
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
oci_modelcar-0.2.1.tar.gz -
Subject digest:
3c3f0e211ae9262fbca341ab498a0dc68f98cf0cac09f20be6df6630fdc12d93 - Sigstore transparency entry: 1469548205
- Sigstore integration time:
-
Permalink:
codanael/oci-modelcar@4cca16b5c269f45cc0371d40f454f240ff1dead0 -
Branch / Tag:
refs/tags/v0.2.1 - Owner: https://github.com/codanael
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@4cca16b5c269f45cc0371d40f454f240ff1dead0 -
Trigger Event:
push
-
Statement type:
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
04d370b1d6698cbda6adca7ab3a4f3f97adb4f61bbedaf119856a27bf29993db
|
|
| MD5 |
3b21de9179b1a365f4ea6b6a55327903
|
|
| BLAKE2b-256 |
759f2750f89903ec634d77b9ffd4f35103c138fc41d13d59062b18fd5e4767c8
|
Provenance
The following attestation bundles were made for oci_modelcar-0.2.1-py3-none-any.whl:
Publisher:
release.yml on codanael/oci-modelcar
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
oci_modelcar-0.2.1-py3-none-any.whl -
Subject digest:
04d370b1d6698cbda6adca7ab3a4f3f97adb4f61bbedaf119856a27bf29993db - Sigstore transparency entry: 1469548356
- Sigstore integration time:
-
Permalink:
codanael/oci-modelcar@4cca16b5c269f45cc0371d40f454f240ff1dead0 -
Branch / Tag:
refs/tags/v0.2.1 - Owner: https://github.com/codanael
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@4cca16b5c269f45cc0371d40f454f240ff1dead0 -
Trigger Event:
push
-
Statement type: