Skip to content

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.