The library¶
A library is a self-contained, portable store for your models — the model
files plus their own metadata (tags, cards) under <root>/.heim/. Because the
metadata lives inside the library, the whole thing travels: move the drive to
another machine and your tags come with it.
The default library is HEIM_LIBRARY_ROOT (set per machine, e.g. an external
drive). A project can compose more libraries in front of
it. List what's in scope with:
Layout¶
Models are organized by format, with the Ollama store kept in its native (restorable) layout:
<root>/
├── gguf/<publisher>/<repo>/… # *.gguf (HF + LM Studio pulls)
├── mlx/<publisher>/<repo>/… # *.safetensors + config.json
├── safetensors/<publisher>/<repo>/… # full-precision weights
├── huggingface/<repo_id>/… # fallback for repos with no recognizable weights
└── ollama/manifests/… + ollama/blobs/ # Ollama's content-addressed store
Both Hugging Face and LM Studio pulls land in the format bucket (gguf/, mlx/,
safetensors/), so the same GGUF from either source is one copy on disk.
Migrating an older library
Libraries built before this used huggingface/<repo>. Reorganize them into the format
buckets with heim library migrate — a dry-run prints the plan; --apply performs it (a
same-drive rename per model, and it removes any copy already duplicated in a bucket).
The scanner finds runnable models anywhere under the root (any directory with
*.gguf/*.safetensors, plus the Ollama native store). When a GGUF repo ships
several quants, a balanced one (Q4_K_M first) is picked automatically. macOS
._ AppleDouble files are ignored.
Ollama new-format models
Some Ollama models use a per-tensor format (hundreds of .tensor blobs)
rather than a single GGUF — those can only run via Ollama itself, not
llama.cpp, so they won't appear as runnable.
Model cards¶
Each model's card (its README) is what the UI/CLI info panel shows. Hugging Face pulls ship one; some LM Studio downloads (and older pulls) don't. Backfill the missing ones:
heim library cards # fetch a missing README from HF into each model's .heim/ sidecar
heim library cards --refresh # re-fetch even models that already have a card
It infers the HF repo from the model's <publisher>/<repo> name, is idempotent (skips models that
already have a card), and never fails on a model that isn't on the Hub. Ollama models keep their
generated card (built from the manifest).
Installing a model into a runtime¶
The library keeps one canonical copy per (model, format). Some runtimes read
a loose GGUF/MLX in place (LM Studio, llama.cpp), but Ollama keeps a
content-addressed blob store and needs the GGUF imported first. heim library
install feeds it from the canonical copy, so the drive stays the single source of
truth and the Ollama copy is regenerable:
heim library install Qwen3.5-4B-GGUF # → ollama create qwen3.5-4b
heim library install Qwen3.5-4B-GGUF --name qwen-fast --system "Be terse."
ollama run qwen3.5-4b
It generates a Modelfile pointing at the canonical GGUF (FROM <path>, plus the
vision projector and an optional SYSTEM prompt) and runs ollama create. A local
Ollama daemon must be running (ollama serve or the app). Only GGUF installs into
Ollama — MLX/safetensors aren't supported there.
LM Studio reads loose GGUF/MLX directly, and the library's gguf/<publisher>/<repo>/
and mlx/<publisher>/<repo>/ buckets already match LM Studio's layout — so it needs no copy,
just a pointer:
heim library install Qwen3.5-4B-GGUF --to lmstudio # symlink into LM Studio's models dir
heim library install gemma-4-26B-A4B-MLX --to lmstudio # MLX works too (--format to disambiguate)
This symlinks <lmstudio_models_dir>/<publisher>/<repo> to the library copy (the link lives on
the machine disk, so exFAT's no-symlink limit doesn't apply — zero bytes copied). It's
idempotent and won't clobber a model LM Studio downloaded itself; rescan/restart LM Studio to
see it. HEIM_LMSTUDIO_MODELS_DIR overrides the target. To surface every model at once
instead, point LM Studio at the whole gguf/ (and mlx/) bucket as a models directory.
mlx_lm needs no install step at all: mlx_lm.server / mlx_lm.generate run a loose MLX
model in place, and the library's mlx/<publisher>/<repo>/ is exactly that. Point it at the
model directory — e.g. mlx_lm.server --model "$HEIM_LIBRARY_ROOT/mlx/mlx-community/Qwen3.6-27B-4bit"
(heim itself serves MLX this way). So there's no --to mlx_lm: it already reads the canonical copy.
Seeing and undoing installs¶
The install is reversible, and you can see what's fed where — the library always keeps the canonical copy:
heim library installed # which runtimes each model is installed into
heim library uninstall Qwen3.5-4B-GGUF --from lmstudio # remove heim's LM Studio symlink
heim library uninstall Qwen3.5-4B-GGUF --from ollama # ollama rm the imported copy
installed cross-references the drive against LM Studio (a heim symlink pointing into the
library) and Ollama (a model whose deterministic install name is present). uninstall removes
only what heim put there — it never deletes a real LM Studio download, and never touches the
library copy.
Which formats to keep¶
Format is a per-model choice, not "keep every format of everything":
- GGUF — the portable, cross-runtime backbone (Ollama, LM Studio, llama.cpp; Mac + Linux). Keep for anything you want to run widely. The most shareable tier.
- MLX — Apple-Silicon native and fastest on the Mac. Keep for models you run locally on an M-series machine. (MLX repos are just HF repos, so no separate downloader.)
- safetensors — original full-precision weights, 2–4× the size of a quant. Keep only for models you'll re-quantize or fine-tune — not blanket. Pull on demand, drop when done.
Default policy: keep GGUF + MLX ready for a model you actually use; fetch safetensors only
when you need to convert or train. heim library rm <model> --format safetensors reclaims space
once a conversion is done.
heim library formats makes this actionable: one row per model with a column per format present
and their sizes, flagging any redundant safetensors copy (a GGUF/MLX build already exists) and
any model that's only safetensors (no ready-to-run quant), with the total space reclaimable by
dropping the redundant copies.
Checking integrity¶
heim library verify checks each model on disk is intact — the declared weights (and vision
projector) exist and are non-empty, and the recorded model card is present:
heim library verify # all models
heim library verify Ornith # one
heim library verify --deep # also re-hash Ollama blobs against their sha256
Ollama's store is content-addressed, so --deep gives true content integrity there. HF/LM Studio
pulls carry no per-file checksums, so their check is structural (present + non-empty).
Rebuild a drive¶
Because every model already records where it came from, the library is its own manifest.
heim library manifest --save models.toml writes a portable want list — a [[model]] entry per
model (source + name + format). Keep that file anywhere (commit it to a repo, drop it on another
machine); nothing is stored back in the library. On a fresh or replacement drive, point
HEIM_LIBRARY_ROOT at it and run heim library sync models.toml: it diffs the list against what's
present and re-pulls only what's missing, via the normal per-source paths (--dry-run first to
preview). One model failing doesn't stop the rest, and it exits non-zero if any did. LM Studio
backups re-pull from their Hugging Face equivalent; Ollama entries need the model in your local
Ollama store first (ollama pull <name>). See heim library manifest / sync.
Model cards & metadata¶
Each pulled model gets a .heim/ sidecar with metadata.json and a
model-card.md (the upstream README for HF/LM Studio; a generated Modelfile-style
card from the manifest layers for Ollama) — so every model carries its run
instructions. Tags live in one <root>/.heim/tags.json per library.
Composing libraries in a project¶
A project (heim.toml) can use more than one library, in priority order — its own
project-local library plus the shared/default one — so you can keep a few hot
models next to a project while still using the big archive:
@shared is the token for the machine's default library (HEIM_LIBRARY_ROOT), so
the file stays portable. heim project init scaffolds a models/ directory and
this list; reads span all listed libraries (first match wins), while heim library
pull targets the first (project-local) one by default (--shared for the shared
one). See Projects.
Storage on an external drive¶
A library is portable — point HEIM_LIBRARY_ROOT at wherever it lives on each
machine; the models and their tags come along.
- exFAT is a good choice for a drive shared between macOS and Linux (the only filesystem both read/write natively). No journaling — eject cleanly; no symlinks — dedup is store-once-and-copy, not by link.
- Mount paths differ per machine (e.g.
/Volumes/<drive>on macOS vs/media/<user>/<drive>on Linux) — setlibrary_rootinheim.toml, or override it per machine withHEIM_LIBRARY_ROOT. - Re-downloadable weights make the lack of journaling low-stakes.
Runtime assets travel too
Some runtimes fetch assets by Hugging Face repo id rather than from the library — e.g.
mlx-audio's Dia loads its DAC codec that way. So they don't get left behind in
~/.cache/huggingface, heim points HF_HOME at <library_root>/.cache/huggingface (unless
you've set HF_HOME/HF_HUB_CACHE yourself). Run heim voice setup once to seed Dia's codec
onto the drive; Dia then works offline and travels with it.