Archive complete revisions of Hugging Face model and dataset repositories on long-term storage, and publish them as immutable trees that downstream systems can copy and verify. Public repositories work anonymously; private and gated repositories use an explicit required systemd credential.
hfcache resolves a configured branch or tag to an immutable commit SHA,
downloads the complete repository snapshot at that SHA through the official
huggingface_hub client, SHA-256 verifies every newly introduced object, and
atomically publishes an ordinary-file tree. Consumers mount a server-enforced
read-only NFS export, read one small JSON reference, and copy the exact immutable
tree it names.
A revision is either completely published and verifiable, or it is not visible to consumers at all.
What it does
- Immutable revisions. Every download is pinned to a resolved 40-character commit SHA. Branch and tag names are resolved once per run, never trusted mid-flight.
- Repository-complete. Every file in the upstream repository at that SHA. No include or exclude filters exist, by design.
- Verified before publication. Newly introduced unique content is read and hashed locally. Upstream-supplied digests are comparison values, never substitutes.
- Bounded retention. Five unique revisions per repository. A sixth successful publication prunes the oldest eligible one; the current revision is never pruned.
- Never destructive on error. Upstream
403,404, deletion, or removal from configuration preserves every local revision. Where a failure can leave more or less data, it leaves more. - Explicit lifecycle removal.
purgedeletes only complete repositories that are already absent from configuration, after a reviewable dry-run, with transaction recovery and byte-for-byte manifest audit evidence. - Crash-safe. Every mutation is a single atomic filesystem operation followed by a durable checkpoint. Every state a crash can leave has one correct resolution.
- Content is data, never code. Nothing from a repository is executed,
imported, unpickled, or given
trust_remote_code. - Restricted access is explicit.
hub.token_mode = "required"enables private and gated repositories only when a credential resolves. Every consumer of the shared publication export must be authorized to receive that content.
Requirements
- Writer host — Linux with systemd. The runbook
(
docs/deployment-centos-stream-10.md) targets CentOS Stream 10 with SELinux enforcing; the SELinux steps are skippable on other distributions. Python 3.14 arrives via uv — no system Python is used. - Storage — any NFSv4.2 server that can provide the two exports and the
identity mapping in
docs/storage-requirements.md(the reference deployment uses TrueNAS). The archive root must be an NFS mount; preflight refuses anything else by design. - Consumers — any authorized host that can mount the read-only export; the copy helper needs only Python 3. The export has no per-repository ACLs, so all consumers must be permitted to read every configured restricted repository.
- No NAS yet?
docs/quickstart.mdruns everything on one machine with a loopback NFS export.
Status
v0.1.6 — authenticated-repository release candidate. The writer is complete
and running against a live NFS-backed ZFS archive: it syncs public, private, and
gated repositories, publishes, retains five
revisions, prunes, verifies, repairs, and recovers, under systemd with SELinux
enforcing and validated hardening. Release history is in
CHANGELOG.md; the annotated tags carry the complete notes.
What remains is operator work that this project cannot perform for itself — the
read-only consumer export, snapshots and replication, the disjointness proof from
a second client, reboot persistence, and enabling the quarterly verification
timer. All of it is listed, with copy-pasteable instructions and acceptance
checks, under Operator handoff in docs/progress.md.
docs/progress.md is the authoritative work-package tracker; docs/plan.md is
the accepted implementation plan.
Documentation
| Document | Contents |
|---|---|
docs/architecture.md |
Architecture decisions, filesystem layout and invariants, transaction and crash-recovery boundaries |
docs/configuration.md |
Every configuration key, its constraints, and credential handling |
docs/schemas.md |
Durable JSON formats: manifests, references, catalog, reports, journals |
docs/cli.md |
Command surface, JSON output, exit codes, error taxonomy |
docs/security.md |
Assets, trust boundaries, threats, and the control enforcing each |
docs/quickstart.md |
Run everything on one machine with a loopback NFS export |
docs/storage-requirements.md |
What any NFSv4.2 server must provide: filesystem semantics, the two exports, identities |
docs/deployment-centos-stream-10.md |
Building the writer host, installing, upgrading, and rolling back |
docs/operations.md |
Running it: sync, verify, prune, purge, status, timers, exit codes, measured performance |
docs/recovery.md |
Corruption, repair, snapshot restore, lost writer, and the stopped-machine states |
docs/consumers.md |
Copying and verifying a revision from a downstream host |
docs/cockpit.md |
The Cockpit plugin's contract: rules, privilege model, operation surface |
docs/cockpit-plugin/ |
Installing and using the Cockpit plugin |
docs/plan.md |
The accepted implementation plan |
docs/progress.md |
Work-package tracker and operator handoff |
docs/storage-requirements.md states what any
NFSv4.2 server must provide. docs/truenas.md — the reference deployment's
NAS-side runbook against those requirements — remains unwritten by design: it
belongs to whoever performs the NAS-side handoff items (docs/progress.md,
operator handoff item 11), because none of its content can be made or verified
from the writer.
Coding agents start at AGENTS.md — a context router into these
documents and the agent-specific set under docs/agents/.
CLAUDE.md is a symlink to it.
Browser administration
An optional Cockpit plugin under cockpit/ administers a
deployed writer from the browser: sync control with live activity, archive
browsing, configuration editing, schedule management, verification, run
reports, and maintenance. Every action is one of the runbook's own CLI
commands or systemd units — never a side channel — and the CLI remains the
application's only interface.
docs/cockpit-plugin/ covers installation and
usage; docs/cockpit.md is the contract it is written
against.
Repository layout
| Path | Contents |
|---|---|
src/hfcache/ |
The application: one package on huggingface-hub, pydantic, typer, and the standard library |
cockpit/ |
The Cockpit administration plugin: a browser UI over the CLI and the systemd units |
docs/ |
The contracts and runbooks in the table above |
docs/agents/ |
Agent-facing workflow, conventions, environment, and release docs |
packaging/ |
systemd units and timers, journald and tmpfiles drop-ins |
scripts/ |
check.sh (the quality gate), install-release.sh, and the hardening probe pair |
examples/ |
A runnable config.toml, and copy-revision.py — the consumer-side helper |
tests/ |
unit/ (default, offline), system/ (opt-in markers), fixtures/ (golden schemas) |
Development
Requires uv and CPython 3.14.
git clone https://github.com/wtcross/hfcache.git cd hfcache uv sync # create the locked development environment uv run hfcache --help
The quality gate is one script — lock check, formatting, lint, types, the default test suite, and a build — and it is exactly what must pass from a clean checkout:
scripts/check.sh # the docs/plan.md §8.4 gate scripts/check.sh --with-build # plus a wheel install smoke test
Three opt-in suites are deselected by default:
uv run pytest -m live # contacts the Hugging Face Hub uv run pytest -m nfs # requires the live NFS archive mount on the writer uv run pytest -m build # builds artifacts and installs into a throwaway venv
License
MIT (LICENSE), with one subtree different:
cockpit/ is LGPL-2.1-or-later — it derives from the Cockpit
starter kit — declared per file in SPDX headers (decision 90 in
docs/progress.md).