Five minutes. The registry is plain JSON on Git. There is no database. Every file under registry/ is a record; every record carries schema_version: "local-ai-registry/v1" and validates against registry/schema/<collection>.schema.json.
| Collection | One record is | Key fields |
|---|---|---|
hardware/ |
One accelerator or Apple chip at one memory capacity. rtx-3090-24gb, apple-m5-max-128gb. |
vendor, memory, compute, accelerator_backend, aliases, products |
model/ |
One canonical base model. qwen3-8-27b. |
params, active_params, architecture, huggingface |
model-instance/ |
One downloadable artifact of a model: a quantization at a pinned revision. | model_id, repository, revision, weights, huggingface |
recipe/ |
One artifact × hardware × engine launch. The unit of the registry. | model_instance_id, hardware_id, hardware_count, engine, launch, serving, capabilities, status, recommended, speed_sweep_ids |
speed-sweep/ |
Measured evidence for one recipe. | recipe_id, accepted_at, source, metrics, rows |
benchmark/ |
Scraped public leaderboard scores, keyed by model variant. | category, rows[].model_id, rows[].score |
price/<product>/<region> |
Retailer observations for one product in one region. | product, region, observations, hardware[] |
asset/ |
An engine config or patch a recipe mounts. Manifest plus blob. | file, sha256, purpose |
recipe/<id>.json
model_instance_id ─▶ model-instance/<id>.json
model_id ─▶ model/<id>.json
hardware_id ─▶ hardware/<id>.json
products[] ◀─ price/<product>/<region>.json
speed_sweep_ids[] ─▶ speed-sweep/<id>.json
launch.asset_ids[]─▶ asset/<id>.json
Underscores in a field name become hyphens in the directory: model_instance_id resolves under model-instance/.
launch.kind is docker, compose, script, or reference. Only the first three are executable. A docker launch states image (digest-pinned when validated), entrypoint or arguments, environment, mounts, host_port, container_port, accelerator_backend, network_mode, and provenance for the image. reference launches keep an observed command in metadata and are never run.
status is one of two values and is computed, not typed in.
validated requires all of:
launch.kind is executable.revision is a full commit hash.@sha256-pinned (docker/compose) or the script path contains a 40-hex commit.source.kind == "acceptance-run", or a commit-pinned campaign sweep from a repository under github.com/0xSero/.serving.max_context_tokens.candidate is everything else. Candidates stay visible with the reason they are not validated.
The definition lives in one file, scripts/trust.py. validate_registry.py imports it and CI fails if any stored status disagrees.
registry/index/recommendations.json maps each hardware id to exactly one recipe. An entry must be validated, single-GPU, docker, bridge networking, no host IPC. scripts/recommend.py picks it (model tier by VRAM, then engine rank, then largest context, then newest acceptance); the validator refuses more than one per hardware. Served at GET /api/v1/recommendations.
registry/index/ holds discovery shards so a client fetches only what its question needs: collections.json (ids and counts), recipes.json (compact filter rows), recipes-by-hardware.json, instances-by-model.json, benchmarks-by-model.json, prices-by-hardware.json, hardware-speed-evidence.json, recommendations.json. All are regenerated by make index and CI refuses a stale index.
Everything under /api/v1 is GET-only and reads registry/ directly: index, recommendations, facets, models, model-instances, hardware, prices, recipes, compatibility, speed-sweep, benchmarks, each with /:id. Filters mirror record fields. There is no write path.