Omatico Provisioning — Instruction Manual Release Distribution · Rev 2026-09-10

From Build to Bench

A station may only flash what the published catalog authorizes for its batch. Publishing a release on GitHub makes it available. A separate catalog publication authorizes its use for batches. Neither action enables fleet OTA updates.

Build & review draft → Publish on GitHub → Register & verify → Authorize for batches → Station installation.

Published assets and release notes live in Omatico-Firmware-Releases ↗. The admin Releases page shows stored verification and current batch selections, with GitHub discovery and notes available on request.

1

The objects

Six things participate in getting firmware onto a device. Three are created ahead of time (batch, spec, station credential), one is created per release (the release record), and two are never created directly at all — the catalog is computed, and the published version is a receipt.

FirmwareRelease

Firmware release record

One row per firmware version, registered by exact version from the public release repository and verified independently from the delivered bytes: the four assets (descriptor, factory payload, MicroPython runtime, OTA archive), their SHA-256 digests, the source commit and CI run, and the baselineBundle the backend derived itself.

Created by register-release --version vX.Y.Z. Nobody types a filename or a digest; the backend downloads, re-hashes, inspects, and derives.

Why — every digest the catalog pins traces to bytes the backend verified. Rows are append-only, unique by version, and never edited; a corrected build is a new version.

published · immutable draft (local export only)
FactoryBuildSpec · "Configuration"

Build spec

One authorized factory configuration, under a code like FBS-MAP-2-IEL-C-002: board codes, firmware version, pinned content digests, test profile, and a reference to the firmware release record it was minted from. It is the unit the catalog authorizes and the record every provision request is validated against.

Created by an admin when a configuration is first authorized — and minted automatically by every publish thereafter.

Why — specs are immutable pins. A new release never edits a spec; it mints a successor and retires the old one once no batch references it, so what was authorized when stays answerable.

active retired
ProvisioningBatch

Batch

A manufacturing authorization: which production run may build devices, how many (quota), and which specs it may use. Identified like MFG-20260701-MAP-C-001.

Created by an Omatico admin before any device work starts. Quota counts unique devices — reflashing a known device never re-consumes a slot.

Why — nothing flashes without an authorization envelope and a ceiling. Suspending a batch is the immediate brake on a line.

open closed suspended
GET /v1/catalog

Catalog

Not a stored document. The catalog is the answer to GET /v1/catalog, computed on every read from the open batches and their active specs, plus the asset-digest allowlist and an expiry.

Created — never. It is derived, so the backend has no separately stored catalog to invalidate. Stations still need to sync and respect its expiry.

Why — what a station sees is, by construction, exactly what the database authorizes. The catalog cannot drift from the tables it is computed from.

derived on read expires 6 h
catalogVersion + CatalogPublication

Published catalog version

"Published" is not a toggle and there is no draft state. A version (e.g. 2026-08-26.1) is stamped on batches by a publish; the served version is the highest among open batches. The publication row is the immutable receipt of that publish: who, why, prior → resulting map, payload hash.

Created inside the publish transaction itself — one row per publish, and each version string can be claimed exactly once.

Why — the receipt commits atomically with the change it describes, so the audit trail can never disagree with the catalog.

Where — every receipt is browsable, read-only, in the admin console under Releases → Publication history (/admin/provisioning/releases/publications): who, why, the prior → resulting map per batch, the payload hash, and the stored pre-commit result.

immutable receipt one per publish
ProvisioningCredential

Station

A bench identity, not a database object of its own: a machine credential bound to a factory and station ID, allow-listed to specific batches, with an expiry that forces re-enrollment.

Created dormant by an admin and activated when the station redeems its enrollment code.

Why — the catalog is scoped per credential. A station is only ever shown the batches it may later provision against.

batch allow-list expires
2

The route: build → catalog → station

The batch, its spec, and the station credential already exist before a release moves (they are the standing authorization). A release then travels the numbered path below. Callouts ① – ⑧ are described in the legend.

FIRMWARE CI BACKEND · RELEASE CONTROL STATION Review draft → publish CI-built assets · review title and notes → immutable GitHub release Register by version → release record re-hash · inspect · derive baselineBundle published + immutable only Select the release by version publish-release --firmware-version Publish — one transaction mint successor spec (fw + baseline ← release) relink batch → new spec retire old spec · spec references the release stamp new catalogVersion write CatalogPublication (receipt) GET /v1/catalog open batches × active specs computed on read · never stored Sync catalog credential-scoped batch list allowed configs + digests Verify & flash re-hash bytes == expected digests flash device firmware Provision — equality checks batch open · spec active + authorized catalogVersion == batch · boards match baselineBundle + firmwareImage == release → shadow: desired.app = spec firmware 1 2 3 4 5 6 7 8
FIG. 1The route a release takes. The batch, spec, and station credential exist before step ① — they are the standing authorization the release flows into. Only step ④ changes what is authorized, and it does so in a single transaction.
  1. Build and review a draft, then publish on GitHub — CI builds the descriptor, factory payload, exact MicroPython runtime and application archive. Review those four assets and replace any fixture title or notes before approving publication. The publication workflow verifies and publishes the existing draft in place; it does not rebuild. Published assets and the tag are permanent, while the title and notes remain editable. Immutability is GitHub's guarantee that the assets can never be swapped after publication.
  2. The backend registers the release by exact version — it resolves the tag anonymously, refuses a draft or a mutable release, downloads the four assets, re-hashes them, inspects the payload, and derives baselineBundle itself from the application, /lib, and product_config digests it recomputed. A transcribed digest cannot reach the catalog: the backend derives the value and refuses a release whose own claim disagrees.
  3. An admin selects the release by versionpublish-release --firmware-version names the registered record, source spec, affected batches, a new catalog version, reason and operator. Filenames and digests are derived automatically. One release model, one registration path, one way to publish.
  4. Publish runs as one transaction — mint the successor spec (firmware version and baseline copied from the release record, which the spec references), relink the affected batches, retire the old spec if no batch references it, stamp the new catalogVersion, write the publication receipt. An invariant gate runs before commit; any failure rolls the whole thing back. A half-applied catalog would be unsafe, so it cannot exist.
  5. /v1/catalog now serves the new pins — recomputed on read, so there is no cache to invalidate; a post-commit confirmation verifies the served state. Pre-commit checks the intended catalog; post-commit checks the one actually served.
  6. The station syncs — its credential scopes the response to the batches on its allow-list, each carrying its allowed configurations and their digests.
  7. The station verifies, then flashes — it re-computes the digests of the bytes it staged and proceeds only when the staged baseline matches the catalog and the assets match the release descriptor. The runtime digest comes from the release assets, not the catalog response.
  8. The station provisions the device — reporting batch, spec code, catalogVersion, and its computed asset digests. The backend re-checks everything (Table 1) against the release record the spec references, reserves one quota slot, creates the device identity, and writes the device shadow's desired.app from the validated spec — the catalog-authorized firmware, resolved fresh and failing closed if it can't be.
3

What one publish changes

Catalog publication preserves release records and existing spec pins. It mints a successor spec that references the selected release record, moves the batch's authorization link, and retires what is no longer referenced — the old spec and every release record remain, inert, for provenance. That is why a "published catalog version" is a receipt rather than a flag: the version exists because this transaction happened.

BEFORE · catalogVersion 2026-08-20.1 AFTER · catalogVersion 2026-08-26.1 PUBLISH Batch MFG-20260701-MAP-C-001 open · quota 40 · v 2026-08-20.1 authorizes Spec FBS-MAP-2-IEL-C-001 · active fw v0.3.3 · pinned digests · boards references release Release v0.4.0 · registered published · immutable · baseline derived Batch MFG-20260701-MAP-C-001 open · quota 40 · v 2026-08-26.1 authorizes × unlinked Spec …-C-002 active · minted fw v0.5.0 ← release Spec …-C-001 retired · kept never edited references Release v0.5.0 registered · immutable Release v0.4.0 still registered · kept written in the same transaction: CatalogPublication catpub_v7Qk3m actor · reason · release v0.5.0 · prior → resulting map
FIG. 2Before and after one publish. The only mutations are the moved authorization link, the batch's new version stamp, and the spec state flip (active → retired). Release records are never changed. Rolling back is simply another publish that selects the earlier registered release under a fresh version — history is never rewritten.
4

The handshake: who is the authority

The catalog names the authorized firmware version and baseline. The station fetches that exact release, checks its descriptor and assets, computes the staged baseline and runtime digests, and reports them when provisioning. The backend compares those reported digests with its independently verified release record. Equality validates the reported values; it does not independently prove that the device was flashed. Station flash and device checks provide that evidence.

Catalog → station

The backend authorizes

The catalog supplies the batch, configuration, firmware version and expected baseline.

The runtime digest stays in the registered release record.

Release assets → station

The station verifies

The station checks the exact release assets and computes the staged baseline and runtime digests before installation.

It reports those computed digests when provisioning.

Provision request → backend: compare the reported digests with the verified release record. Reject any mismatch.

FIG. 3Catalog authorization and release verification. The runtime digest is checked against the registered release on the backend; it is not carried in the catalog response. The configCatalog field is a compatibility echo of the spec's pin, not a hash of the served catalog bytes.
TABLE 1 — CHECKS ENFORCED AT PROVISION TIME
Check A failure means
1 Batch is open, with quota remaining A closed or suspended batch stops the line instantly — suspension is the emergency brake.
2 Spec is active and authorized by that batch Retired specs and specs from other batches are refused, even if the station still has them cached.
3 catalogVersion equals the batch's version A station on a stale sync must re-sync before it may provision.
4 Observed board codes match the spec The physical hardware on the bench must be what the configuration says it is.
5 The spec references a registered firmware release A configuration whose bytes the backend never verified authorizes nothing.
6 baselineBundle digest equals the release's derived baseline The reported baseline differs from the authorized application, /lib, and product_config baseline.
7 firmwareImage digest equals the release's MicroPython runtime The reported runtime digest differs from the runtime verified at registration.
8 configCatalog digest equals the spec's pin The echoed compatibility pin differs from the spec. This field does not verify catalog content bytes.

The station-computed toolBinary digest is recorded for audit but is not yet grounds for rejection.

5

Design rules the flow enforces

Immutable pins

A spec is never edited. Every catalog publication mints a successor and retires the predecessor when no batch references it, so "what was authorized on that date" is always answerable.

One transaction

Spec mint, batch relink, retirement, version stamp, and the publication receipt commit together or not at all. There is no partially published catalog.

Computed on each read

The catalog is computed from the tables on every read. It cannot drift from the database, because it is the database.

Authority inversion

The backend authorizes the release; the station verifies the bytes and reports computed digests. The backend rejects values that disagree with its verified release record.

Rollback is a publish

Rolling back selects an earlier registered release under a fresh version — never by rewriting history. Suspending the batch is the immediate stop.

Derived, never transcribed

baselineBundle is derived by the backend from bytes it re-hashed and compared with the release's own claim. No operator ever types a filename or a digest on the release path.

Terminology follows the admin release & catalog concepts guide. Spec codes, batch IDs, versions, and publication ids shown in the figures are representative examples.