Skip to main content

The update protocol

Agent packages and custom firmware fetch updates with two HTTPS calls: one to ask whether there is anything, one to download it. Any HTTP client can implement it, in any language, on any stack. It is what Connhex Edge does under the hood, and what you write yourself on a microcontroller or your own stack.

System image updates work differently. SWUpdate and RAUC speak the hawkBit DDI protocol on an endpoint of their own, and nothing on this page applies to them.

Enterprise plan

On an Enterprise instance, replace connhex.com with your domain.

Where to poll

A device that enrolled polls over mutual TLS with the certificate it already holds. The certificate identifies the device, so there is no id in the path and no credential in a header:

HEAD /things/ota HTTP/1.1
Host: edge.connhex.com
Version: 1.4.0

Use this endpoint for native OTA updates. The same certificate authenticates the device to the broker, configuration API, and update endpoint.

The Version header is how the device says what it is running. Send valid semver. Without it, the server has nothing to compare against and will offer the current release even if the device is already on it.

Checking for an update

curl -I --cert /data/connhex/client.crt \
--key /data/connhex/device.key \
-H "Version: 1.4.0" \
https://edge.connhex.com/things/ota

The client certificate identifies the device. curl uses the normal system CA bundle to verify the Connhex endpoint.

ResponseMeaning
200There is an update. The headers describe it
404Nothing for this device. Already current, or nothing has been published for it

A 200 carries:

HeaderMeaning
VersionThe version being offered
Content-LengthSize in bytes
Content-Digestsha256=<hex> of the file
Content-DispositionThe file name
Min-Version, Max-VersionPresent when the release sets them
ETagThe offered version, quoted
Accept-Rangesbytes

Read everything you need from this response. It is the only place the metadata appears.

Downloading

curl --cert /data/connhex/client.crt \
--key /data/connhex/device.key \
-H "Version: 1.4.0" \
-o firmware.bin \
https://edge.connhex.com/things/ota

GET on the same URL returns the file. Add Range to fetch it in pieces:

curl --cert /data/connhex/client.crt \
--key /data/connhex/device.key \
-H "Range: bytes=0-1023" \
-o part1.bin \
https://edge.connhex.com/things/ota

Ranges are zero-based and the end is inclusive. Omitting the end still needs the dash, as in bytes=524288-.

ResponseMeaning
200The whole file
206The requested range
416The range is out of bounds
404Nothing for this device

Resuming

To continue an interrupted download, send If-Range with the ETag you got earlier alongside your Range:

curl --cert /data/connhex/client.crt \
--key /data/connhex/device.key \
-H "Range: bytes=524288-" \
-H 'If-Range: "1.5.0"' \
-o rest.bin \
https://edge.connhex.com/things/ota

If the offered release is still the same, you get 206 and the remaining bytes. If it changed while you were away, you get 200 and the whole new file.

Verifying

Check the file against Content-Digest before installing anything:

sha256sum firmware.bin
A complete check-download-verify
#!/bin/sh
set -e

URL="https://edge.connhex.com/things/ota"
CERT="/data/connhex/client.crt"
KEY="/data/connhex/device.key"

digest=$(curl -sS --cert "$CERT" --key "$KEY" -D - -o firmware.bin \
-H "Version: $CURRENT" "$URL" \
| awk 'tolower($1) == "content-digest:" {print $2}' | tr -d '\r')

case "$digest" in
sha256=*) expected=${digest#sha256=} ;;
*) echo "unexpected digest format: $digest" >&2; exit 1 ;;
esac

actual=$(sha256sum firmware.bin | cut -d' ' -f1)
[ "$expected" = "$actual" ] || { echo "checksum mismatch" >&2; exit 1; }

echo "verified $expected"

How progress is reported

The version your device sends on its next poll is what tells Connhex the update worked. A device that comes back on the target version is recorded as updated, one that comes back on the old version is offered the update again, and after long enough without progress the attempt is recorded as failed.

That makes the Version header the one thing worth getting right. It is your device's only voice in the campaign monitor.

Polling cadence

Poll every 15 minutes or more, plus a check whenever the device reconnects. Spread a large fleet with a little jitter, so a site coming back after a power cut does not arrive all at once. Sustained polling beyond a modest burst is throttled, so back off when a request is refused instead of retrying immediately.