Muninn

Fetch it once.
Serve it everywhere.

A pull-through cache that speaks two protocols — the Hugging Face Hub API and the OCI registry API — so model weights and container images cross your uplink once instead of once per machine.

What it does

Point a client at Muninn instead of at the upstream. The first request fetches from upstream and keeps a copy; every request after that is served from local disk. Nothing is rewritten, so checksums and signatures still verify at the client exactly as they would have upstream.

Two protocols, one service

Hugging Face file and metadata traffic, and the full OCI registry pull surface. The same host answers both.

Any upstream, by path

Registries are addressed by prefix rather than configured one at a time. No mirror entry per registry.

Bytes pass through unaltered

Muninn is a cache, not a rewriter. Image signatures and file digests keep verifying at the client.

Containers

The first path component names the upstream registry. That is the whole configuration:

# instead of docker.io/library/redis:7
docker pull ravencache.io/docker.io/library/redis:7

# any registry, same shape
docker pull ravencache.io/ghcr.io/some/project:v2
docker pull ravencache.io/quay.io/other/tool:latest

Because the upstream is part of the reference, adding a registry needs no change on the cache and no change to a daemon config. A reference is self-describing: you can see what it pulls and where it came from.

Models and datasets

Set one environment variable and the Hugging Face libraries do the rest:

export HF_ENDPOINT=https://ravencache.io

# unchanged code, now served from cache
python -c "from huggingface_hub import snapshot_download;
           snapshot_download('openai-community/gpt2')"

Metadata is proxied; file bytes are intercepted and cached. The second machine to ask for a checkpoint reads it off local disk at NVMe speed rather than pulling it again.

Why bother

The uplink is the bottleneck, not the disk

A cluster of machines pulling the same multi-gigabyte checkpoint pays for it once per machine. A cache turns that into once, total — and every subsequent read is local, which is faster than any CDN can be.

Upstream is not forever

Repositories get renamed, gated, or removed. A cache that already holds the bytes keeps working. Muninn can be configured to retain content whose upstream has disappeared, which makes it a reproducibility archive rather than just an accelerator.

Ingested bytes are verified

Content is hashed on the way in and refused when it does not match the digest it was fetched under — on both protocols. A corrupt or truncated response is rejected rather than cached and re-served forever.

Verification covers ingest. It says nothing about bytes that were already on disk before it was enabled — a distinction worth keeping, because a green counter that silently means “nothing was checked” is worse than no counter at all.

What it is not

Muninn is a cache, not a registry and not a mirror. It does not host content of its own, does not rewrite manifests, and does not try to be the source of truth for anything. Where a request cannot be served from cache, it is a proxy to the real upstream and nothing more.

Access control is deliberately simple: a credential is a key to the gate, not a per-user scope. Anyone who can authenticate can read anything the cache holds. That is the right model for a shared team cache and the wrong one for multi-tenant isolation, and it is stated here rather than discovered later.

Open source

Muninn is MIT licensed and developed in the open at github.com/skibare87/muninn — including the parts that went wrong. The README documents the traps, the measurements behind each default, and the failures that produced them.