You run the registry: you decide which models, quants, and runtime configurations your team gets, and you sign them. This guide walks the whole job — first publish to ongoing maintenance — using the commands as they exist today. Everything here assumes one curator, one key, one static HTTPS host.
Every byte a client uses is verified against your key or a hash you signed. The host can serve files or fail; it cannot lie successfully. Any static file server on your domain works.
Models, runtime archives, and registry payloads are pinned by SHA-256. Where the bytes come from — GitHub, your mirror, a teammate on the LAN — never matters; whether they match does.
Release numbers, hash computation, and publish verification are all done by the tooling. If a command asks you to remember a number or paste a hash, something is wrong.
Prerequisites: a clone of the repo, Rust, a domain with any static file host behind it, and a machine that can run the models you intend to curate — curation means you ran it first. The tooling removes the clerical work; the judgment is yours.
The bootstrap script builds the tools, generates your Ed25519 signing key, and walks manifests and profile creation interactively.
The secret is printed once. It is the team identity: every client that joins will trust this key, and it cannot be recovered or rotated in-band. The script will not continue until you confirm the backup, and it remembers whether you ever did.
$ scripts/bootstrap-curator.sh -- Step 1: Create signing key -- Key ID (e.g. myorg-2026): myteam-2026 wrote registry-signing-secret.json and registry/trust.json Back up the signing secret NOW — it cannot be recovered… myteam-2026 kkC8/wCCpwqBu3DWiROHnrX3Y5gdH5D4Fx0ImyJJdQA= Type 'saved' once it is in your password manager: saved
Manifests come from the Hugging Face API — filenames, sizes, SHA-256 from LFS metadata. Pin a commit SHA, never a branch: branches are selectors, not identity.
enrich adds the BLAKE3 hash (the LAN-transfer identity)
for any file you have locally — including the copy the app already
hashed on your own machine.
$ registry-gen gen unsloth/Qwen3.6-35B-A3B-GGUF \
--revision 1a2b3c4d… # commit SHA, not "main"
wrote registry/manifests/unsloth--Qwen3.6-35B-A3B-GGUF.json
— 14 entries (2 with blake3)llama.cpp publishes prebuilt archives per b-tag that are
directly installable — relocatable, self-contained, signed where the
platform requires it. pin-runtime downloads them, computes
the archive and binary hashes, rewrites the profile in place, and drops
mirror copies for your host.
You trust ggml-org's CI once, now, when you sign these pins. After that the bytes are immutable — the URL points at your mirror, and a client accepts nothing that doesn't hash to the pin.
A custom build (a tuned Vulkan build, a backend upstream doesn't ship) is packaged by script into the same shape and must earn its place by benchmark against the official asset.
$ registry-gen pin-runtime b10145 \
--artifact macos-arm64-metal=macos-arm64 \
--artifact linux-x86_64-strix-halo=ubuntu-vulkan-x64 \
--profile registry/profiles/code-agent.json \
--mirror dist/runtimes \
--url-base https://weights.example.com/runtimes
pinned macos-arm64-metal = ggml-b10145-macos-arm64
archive_sha256 f3ec2351e06322478e3f38f23f…
binary llama-b10145/llama-server
binary_sha256 a4998768a70ba2be02617ec9d8…
updated registry/profiles/code-agent.jsonrelease merges manifests and profiles into the payload,
signs it, and verifies its own output. --initial is required
exactly once — for every later release the tool reads the previous
version, authenticates it against your trust root, and increments.
$ registry-gen release --initial
wrote registry/registry.json — 1 manifests, 1 profiles
wrote registry/release.json — release 1
verified release 1 — 1 manifests, 1 profiles
release 1 signed and verified — publish registry.json
and release.json togetherThe host needs nothing but files. The script uploads runtime mirrors first (a client must never see a release whose bytes aren't there), stages the registry pair under temporary names, renames payload before release document, then downloads both files back over the public URL and byte-compares them against what was signed. "Published" means proven, not attempted.
registry.json — the signed payloadrelease.json — version + signatureruntimes/…tar.gz — mirrored runtime archivestrust.json is deliberately not on the host: the
trust root travels inside invites, out-of-band.
Throughout this guide weights.example.com stands in for
your host — the reference deployment serves the registry at
https://registry.wheresmy.ai (registry.json and
release.json at the root); everything else, like the app
installer, uses the shorter https://wheresmy.ai.
$ scripts/publish-registry.sh \
deploy@web:/var/www/weights https://weights.example.com
verified release 1 — 1 manifests, 1 profiles # preflight
verified release 1 — 1 manifests, 1 profiles # read-back
published and verified at https://weights.example.comThe invite carries the registry URL and your public key. The fingerprint is the out-of-band check that the key wasn't swapped in transit — post it through a different channel than the link (link in chat, fingerprint in a direct message, or vice versa).
Teammates paste the link into the Weights app (or run
weights join on a headless box) and the rest is the
end-user journey: verify, fetch from LAN where possible, install, run.
$ registry-gen invite \
--registry https://weights.example.com \
--profile code-agent --org "My Team"
ccweights://join?registry=…&key_id=myteam-2026&key=…
Fingerprint: 7f3a 92c1 08b4 d6e5
Paste this alongside the link, in a separate message.Every change ships the same way: edit → release →
staging → your own machine → promote. Point your own app's registry URL
at https://weights.example.com/staging and you become client
zero for every release — each one is installed through the exact end-user
code path before teammates can see it. Promotion re-runs publish without
--staging: same files, no re-sign.
registry-gen pin-runtime b10234 …registry-gen releasescripts/publish-registry.sh … --stagingscripts/publish-registry.sh …registry-gen gen … / enrich …model_sha256 at itrelease → staging → promoteruntime_args)release → staging → promotescripts/package-release.sh v0.2.0 — builds, ad-hoc
codesigns, tars app + CLI, generates install.shdist/ trio next to the registrycurl -fsSL …/install.sh | sh
(browser downloads arrive quarantined — the terminal path is the
supported one)registry-gen invite …Each refusal exists because the mistake it prevents is expensive and was either made or nearly made. When you hit one, the answer is never to force past it — it is to understand which of these you are about to be.
| Refusal | What it protects |
|---|---|
release without --initial, no release.json |
An accidentally deleted release document would silently restart at version 1 — valid to you, rejected by every synced client as a rollback. Restore the document instead. |
keygen into ~/.weights |
The data directory is wiped by tests and fresh installs — the exact
way the first secret was lost. The guard resolves
symlinks and ..; there is no clever path around it. |
publish-registry.sh … --stagin |
A typo in the staging flag must not fall through to production — unknown arguments and wrong argument counts abort. |
| Publish declares success only after byte-compare | A CDN serving the previous release would pass signature verification. Identical bytes prove the host serves this release, not a valid one. |
| Client rejects version 2 twice with different content | Equivocation guard: one version number means one payload, forever.
Never edit and re-sign the same version; run
release again. |
keygen with a
new key_id, re-pin and release --initial, publish, send
a fresh invite to every member — and every machine that synced under the
old key must reset its ~/.weights before joining. This is
a bad week. The backup line from step 1 is what stands between you and it.
release (a higher version), publish. Clients converge on next
sync. The staging loop exists so this stays rare — a release that never
ran on your machine should never reach the team.