Tiny-model registry — schema and verification¶
The registry at model/tiny/registry.json is the trust root for libvmaf's tiny-AI surface. Every ONNX model shipped under model/tiny/ is indexed here with a SHA-256 pin, license metadata, and a Sigstore bundle path. T6-9 / ADR-0211 formalised the schema and wired --tiny-model-verify to cosign verify-blob.
Registry shape¶
{
"$schema": "./registry.schema.json",
"schema_version": 1,
"models": [
{
"id": "learned_filter_v1", // kebab-case; --tiny-model=<id>
"kind": "filter", // fr | nr | filter
"onnx": "learned_filter_v1.onnx",
"opset": 17,
"sha256": "412d537…e27",
"quant_mode": "dynamic", // fp32 | dynamic | static | qat
"int8_sha256": "1cff6fe…2d3",
"quant_accuracy_budget_plcc": 0.01,
"license": "BSD-3-Clause-Plus-Patent",
"license_url": "https://github.com/VMAFx/vmafx/blob/master/LICENSE",
"sigstore_bundle": "learned_filter_v1.onnx.sigstore.json",
"description": "Tiny residual filter for vmaf_pre — degraded → clean luma.",
"notes": "Self-supervised on KoNViD-1k …"
}
]
}
The full JSON Schema is at model/tiny/registry.schema.json. Fields documented inline; key invariants:
sha256is lowercase hex, 64 chars. Mismatch against the on-disk ONNX bytes is a hard error.int8_sha256is required iffquant_mode != "fp32".sigstore_bundleis a path relative tomodel/tiny/and must end in.sigstore.json. The bundle file itself is generated at release time by.github/workflows/supply-chain.yml; pre-release the path is declared but the file may be absent. The runtime verifier (--tiny-model-verify) treats absence as a fail-closed signal.licenseis an SPDX identifier when possible; for fork-trained models this isBSD-3-Clause-Plus-Patent(matches libvmaf). Upstream-derived models carry the upstream license verbatim (e.g. LPIPS-Sq isBSD-2-Clause).
Validating the registry¶
The Python validator at ai/scripts/validate_model_registry.py runs both the JSON Schema check and the cross-file consistency check (every ONNX exists, every sha256 matches, every non-smoke entry has a sidecar). It is a CI gate; run it locally before pushing:
python3 ai/scripts/validate_model_registry.py \
--out-json runs/tiny_model_registry_validation.json
# → OK: 5 registry entries valid against registry.schema.json
Pass a different registry / schema explicitly when working off-tree:
python3 ai/scripts/validate_model_registry.py /path/to/other-registry.json \
--schema /path/to/registry.schema.json \
--out-json runs/offtree_registry_validation.json
The validator falls back to a structural check when jsonschema is not installed, so distros without python-jsonschema still get the required-field invariants. Install jsonschema for full Draft 2020-12 coverage.
The optional --out-json report records the pass/fail verdict, model count, errors, registry/schema paths, and ADR-0661 run_provenance. Use it for release evidence and for debugging registry failures without relying on CI log scrollback.
Runtime verification — --tiny-model-verify¶
When the flag is set the loader:
- Looks up the model's basename in
model/tiny/registry.json(alongside the.onnx, by default). - Reads the entry's
sigstore_bundlepath. - Spawns
cosign verify-blob --bundle=<path> --certificate-identity-regexp … --certificate-oidc-issuer https://token.actions.githubusercontent.com <onnx>viaposix_spawnp(3p)(no shell — explicit argv array). - Refuses to load on any non-zero exit, missing
cosign, missing bundle, or missing registry entry.
The flag is off by default for dev-friendliness. Production deployments should set it on. cosign must be on $PATH; install prebuilt binaries from the Sigstore release page.
The C entry point is vmaf_dnn_verify_signature(onnx_path, registry_path) in core/include/libvmaf/dnn.h; both arguments are NULL-tolerant in the documented way.
Directory jail — VMAF_TINY_MODEL_DIR¶
The registry pins model identity. The optional VMAF_TINY_MODEL_DIR environment variable constrains model location. Set it to the trusted model directory on production hosts:
export VMAF_TINY_MODEL_DIR=/opt/vmaf-models
vmaf --tiny-model /opt/vmaf-models/vmaf_tiny_v2.onnx --tiny-model-verify ...
At load time libvmaf canonicalises both the jail and the requested ONNX path. The model must resolve below the jail directory; sibling-prefix escapes, symlink escapes, missing jail directories, and jail values that point at files fail closed with -EACCES. The jail does not replace registry verification or the operator allowlist: run all three layers together for production deployments.
What the registry is not¶
- Not the inference contract — per-model input/output names, normalisation, and expected ranges live in the sidecar JSON next to the ONNX (
<basename>.json). The registry stays small and easy to audit; the sidecar carries the runtime knobs. Attached multi-output models may use sidecaroutput_names[]to provide stable scalar-score suffixes for report keys. - Not the operator-allowlist source of truth — that's
core/src/dnn/op_allowlist.c. The registry pins identity; the allowlist constrains content. - Not a path allowlist — use
VMAF_TINY_MODEL_DIRwhen deployments need to reject model paths outside a caller-trusted directory.
Adding a new model¶
- Drop the
.onnxand matching<basename>.jsonsidecar undermodel/tiny/. - Compute the sha256:
sha256sum model/tiny/<name>.onnx. - Add an entry to
registry.json— see existing entries as templates; the schema enforces required fields. - Bundle generation happens at release time; pre-release the
sigstore_bundlepath may point at a not-yet-existing file. - Run
python3 ai/scripts/validate_model_registry.pyand fix any reported issues.
The /add-model <path> skill scaffolds steps 1–4 for you.
CI-only smoke fixtures (smoke: true)¶
Several registry entries exist solely to exercise the loader / validator pipeline in CI and are not user-facing surfaces. They are exempt from the ADR-0042 five-point model-card requirement; no docs/ai/models/ card is expected or needed for them.
| Registry id | Notes |
|---|---|
smoke_v0 | Minimal ONNX graph used by test_model_loader.c smoke test. |
smoke_fp16_v0 | Same graph, fp16 weights — exercises the fp16 loader path. |
smoke_multi_output_v0 | Multi-output fixture (mean_score + peak_score) used by test_vmaf_use_tiny_model.c — exercises the attached multi-output DNN path. |
smoke_v0_symbolic_batch | Symbolic-batch fixture (dynamic first dim) used by test_vmaf_use_tiny_model.c — exercises the batch-agnostic load path. |
dists_sq_placeholder_v0 | Superseded placeholder; replaced by dists_sq (real weights). The non-placeholder card lives at docs/ai/models/dists_sq.md. |
mobilesal_placeholder_v0 | Placeholder until the full U-2-Net weights clear compliance (ADR-0257). Non-placeholder card: docs/ai/models/mobilesal.md. |
vmaf_tiny_v1 | Superseded by vmaf_tiny_v2; kept for loader back-compat tests. |
vmaf_tiny_v1_medium | Superseded by vmaf_tiny_v2; kept for loader back-compat tests. |
These ids are excluded from doc-coverage checks. Do not promote them to smoke: false without first shipping a model card and the full ADR-0042 bar.
lpips_sq_v1 — name vs card mismatch¶
The registry entry lpips_sq_v1 (smoke: false) points to lpips_sq.onnx. Its model card is at docs/ai/models/lpips_sq.md (without the _v1 suffix). The card covers the same model; the naming divergence is a tracked cosmetic gap (scaffold-audit ADR-0621 P3-5). Until the card is renamed, treat lpips_sq.md as the authoritative card for lpips_sq_v1.