uclogserver
Log in

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 /devices lists 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) gives 404 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):

  1. A leading v/V is ignored; +build metadata is ignored.
  2. The numeric core is X.Y.Z or X.Y.Z.W. The 4th number counts: 1.5.1.2 is later than 1.5.1, and a missing 4th number is 0 (1.5.1 == 1.5.1.0). X.Y alone is not a valid version.
  3. A trailing -N-gHASH is a git-describe suffix: N commits after the version. So 1.4.1-10-g30d0049 is later than 1.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 reporting 0.3.2 is 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.
  4. Any other -suffix is 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-g30d0049 that reports only 1.4.1 will be offered 1.4.1-10-g30d0049 again, because that is later than 1.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.

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.