Concepts
Customers and tenancy
Each organisation using the service is a customer, identified by its domain name,
e.g. example.com.
- Your API token (or console session) decides the customer:
GET /deviceslists the devices of the token's customer. Only the public update check names the customer, in its path (/firmware/example.com/…), because devices call it without a token. - Customers' data is completely separate. Device ids, tags, firmware and log-file
names only have to be unique within one customer. Two customers can each have a device
called
SN-000123. - An API token belongs to exactly one customer. Asking for another customer
(
?customer=other.example, which only the service's administrators may use) gives404 customer not found, the same answer as for a domain that doesn't exist. The service never reveals whether another customer exists. - Separation is enforced twice: every query is filtered by customer, and the database itself enforces row-level security.
Firmware identity and what strings
A firmware binary contains one or more what strings: NUL-terminated text that starts
with @(#) (the Unix what(1) convention). The service reads these fields from them:
| Field | Meaning | Example |
|---|---|---|
fw_version |
firmware version (see ordering below) | 1.6.0-10-g30d0049, v1.5.1.2 |
fw_name |
firmware product name | demo |
hw_name |
hardware product name | example-hw |
hw_version |
hardware version | 2.0.0 |
build_date |
build timestamp (RFC 3339) | 2025-12-05T15:25:18-05:00 |
fw_type |
AFI application, MFI manufacturing, EFI engineering image |
EFI |
These what-string layouts are recognised:
| Format id | Layout | Example |
|---|---|---|
v1-3field |
fw-version, date, fw-type |
1.4.1-10-g30d0049, 2025-07-14T22:25:07Z, AFI |
v1b-3field |
same, with v prefix or 4-part version |
v1.5.1.2, 2025-12-01T17:11:31-05:00, AFI |
v2-5field |
fw-version, fw-name, hw-name@hw-version, date, fw-type |
1.6.0-10-g30d0049, demo, example-hw@2.0.0, 2025-12-05T15:25:18-05:00, EFI |
v2-4field-nohw |
fw-version, fw-name, date, fw-type |
2.0.0, demo, 2025-12-05 10:00:00, MFI |
v2-4field-noname |
fw-version, hw-name@hw-version, date, fw-type |
2.0.0, board@1.2, 2025-12-05T10:00:00Z, AFI |
If the what string doesn't contain a field, you supply it in the upload URL or as a form/query parameter. See Firmware → Resolving fields.
A firmware's unique identity within a customer is
(fw_name, hw_name, hw_version, version, fw_type), where versions that compare equal are
the same version: 0.3.2, v0.3.2 and 0.3.2-0-gfac16cc are one identity, so once one of
them is uploaded the others are refused (409). Uploading identical bytes again is
harmless. Uploading different bytes under the same identity is refused (409): builds
are write-once and are never deleted — release a new version instead. A withdrawn
build keeps its identity, so its version can never be uploaded again.
Version ordering
Versions are compared with these rules (applied the same way to uploaded versions and to the version a device reports):
- A leading
v/Vis ignored;+buildmetadata is ignored. - The numeric core is
X.Y.ZorX.Y.Z.W. The 4th number counts:1.5.1.2is later than1.5.1, and a missing 4th number is0(1.5.1 == 1.5.1.0).X.Yalone is not a valid version. - A trailing
-N-gHASHis a git-describe suffix: N commits after the version. So1.4.1-10-g30d0049is later than1.4.1, and-11-g…is later than-10-g…. The hash never affects ordering, and-0-gHASH(a build of the tagged commit itself) is the same version:0.3.2-0-gfac16cc == 0.3.2, so a device reporting0.3.2is not offered that build, and only one of them can be uploaded. Builds with the same commit count but different hashes (1.4.1-10-g30d0049,1.4.1-10-gffffff0) are distinct uploads that compare equal. - Any other
-suffixis a semver pre-release, which comes before its release:1.4.2-rc.1 < 1.4.2. Pre-release identifiers compare per semver (numeric < alphabetic,rc.1 < rc.2).
The complete ordering, lowest first:
0.0.0 < 1.4.1 < 1.4.1-10-g30d0049 < 1.4.1-11-gabcdef0 < 1.4.2-alpha < 1.4.2-rc.1
< 1.4.2-rc.1-3-gabc1234 < 1.4.2-rc.2 < 1.4.2 < 1.5.1 < 1.5.1.2 < v1.5.1.3
< 1.6.0-10-g30d0049 < 1.7.0
v1.5.1 == 1.5.1 == 1.5.1.0 1.4.1-10-g30d0049 == 1.4.1-10-gffffff0 (same rank)
The canonical form, returned as version in API responses, removes the v, a zero 4th
number and +build, and lower-cases the hash: v1.5.1.2 → 1.5.1.2,
1.4.1-10-g30D0049 → 1.4.1-10-g30d0049.
Devices should report their full version (the complete git-describe string) in the update check. A device running
1.4.1-10-g30d0049that reports only1.4.1will be offered1.4.1-10-g30d0049again, because that is later than1.4.1.
Two builds can rank equal, e.g. 1.4.1-10-g… built from two different branches. Both
are stored as separate firmware. When both appear in an update-check result, they are
ordered by build date.
General release, tags and staged builds
Every firmware has a tag set (initially empty) and a tag_filter_disabled flag
(initially false). Together they decide who sees the build in update checks:
tag_filter_disabled |
Tags | Who is offered this build |
|---|---|---|
true |
any | everyone (general release) |
false |
beta, qa |
only devices that have at least one of beta or qa |
false |
none | nobody (staged; this is the state of every new upload) |
Devices have tags too. The update check for a given hwid (the last path segment of the update-check URL) returns the union of general releases and builds whose tags overlap the device's tags. Without a hwid, or with an unknown or withdrawn device's hwid, only general releases are returned.
Tags are one namespace per customer shared by devices and firmware (beta on a device
and beta on firmware are the same tag). Tag names are 1–64 characters of letters,
digits, ., _, : and -, starting with a letter or digit, and are case-insensitive.
To pull a bad build, set status to withdrawn. It disappears from update checks and its
download links stop working, but it stays in your account (and can be reactivated). A
build's tags, general-release flag and status are its release settings — the only
things about a build you can change.
Firmware types and the AFI default
Update checks return AFI (application) images only unless you ask for more with
?type=all or ?type=EFI, ?type=AFI,MFI, etc. This stops a device in the field from
being offered an engineering or manufacturing image by accident.
Devices and keys
A device record holds a hwid, tags, free-text notes and two optional Ed25519 public
keys.
The hwid is free-form: 1–64 printable ASCII characters without spaces or control
characters, e.g. SN-000123, a1b2c3d4 or bench/unit-7. It is case-sensitive
(abc and ABC are different devices) and unique per customer. In URLs it is one path
segment, so percent-encode reserved characters (bench/unit-7 → bench%2Funit-7).
Anything else is rejected with 400.
Devices are never deleted. Set status to withdrawn to take one out of service: it is
no longer offered tag-targeted builds, and it can't be updated, re-created or re-tagged
until you set status back to active.
The keys are:
identity_pubkey: so the server can later verify the device's identity by challenge–response. Verification is not enforced yet, so in update checks a hwid is an identifier, not a secret. Tag-restricted firmware is therefore "not advertised" to other devices rather than access-controlled. The protocol is designed to be added without changing these URLs.session_pubkey: for establishing secure device ↔ server sessions.
Once a key is set, replacing it with a different key requires ?force=true. This protects
you from accidentally re-provisioning a device.
Log data
Log files are stored as opaque bytes under a 512-bit name written as 128 hex characters
(upper case is accepted and stored lower case). Uploading identical bytes under the same
name is fine. Different bytes are refused (409): log files are write-once, like a
content-addressed store, and are never deleted.
Download links
The service never gives out permanent file URLs. Every download goes through a link of
the form https://fw.unitcircle.ca/dl/<signed-token>:
- it expires 5 minutes after it was issued;
- following it counts the download and then redirects (
302) to a 60-second link on the object store (or streams the file directly on single-server installs); - an expired or tampered link returns
410 Gone.
See Download links.
Roles and scopes
People log in to the web console (by email, invite only) and have a role in each
customer: viewer, editor or owner. Programs use API tokens, which carry a list of
scopes, such as fw:read or logdata:write. A person can only create tokens with
scopes their own role allows. See Authentication.