Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

DDI API Reference

The device-facing API, under /{tenant}/controller/v1/{controllerId}. Requests are authenticated by target token, gateway token, or anonymous mode — see Authentication.

Configure clients with tenant DEFAULT. raptor answers to exactly one tenant, set by the tenant config key (default DEFAULT, matched case-insensitively). A device configured with any other tenant — Zephyr’s CONFIG_HAWKBIT_TENANT in particular — gets 404 on every DDI request. If your fleet is genuinely configured with a non-default tenant name, set tenant to match rather than reconfiguring every device.

Response JSON matches the hawkBit DDI v1 schemas field-for-field.

Handlers live under raptor/src/api/ddi/, one module per resource: poll root in root.rs, deployment/installed base in deployment.rs, feedback/cancel in feedback.rs, confirmation flow in confirmation.rs, and artifacts in artifacts.rs, all wired together in mod.rs.

Endpoints

MethodPath (under /{tenant}/controller/v1/{cid})Description
GET/poll root: config.polling.sleep + _links
PUT/configDatareport device attributes (merge / replace / remove)
GET/deploymentBase/{actionId}the deployment to install
POST/deploymentBase/{actionId}/feedbackdeployment progress/result
GET/confirmationBase/{actionId}pending deployment awaiting confirmation
POST/confirmationBase/{actionId}/feedbackconfirm / deny
POST/confirmationBase/activateAutoConfirmdevice enables auto-confirm
POST/confirmationBase/deactivateAutoConfirmdevice disables auto-confirm
GET/cancelAction/{actionId}cancellation to acknowledge
POST/cancelAction/{actionId}/feedbackconfirm cancellation
GET/installedBase/{actionId}last successfully installed deployment
GET/softwaremodules/{moduleId}/artifactsartifact list for a module
GET/softwaremodules/{moduleId}/artifacts/{filename}artifact download (HTTP Range)
GET/softwaremodules/{moduleId}/artifacts/{filename}.MD5SUMmd5sum-file

Poll root

{
  "config": { "polling": { "sleep": "00:05:00" } },
  "_links": {
    "configData":       { "href": ".../configData" },
    "deploymentBase":   { "href": ".../deploymentBase/7" }
  }
}

config.polling.sleep is not always the configured default: [ddi] polling_interval can carry RSQL-matched override rules (hawkBit 0.10’s pollingTime grammar) that give a slice of the fleet — or one device during an incident — a different interval, with optional jitter. See Polling time overrides in the configuration reference.

Which _links appear depends on the target’s state: deploymentBase when an action is running, confirmationBase when it’s wait_for_confirmation, cancelAction when it’s canceling, and installedBase once something has been installed.

configData appears only while raptor actually wants attributes — a freshly registered target, or one an operator has re-armed with requestAttributes (see Management API). It disappears as soon as a configData PUT arrives. This matters because some clients (the Zephyr hawkbit client among them) re-upload their entire attribute set on every poll that carries the link, which is wasted uplink on a large or cellular fleet.

deploymentBase

{
  "id": "7",
  "deployment": {
    "download": "forced",
    "update": "forced",
    "chunks": [
      { "part": "os", "version": "1.0", "name": "rootfs",
        "artifacts": [
          { "filename": "rootfs.img", "size": 12345,
            "hashes": { "sha1": "...", "md5": "...", "sha256": "..." },
            "_links": { "download-http": {"href": "..."},
                        "md5sum-http":  {"href": "..."} } }
        ],
        "metadata": [ { "key": "signature", "value": "sig-1" } ] }
    ]
  },
  "actionHistory": { "status": "RUNNING", "messages": [] }
}

download/update follow the action’s type, matching hawkBit’s calculateDownloadType/calculateUpdateType:

Action typedownloadupdate
forcedforcedforced
softattemptattempt
timeforced, before forcetimeattemptattempt
timeforced, after forcetimeforcedforced
downloadonlyforcedskip

The modes are computed per request, so a timeforced action flips on its own once the deadline passes. See Assignments & Actions for the operator side. The confirmationBase response is identical but keyed confirmation instead of deployment.

maintenanceWindow

An action assigned with a maintenance window adds one more field inside deployment:

"deployment": { "download": "forced", "update": "skip",
                "maintenanceWindow": "unavailable", "chunks": [] }
  • "unavailable" — the window is shut. update is forced to skip whatever the action type says, so the device downloads and waits.
  • "available" — the window is open. update is the action’s real mode.

The key is omitted entirely for an action without a window, so payloads for ordinary assignments are unchanged. Like the modes above it is computed per request, so a device polling across the window boundary sees it flip with no server-side scheduling involved. installedBase replays a finished action and never carries the field.

A chunk’s metadata array carries any software-module metadata marked targetVisible (see the Management API). The key is omitted entirely when a module has no visible metadata.

Feedback

{ "status": { "execution": "closed", "result": { "finished": "success" } } }
  • execution ∈ proceeding, scheduled, resumed, downloading, downloaded, canceled, rejected, closed.
  • result.finished ∈ none, success, failure.
  • result.progress ({"cnt": 3, "of": 10}) is optional. raptor accepts it, shows it live in the console and on the events stream, and does not persist it. A malformed value is ignored; it never fails the feedback.

closed is terminal for every action type. For a downloadonly action downloaded is also terminal — that is the whole job, so the action closes with detailStatus: downloaded and the target’s installed DS is left unchanged. For all other types downloaded is recorded as progress only. Posting feedback to a non-active action returns 410 Gone.

Confirmation feedback uses a different body:

{ "confirmation": "confirmed", "details": ["…"] }   // or "denied"

configData

{ "mode": "merge", "data": { "hw": "rev2", "os": "linux" } }

mode ∈ merge (default; upsert keys), replace (drop all, then set), remove (delete the listed keys). Extra legacy fields in the body are ignored.

Artifact download & Range

The artifact download endpoint honors HTTP Range (RFC 9110 §14) so an interrupted download resumes with a 206 Partial Content response rather than restarting.

  • bytes=a-b, bytes=a- and suffix bytes=-n (the last n bytes) are served as a single part with Content-Range.
  • Several comma-separated ranges are answered with one 206 of type multipart/byteranges, one part per range, each with its own Content-Range. This is what SWUpdate’s delta handler sends (up to max-ranges per request, default 10) when it fetches zchunk chunks from a .zck artifact.
  • Parts come back in the order they were requested. A range that overlaps or directly follows the one before it is merged into it; if that leaves a single range, the response is a plain single-part 206.
  • If no range overlaps the artifact, the response is 416 with Content-Range: bytes */<size>.
  • A malformed header, a unit other than bytes, a request needing more than 64 parts after merging, or ranges that add up to more than the artifact’s size (overlaps) are ignored, and the whole artifact is sent with 200.

download-http vs download

Each artifact carries up to two link families, following hawkBit’s convention: download-http / md5sum-http are the plain-HTTP URLs, download / md5sum the HTTPS ones. Clients pick one and take the scheme from the href itself — the Zephyr hawkbit client uses download-http.

raptor only advertises a genuinely different plain-HTTP URL when you have told it one is reachable, via [ddi] artifact_http_url:

url = "https://ota.example.com"
[ddi]
artifact_http_url = "http://dl.example.com:8088"
Configdownload-httpdownload
url http, no artifact_http_urlthe url (http)(absent)
url https, no artifact_http_urlthe url (https)the url (https)
url https + artifact_http_urlartifact_http_url (http)the url (https)

The middle row is why download-http can carry an https:// href: on a TLS-only deployment there is no plain-HTTP port to point at, and emitting one anyway would break every client that follows the link. Set artifact_http_url when you want device downloads to bypass TLS.