Skip to content

Registering a base model

Registration adds a base model (see Models) to fAIr: it validates a STAC item, publishes it to the base-models collection, and makes the model available for training and prediction.

Registration is admin only and runs asynchronously. Models are contributed to the fAIr-models repository (see Contributing a model); once the pull request is merged, an admin can trigger registration.

What you need

  • An admin account (is_staff = true).
  • A STAC item that describes the model, either inline JSON or a public URL that returns the JSON.
  • Optionally, a category slug that already exists in /api/v1/categories/ (for example buildings); it defaults to other.

The STAC item

The item must be a valid MLM (Machine Learning Model) STAC item. At minimum it needs properties["mlm:name"], which becomes the model id in the catalog, and a source-code asset whose mlm:entrypoint is a pipeline bundled in the backend image (see What registration does). In practice a base model item also carries the MLM fields (framework, architecture, tasks, input/output, hyperparameters) and the fair:* fields fAIr uses (compute requests, preview info, metrics spec).

Two rules to keep in mind:

  • keywords are validated against an allowed list. Use the standard task and domain terms (for example building, semantic-segmentation, polygon). Do not put the category slug in keywords; the category is written separately as the fair:category property during registration.
  • Model weight assets (model, checkpoint) should point to a location the platform can read. Registration mirrors them into the artifact store under a versioned path and publishes the mirrored hrefs.

A complete reference item lives in the fAIr-models repository, for example models/dinov3s_buildings/stac-item.json.

Register

Send exactly one of stac_item (inline JSON) or stac_item_url (a link fetched once at request time). category is optional and defaults to other. inference_endpoint is optional; when given it is written as the mlm:inference-endpoint asset. The Django admin "Add base model" form takes the same fields.

By URL:

curl -X POST https://<host>/api/v1/base-models/ \
  -H "Authorization: Bearer <admin-token>" \
  -H "Content-Type: application/json" \
  -d '{
    "stac_item_url": "https://<host>/collections/base-models/items/<id>",
    "category": "buildings",
    "inference_endpoint": "https://<id>.predict.<host>/predict"
  }'

Inline instead of a URL:

curl -X POST https://<host>/api/v1/base-models/ \
  -H "Authorization: Bearer <admin-token>" \
  -H "Content-Type: application/json" \
  -d '{"stac_item": { ...full STAC item JSON... }, "category": "buildings"}'

The response is 202 Accepted with the DB record and status: "registering".

For a release, first pin a local copy of the item:

fair basemodel pin item.json

Submit that exact file as inline stac_item in staging and, after testing, in production. Do not fetch or pin it again between environments: mutable tags may have moved.

What registration does

  • Writes the chosen category onto the item as fair:category.
  • Pins the mlm:training and mlm:inference image tags to digests.
  • Rejects the item (400) unless mlm:entrypoint is models.<model>.pipeline:<function> and bundled in the backend image. New models need a fair-py-ops release, then a backend release.
  • Adds the mlm:inference-endpoint asset when inference_endpoint is supplied.
  • Mirrors downloadable model assets into a version-specific artifact path.
  • Applies the model's Knative service. With FAIR_KNATIVE_TAG=staging, the new revision is served at https://staging-<model>.predict.<domain> and the live route is unchanged. Without it, the live route moves after the new revision's /health readiness probe succeeds.
  • Publishes the item to the base-models collection off-request, where the version metadata lives.
  • On success sets the DB row status to active and records stac_item_id. On failure sets status to failed and records the error.

The request does not block on publishing. Poll the record until it settles.

A reconcile_knative CronJob re-applies services for all active base models. Releasing services whose model left STAC (--prune) stays off until STAC item listing handles every result page.

Verify

Poll the record:

curl -s "https://<host>/api/v1/base-models/<id>/" \
  -H "Authorization: Bearer <admin-token>" | jq '{name, status, category, stac_item_id, error}'

Fetch the full model with the STAC item inlined:

curl -s "https://<host>/api/v1/base-models/<id>/?expand=stac" | jq '.stac.properties["fair:category"], .stac.assets'

Read the published STAC item directly:

curl -s "https://<stac-host>/collections/base-models/items/<mlm:name>" | jq '.properties, .assets'

Re-registering

The "Re-register selected from STAC" admin action re-publishes an already-published model from its STAC item. A model that never published (no stac_item_id) is registered again through POST /base-models/ or the Django admin add form. Every registration writes the row's is_pinned to the new item's fair:pinned. Registering from the fAIr-models item replaces the title, description and fair:preview set since the last registration.

Optional: feature the model (pin)

Pinning marks a model as featured and can attach a sample imagery layer and a preview location.

curl -X PATCH https://<host>/api/v1/base-models/<id>/pin/ \
  -H "Authorization: Bearer <admin-token>" \
  -H "Content-Type: application/json" \
  -d '{
    "is_pinned": true,
    "preview_imagery": "https://tiles.example/{z}/{x}/{y}.png",
    "preview_location": {"type": "Point", "coordinates": [<lon>, <lat>]}
  }'

This flips is_pinned in the database and, for a published model, writes fair:pinned to the STAC item. preview_imagery and preview_location update the imagery, center and place of the item's fair:preview, which the model must already have. A new preview_location also clears the preview bbox.

Edit the title, description and preview

PATCH /api/v1/base-models/<id>/metadata/ takes any of title, description and fair_preview, and writes them to the STAC item after validating the merged item. It returns 409 while the model has no STAC item. The Django admin change form edits the same three fields, and its "Pin selected" and "Unpin selected" actions behave like the pin endpoint. Its "STAC patch" field takes the same body as PATCH /api/v1/base-models/<id>/stac/ and is written together with those fields after one validation.

Remove a model

curl -X DELETE https://<host>/api/v1/base-models/<id>/ \
  -H "Authorization: Bearer <admin-token>"

This removes the database row, the inference service (when the backend has a Kubernetes configuration), and the STAC item with its archived versions. If removing the STAC item fails after the service is gone, the row is restored and reconcile_knative redeploys the service. It returns 409 while the model is registering or local models are built on it. Deleting from the Django admin does the same.

Check that the database and STAC agree

docker compose exec api python manage.py check_stac_drift

It compares base models, local models and built datasets with their STAC collections. It lists rows without a live STAC item, live STAC items without a row, and model pin flags that differ, and exits non-zero when it finds any.