Firmware artifacts¶
A firmware artifact is an immutable bundle of:
- The binary (
firmware.bin): stored in object storage. - The manifest: declares what hardware this binary fits.
- A SHA-256 of the binary: devices verify after download.
- A project-scoped build number: assigned at upload time, never reused, never gap-filled.
Artifacts are immutable. Re-upload = new artifact = new build
number. There is no "edit this artifact" operation.
Storage¶
Binaries live in object storage managed by SimpleOTA. Devices are only ever handed a pre-signed download URL with a short expiry (default 5 minutes); the binary bytes never flow through the SimpleOTA API servers.
Manifest¶
Required for Arduino, optional for ESP-IDF.
A minimal Arduino manifest:
{
"framework": "arduino",
"chip_family": "esp32",
"board_id": "esp32dev",
"partition_profile": "default_4mb",
"nvs_schema_version": 1,
"security_mode": "basic"
}
See Manifest schema for the full reference.
Where do artifacts come from?¶
- CI/CD: typical. Tag a release in your firmware repo, GitHub Actions / GitLab CI builds and uploads. See GitHub Actions.
- Dashboard upload: fine for one-offs and internal tools.
- Developer API direct: for custom build pipelines.
Lifetime¶
A ready artifact is permanent. There is no delete, in the dashboard or the
API, so the build history of a project only grows. Deleting the project is
what removes them, along with their binaries in object storage.
Abandoned uploads are the exception. An artifact row is reserved before the
binary reaches object storage, so an upload that dies midway leaves a
pending row behind, and one that errors leaves a failed one. An hourly
janitor reaps both after a TTL.
That distinction matters for your monthly quota: pending rows count against
it, because a reservation still uploading is holding a slot, while failed
rows do not, so a transient upload error costs you nothing. See Plans and
limits.