rpi-sb-provisioner (2.3.4) unstable; urgency=medium

  * Refresh the fastboot gadget images against rpi-fastbootd
    14.0.0~git20260828.5ae7d5d. All five artefacts are rebuilt -- the
    generic gadget, the three per-family images and the pre-Pi4 bootfiles
    tarball -- so a station picks up the new daemon whichever family it is
    pinned to. The per-family images are the ones get_fastboot_gadget()
    actually serves once RPI_DEVICE_FAMILY is set, so refreshing only the
    generic image would have left every pinned station on the previous
    daemon.

  * Pick up the fastbootd fix for a committed partition table the kernel
    never adopted. oem partinit and oem partapp wrote the table and reported
    OKAY, but nothing asked the kernel to re-read it, so /dev/<dev>pN kept
    the previous layout's offsets and sizes and the next command acted on
    the old geometry. On a 32GB card partitioned as 512MiB boot plus
    remainder, oem cryptinit LUKS-formatted a stale 128MiB p2;
    partition-size:mapper/cryptroot then reported 112MiB, this package sized
    the rootfs filesystem to match, and mke2fs ran out of blocks part way
    through populating it. A partition still held open elsewhere -- a stale
    device-mapper node, a mount -- now fails the command rather than leaving
    the old table in place.

  * Pick up the fastbootd change to report the wired MAC the firmware
    settled on, read from the device tree's ethernet node before falling
    back to OTP. Where the MAC rows were never programmed the bootloader's
    choice, not the blank OTP, is the address the board presents on the
    network. Unprogrammed rows now read as an empty string rather than
    00:00:00:00:00:00, so mac-wifi and mac-bt on a part without them are no
    longer answered with a MAC-shaped value that downstream consumers would
    record as real.

  * Stop shipping fastboot-gadget-pi3-family.img, which nothing ever read.
    get_fastboot_gadget() is reached only from branches guarded by
    ALLOW_SIGNED_BOOT, which is set for device families 2712 and 2711 and
    cleared for 2710 and 2764, so the only slugs that can reach it are
    pi5-family and pi4-family. A Pi 3-family board takes
    fastboot-gadget.2710-bootfiles-bin from the !ALLOW_SIGNED_BOOT path
    instead and never asks for a boot.img at all. The image was 30MB of
    payload in every copy of this package. The pi3-family pi-gen-micro build
    is still needed -- it is where the 2710 bootfiles tarball comes from --
    so only the unused boot.img goes. An existing installation has the file
    removed on upgrade, since it is no longer in the package's file list.

 -- Tom Dewey <tom.dewey@raspberrypi.com>  Fri, 28 Aug 2026 14:49:08 +0100

rpi-sb-provisioner (2.3.3) unstable; urgency=medium

  * Fix the permanent-failure marker never reaching the provision-failed
    hook from the signing paths. mark_permanent_failure() recorded its state
    in a shell variable, but the two callers that most need it --
    get_openssl_sign_args() and get_sign_bootcode_key_args() -- are invoked
    as `$(...)`, and a variable set inside a command substitution dies with
    that subshell. A station with no signing key configured therefore
    reported PROVISION_FAILED_PERMANENT=0, so a hook filtering transient
    bootstrap-phase failures discarded it as churn and left the head on its
    in-progress indicator indefinitely -- exactly the case 2.3.2 set out to
    fix. The marker is now a file keyed on the invoking shell's PID, which
    subshells inherit unchanged, so it survives the capture while keeping
    concurrent devices independent. It is cleared once the outcome has been
    reported, so a later run reusing the PID cannot inherit it.

  * Stop the UI service aborting at startup when it cannot establish its own
    package version. The GitHub release check parsed that version with
    std::stoi, which throws on a component holding no leading digits, and
    nothing on the path catches it. getPackageVersion() falls back to
    "unknown" whenever dpkg-query cannot answer -- which is any binary run
    from a build tree rather than an installed package -- so the service
    terminated during startup, before it bound its listener, whenever GitHub
    was reachable enough to return a release tag to compare against. An
    unreachable GitHub returns early and never reaches the comparison, which
    is why this went unnoticed on stations without network access. A version
    that will not parse is now treated as "no update known"; Debian revisions
    and pre-release suffixes order exactly as before, so non-native builds
    keep receiving update notifications.

  * Make the device cards the default view on the Devices page, and move
    them to the left of the view selector. The topology map led, so an
    operator watching a flash had to switch to the cards on every visit --
    and the tab the page remembered was written on every page load,
    including the one that had merely applied the old default, so a browser
    that had never expressed a preference still had one recorded. Only an
    explicit click is remembered now, under a new storage key, and the graph
    refits itself when its tab is revealed rather than laying out against a
    hidden container. Reported by Patrick Groothuizen (#345).

  * Show what a device is actually doing on its card, rather than only which
    of the four stages it has reached. Each card now carries a
    plain-language activity line -- "Writing root filesystem", "Updating
    bootloader EEPROM" -- with a live counter for how long that step has
    been running, so a stalled device is visible without opening its log.
    Where few enough devices are connected for it to fit, the card also
    lists that device's recent state transitions with timestamps, which is
    the per-device form of the state-transition table earlier releases
    showed. Those rows are keyed by USB path, so the list stops at the end
    of the previous board's run rather than presenting it as this one's. The
    wired MAC is shown alongside the serial once the provisioner has read it
    from the device, so a finished module can be identified without
    cross-referencing.

  * Report provisioning progress in much finer detail. The provisioners
    recorded three states per stage -- started, finished, aborted -- so
    everything between them was invisible: a device spent minutes on
    IMAGE-PREPARING or WRITING-ROOTFS indistinguishably from one that had
    hung. Every stage now records the step it is entering, via a
    record_progress() helper that keeps the stage name on the front of each
    state, so the UI places a step it has never seen in the right stage
    without needing to know it. That covers image preparation, signing,
    storage erase and partitioning, each partition write (including one
    state per partition in the IDP flash loop), and finalisation; the
    customisation hooks report themselves too, so an operator script that
    blocks is no longer mistaken for the provisioner stalling; and a device
    queued behind another head's shared image build now says so instead of
    appearing to sit still.

  * Make the Devices page ignore state.db rows written before the service
    started, as it was always meant to. The freshness filter bound the start
    time as an integer against a text timestamp column, and SQLite sorts
    every integer before any text, so the comparison was true for every row
    ever recorded -- a freshly connected board inherited whatever state the
    previous occupant of that USB port had finished in. Consequently a
    device left connected across a service restart now shows no state until
    it next changes, which is the honest answer: the service cannot know the
    board still in the socket is the one those rows describe.

  * Stop offering boot package generation on stations with no signing key.
    Boot packages are signed, so without key material the button started
    rpi-sb-image-bootimg-generator.sh, which logs "No signing key
    configured", exits 0, and produces nothing -- leaving the page polling
    for a package that would never appear and reporting the result as
    "Not generated", the same wording as an image nobody had asked to
    build. The Options page now reads the configured signing material from
    a new GET /options/signing-status, classified exactly as
    init_signing_context() classifies it, and withholds the control when
    there is nothing to sign with; the Boot Package row says which of the
    reasons applies -- no key, a PEM key naming a file that is not there,
    or both a PEM and a PKCS#11 key configured at once, which the scripts
    refuse to choose between -- and links to the signing key settings where
    those are on screen. The status is re-read whenever a key is added,
    activated or removed, so it follows the configuration without a reload.
    /generate-boot-package refuses the request with 409 NO_SIGNING_KEY
    rather than relying on the button being absent. The PKCS#11 token is
    not opened to answer this, so a configured URI still reports a key
    present while its token is unplugged.

  * Check for a usable device key where secrets are stored, not at install
    time. Uploading a signing key to a station whose OTP key slot had never
    been programmed failed with a bare HTTP 500, the reason ("Failed to
    device-wrap uploaded key") reaching only the journal. The 2.3.x
    bootstrap that was supposed to prevent this ran solely from postinst,
    which for any host built from an image executes in the build chroot
    where the firmware crypto service is unreachable -- so it reported
    "firmware crypto service unavailable; skipping key setup", exited 0,
    and never ran again on the hardware. A host installed straight from a
    build tree never ran it at all.

    The service now classifies the device key at startup and again at every
    point it is asked to store a secret -- the PIN, an uploaded PEM key,
    and the plaintext migration path -- so the check cannot be bypassed by
    how the software arrived. The probe ends in an actual HMAC rather than
    a public key read, because a slot that has never been programmed
    answers for its status while holding all zeros, which is exactly the
    case being detected. GET /options/device-key-status reports the
    classification and the Options page explains it in place of the
    signing key controls; the paths that store secrets refuse with 409
    DEVICE_KEY_BLANK or DEVICE_KEY_UNAVAILABLE, naming the remedy where
    there is one.

  * Stop generating a device key during package installation. The previous
    bootstrap did this on the premise that the key would land in a spare,
    non-DEVICE slot and so cost nothing. That is not true on BCM2712: there
    is exactly one key slot, id 1, and it is the device-unique slot, so
    what the postinst performed was an irreversible OTP write as a silent
    side effect of apt. It now only reports. Where the slot is blank the
    Options page offers to generate one through a new POST
    /options/provision-device-key, which requires an explicit confirmation
    token in the body and is refused for any state other than blank -- a
    host that already has a key returns idempotent success with no write
    attempted. Note that config.txt's lock_device_private_key=1 sets
    READ_LOCKED, which does not prevent generation; that is gated by the
    separate GEN_LOCKED flag, so a locked-but-blank slot can still be
    provisioned.

  * Record why the device refused a fastboot command during IDP
    provisioning. fastboot reports a device-side refusal as FAILED (remote:
    '...'), but timeout_fatal_secs ran each command bare, so that line
    reached the journal while provisioner.log -- the per-device log the
    Devices page renders -- recorded only that a command had started.
    die() echoes rather than logs, so not even the exit code landed there.
    The output is now captured as well as streamed, the remote message
    extracted and logged, and carried into the failure message, so the
    recorded failure names its cause. Where there is no remote message the
    last lines of output are logged instead. Streaming is unchanged, so
    flash progress still appears as it happens.

  * Validate IDP image descriptions against the schema rpi-image-gen
    publishes, rather than a hand-written approximation of it. The embedded
    kImageJsonSchema required only IGversion and layout -- IGmeta,
    attributes, partitiontable and provisionmap were all optional, and
    image-size and image-palign-bytes accepted any type at all -- so a
    description missing every block the device needs was accepted at
    upload and refused on the bench as "IDP:invalid description". The
    schemas are now generated at configure time from a pinned
    rpi-image-gen (RPI_IMAGE_GEN_TAG, v2.7.0), so an update to them
    arrives as a reviewable diff instead of a failed provisioning run.

  * Check the parts of a description a JSON schema cannot express, matching
    what the device checks: image-palign-bytes must read as a genimage size
    and be a whole number of mebibytes (no schema can tell 1M from 1MB),
    IGversion must name major version 2, and every partition reference in
    the provisionmap must name an entry in partitionimages. Verified
    against a real rpi-image-gen artefact, which validates unchanged.

  * Select the fastboot gadget for the connected device's family. A station
    may now carry one gadget per family -- fastboot-gadget-pi5-family.img
    and so on, built by pi-gen-micro's matching target -- and the gadget is
    chosen at stage time from the rpiboot USB product ID, falling back to
    the configured RPI_DEVICE_FAMILY. This matters because the gadget is
    signed, cached and then pushed over USB to every board, so anything in
    it the board cannot load is paid for per device: an all-devices gadget
    is ~35MB against ~27MB for pi5-family, and none of the difference is
    loadable on a BCM2712 part, which takes its firmware from EEPROM.
    Backward compatible -- an unsuffixed image under /etc still overrides
    everything, and a station carrying only the unsuffixed image behaves
    exactly as before. Three byte-identical copies of get_fastboot_gadget()
    are collapsed into rpi-sb-common.sh; two of them were never called.

  * Record an abort when a provisioning stage dies without reaching die().
    die() writes the ABORTED state, but a `set -e` death exits at the
    failing command and never reaches it, and cleanup() -- trapped on EXIT
    in all six scripts -- ran the provision-failed hook without writing any
    state. Any unguarded command that failed therefore left the device
    recorded in whatever transitional state it died in, which is what the
    Devices page shows, so a dead run looked like a running one
    indefinitely. Observed with a truncated OS image, where a failed mount
    left SB-PROVISIONER-IMAGE-PREPARING as the last word on the device. A
    new record_abort_once() writes ${STATE_PREFIX}-ABORTED from every
    cleanup(), guarded so die() and cleanup cannot record it twice. Triage
    keeps its existing suppression: a triage that loses a race with
    bootstrap during USB re-enumeration stays silent, as it did before.

  * Name the reason on every path that stores a secret, not just the PEM
    upload. The device-key classification described above reached
    /options/upload-key, but the HSM PIN, the wrap-a-saved-key action
    and the plaintext migration all flattened the same failure into a
    bool on the way out of utils, so each answered with a flat 500 --
    "Failed to save PIN securely", "Could not encrypt key at rest on
    this device", "One or more secrets could not be wrapped" -- with the
    cause reaching only the journal. An operator configuring an HSM on a
    jig whose OTP slot had never been programmed was told to suspect the
    PIN, or the secrets, rather than the station. All three now classify
    before they write and refuse with the same 409 DEVICE_KEY_BLANK or
    DEVICE_KEY_UNAVAILABLE the upload path uses.

  * Carry the remedy as far as the operator. It travels separately from
    the reason, as the error response's details field, and the Options
    page read only the message -- so the half of the answer saying what
    to do about a blank device key was discarded before it was
    displayed. The PIN controls read errorData.message, a field
    createErrorResponse never sets (it emits error), so those two
    replaced every server-side reason with a generic fallback outright.

  * Stop a device-key classification sticking for the life of the service.
    deviceKeyStatus() cached its answer permanently, so a station whose
    slot was programmed after the UI started -- by Raspberry Pi Connect
    registering, or genkey from the command line -- kept reporting no
    device key from every secret path and from the status endpoint behind
    the Options banner until it was restarted, with no way to re-probe.
    Failures are now cached for five seconds. A usable key is still not
    re-probed on a timer, since it sits on the hot path of every wrap and
    unwrap, but neither is it trusted indefinitely: HMAC_LOCKED is
    settable at runtime and persists until reboot, so a slot that refuses
    an operation now invalidates the classification that promised it
    would not, at the point of failure rather than at the next poll.

  * Name the device the firmware crypto service actually uses. The remedy
    offered for an unreachable service said to check that /dev/vcio
    exists. librpifwcrypto opens /dev/vcio_crypto, a different node with
    different ownership, so an operator following that advice would find
    /dev/vcio present and correctly permissioned and conclude the host was
    fine.

 -- Tom Dewey <tom.dewey@raspberrypi.com>  Tue, 18 Aug 2026 16:55:56 +0100

rpi-sb-provisioner (2.3.2) unstable; urgency=medium

  * Final 2.3.2 release:
    - Roll up the 2.3.2 development work into a supported release.

  * IDP provisioning fixes:
    - Select secure-boot boot slots by the provisionmap .static.role == "boot"
      attribute rather than the partitionimages bootable flag. rpi-image-gen
      legitimately flags bootconfig as bootable, because the EEPROM reads
      autoboot.txt out of it, so the previous predicate swept bootconfig into
      the signing set and aborted provisioning at mkfs.fat. No image-rota
      style layout could complete secure-boot provisioning as a result
      (issue #336).
    - Walk the provisionmap recursively so top-level partitions, A/B slots and
      encrypted groups are all covered without hardcoding the nesting,
      retaining the bootable flag as a fallback for flat layouts that declare
      no roles at all.
    - Create an ephemeral RPI_SB_WORKDIR when the station has not configured
      one, instead of aborting. The IDP provisioner was alone in treating the
      workdir as mandatory, contrary to the documented behaviour and to the
      sb-, fde-, naked- and bootstrap scripts. A station-configured cache
      survives; only a workdir we created is removed on exit.

  * Signing validation and failure reporting:
    - Always validate signature files, and always capture eeprom-digest and
      sign-bootcode output, during bootstrap. Only generate config.txt once
      the EEPROM artefacts have been validated.
    - Validate boot signatures in the boot image generator, and validate and
      capture signing command output in the secure-boot provisioner.
    - Add sigfile validation and stderr capture to the common shell library.
    - Move signing context initialisation below the provision-bootstrap hook
      call, so hooks run before signing state is established.

  * Bootstrap:
    - Fail early when the firmware image does not match the connected
      device's chip family, logging and aborting rather than provisioning a
      mismatched bootloader image.
    - Allow automatic boot order re-ordering, to support trying the chosen
      storage medium first.

  * HSM signing and config file safety:
    - Wire the stored HSM PIN into provisioning-time signing. The
      device-wrapped pkcs11.pin was previously consumed only by the WebUI's
      key-validation path, so saving and validating a PIN succeeded while
      signing failed for login-required tokens (#328). rpi-sb-keyhelper gains
      PKCS#11 sign and pubkey modes that unwrap the PIN in memory and supply
      it to the pkcs11-provider via OpenSSL's passphrase callback, never on a
      command line or in a pin-source file.
    - Shell-quote config values on write and unquote on read. The config file
      is written by the service but sourced by the signing scripts, so any
      value containing a shell metacharacter was mangled; a PKCS#11 URI always
      contains ';' and was silently truncated at the first one. This was also
      a latent config-injection vector, since the sourced file runs as root.
    - Extract the shared provider-load and PIN-unwrap logic into
      pkcs11_common, linked into both the service and the standalone
      keyhelper, so the two can no longer drift.
    - Make config file writes atomic and serialised. Four independent handlers
      each performed an unguarded read-modify-write with an in-place truncate,
      which under the multi-threaded event loop could lose updates or expose
      an empty or partial file to readers, including the signing scripts in
      another process. Writes now serialise behind a process-wide mutex and
      land via a temp file and rename(2), which needs no reader-side lock.

  * Device keypair capture:
    - Stop truncating the captured device public key to its PEM header. The
      gadget returns public-key and private-key as multi-line PEM, but triage
      read them with get_variable(), which stops at the first newline, so
      every <serial>.pub written since the keypair capture landed contained
      only "-----BEGIN PUBLIC KEY-----" (#341). Read armoured values with a
      new get_variable_pem() helper instead, and reuse it for the Connect
      registration path, which had carried a private copy of the workaround.
    - Stop writing a bogus <serial>.der. get_variable() ends in `|| true`, so
      the exit status triage tested for "already provisioned" was always 0 and
      the gadget's literal "refused" reply was written to disk as if it were
      key material. Triage now omits the file when the device declines to
      export the key, which is the normal case: provisioning the firmware
      crypto key locks the OTP ECDSA slot immediately.
    - Document that only <serial>.pub is expected in a keypair directory, and
      that both files are PEM rather than DER.

  * Fastboot gadget update:
    - Refresh the bundled fastboot gadgets.

  * WebUI, image management:
    - Add a Deselect Image button, and allow deleting the currently selected
      image behind a confirmation that warns it is the current selection.
      Previously the only escape from a single-image selection was to clear
      GOLD_MASTER_OS_FILE by hand over SSH (#325).
    - /delete-image now clears GOLD_MASTER_OS_FILE when the deleted image was
      the selected gold master, so every client of the endpoint leaves the
      config in a sane state.

  * WebUI, defects found during a copy review:
    - Restore two manufacturing database columns that were being dropped. The
      API returns all 30 schema columns, but the table and CSV export rendered
      only 28, omitting customer_key_fingerprint and customer_key_label. The
      fingerprint is what the Options page tells users is recorded so devices
      can be matched when re-provisioning.
    - Render the Secure column through the same formatter as its neighbours
      instead of emitting the raw database value, format the bootloader build
      timestamp rather than printing a Unix epoch, put the readable eMMC size
      in the cell with the exact byte count in the tooltip, and preserve the
      "not configured" state in the CSV export.
    - Load ZXing from /static/js/zxing.js. Static assets are served under
      /static/, so the library 404'd and the code scanner threw on
      construction; the page could not run at all. Also give its Start button
      a handler, which previously re-enabled after Stop and did nothing.
    - Fold the two duplicated special-flag tables into one, so the device
      detail page and GET /devices/{serialno}/flags can no longer describe the
      same flag differently.
    - Report an audit log outcome that was never recorded as Unknown rather
      than Failed.
    - Stop using error red for healthy devices in the topology view, where a
      resolved model generation shared a badge colour with the unknown marker.
    - Define .inline-info, referenced by the RPIBOOT GPIO note but never
      declared, and render the cipher recommendation badges from the saved
      device family so both are not marked Recommended before scripts run.

  * WebUI, irreversible device changes:
    - Flag changes that permanently modify a device in red, distinct from the
      amber used for advisory notes. Applies to the JTAG lock, EEPROM write
      protection, the RPIBOOT GPIO pin and the secure boot provisioning
      style, all of which write OTP or apply a lock that cannot be removed.
    - State plainly that locking JTAG prevents Raspberry Pi engineers from
      assisting with hardware debugging or failure analysis on affected
      devices for the life of the device, and that EEPROM write protection
      blocks bootloader updates including security fixes.
    - Mark the two Hardware Security toggles Permanent on the control itself,
      so the cost is visible before the switch is flipped rather than in a
      footnote below the section.

  * WebUI, copy review:
    - Spell out abbreviations that invited the wrong expansion in the
      manufacturing database: "(req.)" read as required rather than
      requested, and "Devkey" as device key rather than development key.
      Rename Secure to Device Identity to match its documented meaning, and
      settle on one name for the Connect device ID column across the table,
      the CSV export and the API.
    - Fix an empty-state message directing the reader below for a field that
      sits above it, a generated PKCS#11 URI labelled Manual, and a PIN
      button naming its two steps in the opposite order to the code.
    - Rename the device view tabs from Relational, Detail and Tiles to names
      describing what they show, label USB bus paths as such rather than as
      ports, and stop abbreviating Triage to "Tri" beneath a step circle
      numbered 2.
    - Rename the Services columns Status and Active to Load State and Run
      State, align the audit log filter labels with the labels the table
      renders, and replace raw provisioner and stage identifiers on the
      Customisation page with display names, keeping the identifier as
      secondary text since it is the script filename on disk.
    - Title the code scanner for the Data Matrix codes it also decodes, which
      is what appears on Raspberry Pi packaging.
    - Expand IDP on first use, correct Raspberry Pi product names, use
      British spelling throughout, and settle on one ellipsis character and
      one word per meaning for absent values.

  * USB port restriction:
    - Restrict provisioning to a known set of USB ports, so a single-headed
      programming jig only picks up boards in its head and leaves anything
      else plugged into the host alone. Ports are listed as topology paths
      (e.g. 1-1.2), shell globs included, in *.conf drop-ins under
      /usr/share/rpi-sb-provisioner/usb-ports.d for packages describing a
      particular jig and /etc/rpi-sb-provisioner/usb-ports.d for the local
      administrator. Files merge by basename with the /etc copy winning, so
      differently-named files are additive and an empty same-named file
      masks a package-supplied rule set.
    - Default to the historical behaviour: with no *.conf in either
      directory the restriction is inactive and every port is accepted.
    - Enforce the restriction in both entry points -- rpi-sb-bootstrap.sh
      for rpiboot devices and rpi-sb-triage.sh for fastboot devices, which
      checks independently because an already-provisioned board can reach
      fastboot without passing through bootstrap.
    - Treat a non-permitted port as a skip rather than a failure: record a
      PORT-EXCLUDED state and log the reason, but take no lock, write no
      udev rule, run no rpiboot and fire no provision-failed hook. Skipped
      devices render muted in the device tiles and carry an explanatory
      banner on the device details page.
    - Skip a device whose USB path cannot be determined while a restriction
      is in force, since an allowlist cannot be honoured for a device whose
      port is unknown.
    - Show the restriction in the port tree and the topology map, which
      previously drew every port as equally usable. The service computes
      whether each port is permitted -- mirroring the shell's matching, so
      the views cannot disagree with the decision the scripts make -- and
      publishes the effective rules alongside the topology. A status bar
      names the active rules and offers a Hide excluded ports toggle, on by
      default whenever a restriction is in force.
    - Fix two port tree defects that hiding exposed. The tier scaffolding
      forced a minimum of two USB 3 root ports so spare ports stayed visible
      to plug into, which left a phantom USB 3 Root on a host that has no
      SuperSpeed device; and the tree is built by mutation with pruning
      deliberately disabled, so filtered-out ports stayed on screen. Both now
      collapse while hiding, taking childless root-port groups and tiers with
      them, and are unchanged when nothing is being hidden.

  * Failing usefully on misconfiguration:
    - Establish that a usable OS image is configured before engaging the
      device. An unset GOLD_MASTER_OS_FILE previously reached losetup, which
      retried for 25 seconds and then failed reporting a loop-device error,
      having already recorded PROVISIONER-STARTED and taken the device
      through bootstrap. Triage now classifies the configured image up front
      and aborts with the actual reason; the provisioners re-check, since
      their units can be started directly and the configuration can change
      between triage selecting one and it running.
    - Route to the IDP provisioner by classification rather than a bare
      directory test. `-d` is also false for an IDP artefact whose directory
      is merely absent -- an unmounted share, a deleted export -- so such a
      device was silently handed to the .img provisioner and failed later
      with an error naming loop devices rather than the missing artefact.
      The directory is now held to the same one-descriptor rule the IDP
      provisioner enforces in its own pre-flight, so the two cannot disagree
      about whether an artefact is usable.
    - Report whether a failure is permanent, via PROVISION_FAILED_PERMANENT
      in the provision-failed hook environment. Hooks could previously only
      see the stage a failure occurred in, and bootstrap-stage failures are
      a mix of the transient (USB re-enumeration as the device reboots) and
      the permanent (no signing key, no OS image). Anything filtering out
      the former to avoid false alarms necessarily discarded the latter too,
      so a jig could sit on its in-progress indicator indefinitely with a
      misconfiguration that would never resolve. The stage is unchanged --
      it still selects the hook's positional arguments -- so existing hooks
      keep working and can adopt the flag when they choose.

  * Service lifecycle:
    - Explicitly handle device event loop termination.

  * Documentation:
    - Add docs/hsm-support.md, covering supported tokens, requirements, PIN
      handling, p11-kit setup, a SoftHSM2 smoke test and troubleshooting.
      Link it from config_vars.md and the README, and correct the stale
      Signing Helpers section in architecture.md.
    - Cover boot package generation, and capture the images endpoint change.
    - Add docs/usb-port-restriction.md, covering the drop-in directories,
      the file format, merge and masking rules, how to find the topology
      path for a port and how a skipped device is reported. Link it from
      the README and config_vars.md.

 -- Tom Dewey <tom.dewey@raspberrypi.com>  Tue, 18 Aug 2026 14:27:41 +0100

rpi-sb-provisioner (2.3.1) unstable; urgency=medium

  * Final 2.3.1 release:
    - Roll up the 2.3.1 development work into a supported release.

  * Customisation hooks:
    - Add provision-failed stage for sb-, fde-, naked-, and idp-provisioner
      scripts so programming rigs can signal errors (e.g. status LEDs).
    - Invoke provision-failed from bootstrap, triage, and provisioner cleanup
      on non-zero exit.
    - Export PROVISION_FAILED_CONTEXT to provision-failed scripts.
    - Do not invoke provision-failed for duplicate bootstrap@ lock contention
      or triage failure while bootstrap is still in progress (expected USB
      re-enumeration during DUT reboot).
    - Export TARGET_USB_PATH and TARGET_DEVICE_PATH to provisioning hooks
      via the environment so programming rigs can drive per-port indicators
      (issue #273).
    - Export all manufacturing-database field values to post-flash hooks
      (BOARDNAME, ETH_MAC, OS_IMAGE_SHA256, etc.) after metadata_gather.
    - Document per-stage positional arguments and environment variables in
      WebUI customisation script templates.
    - Always compute manufacturing metadata during metadata_gather, not only
      when RPI_SB_PROVISIONER_MANUFACTURING_DB is configured.
    - Add get_device_path_for_serial() helper and resolve TARGET_DEVICE_PATH
      during provisioning alongside the existing USB path lookup.
    - Fix get_usb_path_for_serial() udevadm fallback using the wrong variable.

  * Fastboot gadget update:
    - Refresh bundled fastboot gadgets to rpi-fastbootd
      14.0.0~git20260608, picking up on-device EEPROM update, verify and read
      commands plus SPI flash identity getvars.
    - Reject EEPROM images whose MFG_VER is below the board's min_boot_ver
      before write or verify, preventing downgrades on boards that require a
      newer bootloader baseline.
    - Rebundle gadgets with libblockdeviceid-based block device ID derivation
      for LUKS passphrase generation, matching the boot-time cryptroot unlocker
      so NVMe FDE volumes provisioned by the host unlock correctly on first
      boot (issue #316).

  * Signing key registry and WebUI:
    - Add a saved-key registry for multiple PEM and PKCS#11 signing keys with
      one active key driving provisioning and CUSTOMER_KEY_* config sync.
    - Redesign the Options page as a tile grid showing per-key status,
      encrypted-at-rest state, fingerprint and RSA-2048 fit-for-purpose checks.
    - Migrate legacy single-key PEM/PKCS#11 config entries into the registry
      on first access.

  * Key validation and at-rest handling fixes:
    - Fix validate-key and manual key paths outside
      /etc/rpi-sb-provisioner/keys being rejected despite valid PEM content.
    - Load the OpenSSL default provider before key parsing and return clearer
      errors for public keys, certificates, OpenSSH keys and encrypted PEMs.
    - Fix the Options page continuing to show "Encrypted at rest" after the
      last saved key is removed, and improve browse-to-upload handling.

  * Firmware-crypto key bootstrap for at-rest wrapping:
    - Generate a firmware-crypto key on hosts that have none, via
      rpi-fw-crypto genkey in postinst, so device-bound wrapping of secrets
      at rest (HSM PINs, uploaded PEM keys) works without a pre-existing
      factory device key. Skipped when a key already exists or the crypto
      service is unavailable, and never blocks installation.
    - Stop requiring the DEVICE-flagged key for wrapping: keywrap now uses
      any populated OTP key slot, matching the generated (non-DEVICE) key.
    - Depend on rpifwcrypto for the rpi-fw-crypto tool.

  * Manufacturing database:
    - Record customer_key_fingerprint and customer_key_label from the active
      registry key at provision time, with postinst schema migration.

  * Provisioners:
    - Flash OS images over the TCP data-plane specifier when the daemon
      advertises split mode (-i usb+tcp) in naked-, fde-, and sb-provisioner,
      matching idp-provisioner, so image transfer over Ethernet works again
      instead of silently falling back to USB serial (issue #314).

  * Triage:
    - Use best-effort USB path lookup for the initial TRIAGE-STARTED record:
      query the full device serial, tolerate a miss without aborting under
      set -e, and let setup_fastboot_and_id_vars recompute the path later.

  * Documentation:
    - Document IDP artefact handling, key registry and validation endpoints,
      PEM/PKCS#11 device-wrapping, and Raspberry Pi Connect registration fields
      across README and API guides.
    - Document provision-failed hooks, per-stage environment variables, and
      manufacturing metadata exported to post-flash scripts.
    - Document Ethernet image transfer over the fastboot TCP data plane when
      provisioning boards are linked over a network interface.

 -- Tom Dewey <tom.dewey@raspberrypi.com>  Mon, 30 Jun 2026 12:00:00 +0100

rpi-sb-provisioner (2.3.0) unstable; urgency=medium

  * Final 2.3.0 release:
    - Roll up the 2.3.0 preview series and the post-2.3.0~pre4
      stabilisation work into a supported release.

  * IDP (Image Description Provisioning) support:
    - Add the rpi-idp-provisioner.sh orchestrator and systemd unit for
      consuming rpi-image-gen IDP artefacts, including pre-flight JSON,
      storage, image and encryption validation.
    - Support IDP archive upload, extraction and SHA256 sidecars in the
      WebUI, with image-first configuration that derives device family,
      storage type and encryption settings from image metadata.
    - Sign secure-boot IDP boot slots, preserve the boot_ramdisk config for
      first boot, support customisation hooks, and expose IDP service logs.
    - Harden IDP handling around LUKS2 cipher detection, missing storage
      metadata, unsupported storage types, timeout handling and Zero 2 W
      platform mapping.

  * Firmware crypto, Connect and key handling:
    - Provision and OTP-lock the device firmware crypto key for all
      provisioning styles, using it for cryptroot unlock and Raspberry Pi
      Connect device identity registration.
    - Add Raspberry Pi Connect registration options, manufacturing database
      fields, UI/CSV output and non-fatal registration handling.
    - Move PKCS#11 key parsing from the deprecated OpenSSL ENGINE path to
      pkcs11-provider/OSSL_STORE, with in-process PIN handling and provider
      readiness plus HSM key discovery in the WebUI.
    - Encrypt stored HSM PINs and uploaded PEM signing keys at rest using an
      AES-256-GCM wrapper derived from the Raspberry Pi firmware crypto
      device key.
    - Add rpi-sb-keyhelper and PEM/PKCS#11 signing wrappers so wrapped keys
      can be used by the Raspberry Pi signing tools without writing
      plaintext private keys back to disk.

  * Secure-boot bootstrap and firmware updates:
    - Add AB-capable 2712 EEPROM image support by detecting and signing both
      bootcode and bootsys before passing them to rpi-eeprom-config.
    - Use set_reboot_order=0x3 in recovery configs so devices return to
      RPIBOOT automatically for the fastboot bootstrap phase, removing the
      manual Pi 5 re-plug step and the corresponding WebUI guidance.
    - Refresh bundled fastboot gadgets repeatedly through the EEPROM
      measurement, A/B bootfiles and monolithic sparse whole-disk image
      writing updates.
    - Update rpi-eeprom and rpiboot runtime dependencies to versions that
      carry the matching recovery images, bootfiles and firmware support.

  * Reliability and provisioning correctness:
    - Refresh cryptroot_initramfs for the systemd initrd flow, fixing the
      Trixie/systemd 257 switch-root regression reported in #299.
    - Use kernel crypto API aliases in kernel_modules.list and copy modules
      for all applicable kernels, making encrypted images more robust across
      kernel updates.
    - Preserve real provisioner exit statuses from cleanup traps so systemd,
      the WebUI and manufacturing records reflect failures correctly.
    - Force FAT32 for signed boot-slot filesystems and propagate mkfs
      failures rather than silently flashing bad slots.
    - Fix secure-boot images without static.role="boot" partitions and avoid
      rewriting root= in cmdline.txt.
    - Invalidate cached signed artefacts when firmware or signing keys
      change, and wipe the workdir artefact cache on package upgrades.

  * WebUI, manufacturing data and service updates:
    - Replace the state.db inotify watcher with localhost-only internal
      notification endpoints protected by a per-boot token, eliminating the
      steady-state CPU spin while keeping topology updates responsive.
    - Improve device and detail-view liveness, add responsive tile and detail
      views, improve manufacturing table pagination/loading/escaping, and
      consolidate shared styling.
    - Record additional EEPROM measurements, capture intended vs observed
      security state, fix CM5 Lite identifiers, and drop extraneous EEPROM
      fields from manufacturing records.
    - Add SQL busy timeouts for SQLite contention under high device
      throughput.

  * Documentation and packaging:
    - Convert project documentation from AsciiDoc to Markdown and refresh
      README, API and device guidance for the 2.3.0 workflows.
    - Add zlib, zstd, librpifwcrypto, pkcs11-provider, p11-kit and gnutls
      dependencies required by the new archive, key wrapping and HSM paths.
    - Clean stale FetchContent state during package builds, install the new
      IDP and signing helper scripts, create the lock directory on install,
      and fix the udev rules comma.

 -- Tom Dewey <tom.dewey@raspberrypi.com>  Wed, 10 Jun 2026 12:28:37 +0100

rpi-sb-provisioner (2.3.0~pre4) unstable; urgency=medium

  * WebUI: remove Raspberry Pi 5 re-plug notification:
    - Obviated by set_reboot_order=0x3 (2.3.0~pre2), which reboots the
      device straight back into RPIBOOT mode for the fastboot bootstrap
      phase, so the operator no longer has to manually unplug and
      re-insert the USB-C cable after the EEPROM-update reboot.
    - provisioner-service/src/devices.cpp: drop the needsReplug signal
      and the sticky boardTypeBySerial plumbing (per-serial latch,
      placeholder state/board_type carry, final board_type pass) that
      existed solely to keep the badge alive across that reboot.
    - Remove the Re-plug badge from the topology and tile views and the
      re-plug banner from the device-details page (devices.csp,
      tile_view.csp, device_detail.csp).
    - board_type detection in rpi-sb-bootstrap.sh and the state.db
      board_type column are retained as generic device metadata (still
      surfaced as boardType in the topology JSON).
    - docs: rewrite the Pi 5 connection guide for the single-connection
      flow (drop the manual unplug/reconnect "Step 2"), and update the
      stale "unlike Pi 5" reconnection contrasts in the Pi 4 guide and
      README quick reference.

 -- Tom Dewey <tom.dewey@raspberrypi.com>  Mon, 01 Jun 2026 12:02:11 +0100

rpi-sb-provisioner (2.3.0~pre3) unstable; urgency=medium

  * Bootstrap: AB-capable EEPROM image support:
    - service/rpi-sb-bootstrap.sh: detect AB-capable 2712 EEPROM
      images by probing for the "bootsys" filename marker at offset
      0x10008 (new isABCapableImage() helper), and when present,
      sign the extracted bootsys blob with rpi-sign-bootcode in
      addition to bootcode.bin, passing --bootsys alongside
      --bootcode to rpi-eeprom-config. Mirrors usbboot PR
      raspberrypi/usbboot#363; non-AB images take the unchanged
      single-bootcode path.

 -- Tom Dewey <tom.dewey@raspberrypi.com>  Thu, 28 May 2026 17:08:49 +0100

rpi-sb-provisioner (2.3.0~pre2) unstable; urgency=medium

  * cryptroot_initramfs regression fix (#299):
    - Refresh the bundled cryptroot_initramfs from pi-gen-micro. The
      Trixie-rebuilt image (systemd 257) refused systemctl switch-root
      from non-initrd mode, leaving devices provisioned successfully
      but stuck at the initramfs login prompt instead of pivoting into
      the unlocked rootfs.
    - The new image is marked as an initrd (/etc/initrd-release) and
      the cryptroot service has been rewired into the standard systemd
      initrd flow: Before=initrd-root-fs.target with the pivot
      delegated to systemd's stock initrd-switch-root.service, instead
      of the bespoke multi-user.target + manual switch-root from
      v2.1.3.
    - getty is masked in the cryptroot image: the "localhost login:"
      symptom cannot recur, and emergency.target still gets a console
      via sulogin if cryptroot.service fails.

  * Kernel module list durability (also #299):
    - host-support/kernel_modules.list: switch from concrete crypto
      module names (chacha-neon, chacha_generic, aes-arm64, ...) to
      kernel crypto API aliases (crypto-xts(aes), crypto-adiantum,
      crypto-nhpoly1305, crypto-xchacha12). Aliases are resolved by
      libkmod via modules.alias and survive the upstream module
      renames between 6.12 and 6.18, fixing the secondary
      provisioning-time failure also reported in #299.

  * Bundled rpi-fastbootd refresh:
    - Refresh the fastboot gadget image to pick up rpi-fastbootd
      be8a8ce ("vars: Add eeprom manipulation, data fetch"), which
      adds oem eeprom-update / eeprom-verify / eeprom-read plus
      signed-eeprom, eeprom-device, eeprom-size, eeprom-sha256,
      eeprom-jedec, eeprom-unique-id and eeprom-spi-speed getvars.

  * Dependency refresh:
    - debian/control: require rpi-eeprom (>= 28.23-1) to pick up the
      rpiboot/recovery image that honours set_reboot_order in the
      recovery config.txt.

  * Bootstrap: eliminate re-plug requirement on Pi 5 (and Pi 4):
    - service/rpi-sb-bootstrap.sh: insert set_reboot_order=0x3 ahead
      of recovery_reboot=1 in both the secure-boot keywriting recovery
      config and the non-secure EEPROM-update recovery config, so the
      device reboots straight back into RPIBOOT mode ready for the
      fastboot bootstrap phase instead of requiring a manual USB
      re-plug.

 -- Tom Dewey <tom.dewey@raspberrypi.com>  Wed, 27 May 2026 14:57:26 +0100

rpi-sb-provisioner (2.3.0~pre1) unstable; urgency=medium

  * Pre-release:
    - First preview of the 2.3.0 line. Combines the IDP, Connect and
      rpifwcrypto work from the 2.3.0 changelog draft with the
      post-April stabilisation and re-plug guidance work below.

  * IDP (Image Description Provisioning) Support:
    - Add support for consuming IDP-style artefacts from rpi-image-gen,
      enabling pre-built partition layouts, encryption, and sparse image
      provisioning via the device-side fastboot IDP protocol
    - Add new rpi-idp-provisioner.sh orchestrator implementing the full
      IDP protocol: erase, stage JSON, idpinit, idpwrite, idpgetblk/flash
      loop, and idpdone, with timeout_fatal wrappers on all fastboot
      commands
    - Add rpi-idp-provisioner@.service systemd template unit
    - Extend triage to detect IDP artefact directories
      (GOLD_MASTER_OS_FILE pointing to a directory) and route to the
      IDP provisioner automatically
    - IDP pre-flight validation: JSON syntax, referenced .simg file
      existence, device class and storage type cross-checks against
      host configuration

  * IDP WebUI:
    - Add image-first progressive disclosure UI: selecting an IDP
      artefact auto-populates device family, storage type, and cipher
      from the artefact's JSON metadata, locking those fields with
      "Set by image" badges
    - Hide the FDE-only provisioning style tile for IDP artefacts, as
      encryption is defined by the IDP JSON; provisioning style collapses
      to a binary Secure Boot / Naked choice
    - Support upload of .tar.gz, .tgz, and .zip archives containing IDP
      artefacts, with security validation (path traversal checks, disk
      space checks, atomic extraction)
    - Add /analyze-image endpoint returning IDP metadata (device class,
      storage type, encryption, cipher, partition count, image version)
    - Display IDP badge in the image list for artefact directories

  * Image SHA256 Improvements:
    - Compute SHA256 of IDP archives before extraction, capturing the
      fingerprint of the exact artefact uploaded
    - Store IDP archive hash in a .sha256 sidecar file alongside the
      extracted artefact directory, reusing the existing sidecar pattern
    - Update /get-image-sha256 to return sidecar hash directly for IDP
      artefact directories, avoiding unnecessary background calculation
    - Add is_idp flag to ImageInfo struct for reliable IDP detection
      in image listings, replacing fragile sha256 string comparison
    - Clean up sidecar files when deleting IDP artefact directories

  * Bug Fixes:
    - Fix image deletion from the Options page: frontend was calling
      /images/delete (non-existent) instead of /delete-image (the
      documented and registered endpoint)
    - Fix HTTP method for image deletion: changed from DELETE to POST
      to match the backend handler
    - Fix image browser detail pane overflowing its container by adding
      box-sizing: border-box to .image-list and .firmware-notes
    - Fix cleanup_orphans destroying persistent data directories
      (images, workdir, databases): TEMP_BASE now points to a dedicated
      /srv/rpi-sb-provisioner/tmp subdirectory, and all temp directory
      creation uses a new make_temp_dir helper
    - Fix rpi-sb-common.sh base directory variables overwriting values
      pre-set by sourcing scripts (e.g. rpi-sb-bootstrap.sh)

  * IDP Validation in provisioner-service:
    - Extend GOLD_MASTER_OS_FILE validation in options.cpp to support
      directories: validates exactly one .json file exists, JSON is
      syntactically valid, and all referenced .simg files are present

  * Packaging:
    - Add rpi-idp-provisioner.sh to /usr/bin and
      rpi-idp-provisioner@.service to /usr/lib/systemd/system in
      debian/install
    - Create /var/lock/rpi-sb-provisioner in postinst so with_lock()
      works on fresh installs; LOCK_BASE was previously assumed to
      exist but never created by the package
    - Remove redundant per-call board_type migration probe from
      record_state() in host-support/state-recording: postinst
      already performs a more thorough state.db schema migration on
      upgrade, so the runtime probe added two SQLite invocations to
      every device state transition for no benefit

  * Raspberry Pi Connect Device Identity Registration:
    - Automatically register provisioned devices with the Raspberry Pi
      Connect management API when RPI_CONNECT_API_KEY is configured
    - Request signing uses the device's firmware crypto ECDSA key via
      fastbootd's 'oem fwcrypto sign-hash' command; the private key
      never leaves hardware
    - Add RPI_CONNECT_API_KEY and RPI_CONNECT_DESCRIPTION config
      options with validation in options.cpp
    - Add Cloud Services section to options UI with password-masked
      API key input and description prefix field
    - Add connect_registered and connect_device_id columns to
      manufacturing database schema, with migration in postinst
    - Add Connect columns to manufacturing UI table and CSV export
    - Non-fatal: registration failures log warnings but never abort
      provisioning (set +e guard, curl timeouts, robust PEM capture)

  * Cryptroot rpifwcrypto Support:
    - Update cryptroot initramfs to unlock the encrypted root filesystem
      using the device's firmware crypto ECDSA key (rpifwcrypto), in
      place of key material delivered out-of-band or stored on disk
    - Triage now always provisions the device firmware crypto key
      (oem fwcrypto init) before provisioning proceeds, regardless of
      provisioning style, and verifies the key is written to OTP before
      continuing -- aborting the device if provisioning fails
    - Establishes a hardware-held device unique identity that underpins
      both LUKS unlock (via a derived secret bound to the key and other
      measurements) and Raspberry Pi Connect device identity
      registration; the private key never leaves the SoC

  * Provisioner Exit Status Fix:
    - Fix cleanup() trap handler in rpi-sb-provisioner.sh,
      rpi-fde-provisioner.sh, rpi-naked-provisioner.sh,
      rpi-idp-provisioner.sh and rpi-sb-bootstrap.sh swallowing the
      real exit status: the re-entry guard and CLEANUP_DONE=1
      assignment clobbered $? before it was captured, so any failure
      caught by the trap (set -e, die, SIGTERM) was reported as
      success -- the systemd unit showed "Deactivated successfully"
      and the WebUI saw a clean run despite aborted provisioning
    - Capture $? as the first statement of cleanup() so the original
      exit status propagates through to systemd and the manufacturing
      database

  * IDP Refinements:
    - Sign boot slots for secure-boot IDP provisioning, and drop a
      boot_ramdisk config.txt next to the signed boot.img so the
      device picks up the signed bootchain on first boot
    - Ensure the device unique firmware crypto key is OTP-locked at
      the end of provisioning, establishing an immutable hardware
      identity for subsequent boots
    - Expand the encryption flag matcher so all LUKS2 cipher variants
      are recognised as encrypted IDP layouts (fixes LUKS2 cipher
      field handling)
    - Fall back to the configured storage type when the IDP JSON does
      not name one, instead of refusing to provision
    - Catch all non-supported storage types up-front with a clear
      error rather than proceeding to a broken flash
    - Add missing timeout_nonfatal wrappers in rpi-idp-provisioner.sh
    - Default timeout_fatal to 30s in the IDP provisioner
    - Wire provision-started hook arguments through rpi-sb-common.sh
      and move the IDP provision-started hook to the correct
      lifecycle point
    - Add IDP provisioner customisation hook editors to the WebUI
    - Allow IDP service log viewing in the WebUI
    - Fix incorrect zero2w platform name mapping in the IDP
      provisioner, image-handling paths, and documentation

  * Re-plug Guidance:
    - Record the discovered board type per device so re-plug guidance
      can be tailored to the family
    - Add Raspberry Pi 5 re-plug banners to the WebUI, prompting
      operators to physically re-plug devices that cannot enter
      RPIBOOT automatically
    - Improve device and detail-view liveness so the WebUI reflects
      in-flight provisioning state without manual refresh
    - Capture provisioning intent vs observed state for fields that
      cannot yet be confirmed over fastboot (jtag_locked,
      eeprom_write_protected); add a devkey_revoked column observed
      via 'getvar secure-devkey'; clarify signed_boot_enabled as
      derived from pubkey_programmed in the manufacturing API

  * Fastboot Data Path & Gadget Updates:
    - Use TCP for the data path when fastbootd advertises support,
      improving throughput over USB on capable hardware
    - Refresh the bundled fastboot gadget against upstream
      rpi-fastbootd, through b1e51bd4, e8c13a5 and finally
      f3ce930249ab0d657ddaf5d3ba4a076320894c78

  * Workdir Cache Invalidation:
    - Invalidate cached signed artefacts in $RPI_SB_WORKDIR when the
      selected firmware or signing keys change, so stale boot.img
      files are never re-flashed
    - Wipe all cached provisioning artefacts under $RPI_SB_WORKDIR on
      package upgrade

  * Triage:
    - Trust idempotent 'oem fwcrypto init' and drop the pre-check
      that round-tripped over fastboot before initialisation

  * Configuration Plumbing:
    - Introduce shared resolvers for special configuration flags,
      consumed by both provisioner-service and rpi-sb-bootstrap.sh,
      so flag interpretation is consistent across host components

  * Database Contention:
    - Use SQL busy timeouts in provisioner-service to handle SQLite
      contention gracefully under high device throughput

  * Provisioner Service State Change Notifications:
    - Drop the inotify watch provisioner-service used to keep on
      /srv/rpi-sb-provisioner/state.db and -wal; the watch self-
      triggered against the service's own SQLite reads and pinned a
      core at 100% during steady-state operation
    - Introduce a localhost-only HTTP API on provisioner-service for
      the bash provisioning scripts to push state-change and
      manufacturing-row notifications: POST /internal/state-changed
      (paired with record_state in host-support/state-recording) and
      POST /internal/manufacturing-recorded (paired with the
      manufacturing.db INSERT in host-support/manufacturing-data);
      both kick the topology worker for an immediate rescan
    - Gate the endpoints with two access layers: a transport-level
      loopback check on the real peer address (X-Forwarded-For is
      ignored so a reverse proxy cannot spoof 127.0.0.1), and a
      per-boot shared-secret token written by the service to
      /run/rpi-sb-provisioner/internal.token at mode 0600. Token
      comparison is constant-time
    - Calls from the provisioning scripts are fire-and-forget with
      short curl timeouts; a missing token file or unreachable
      service does not fail provisioning, and the worker's existing
      10s wait_for backstop still covers anything that slips past

  * Common Filesystem Handling:
    - Force FAT32 when formatting the signed boot slot vfat, and
      propagate mkfs failures so a bad slot is not silently flashed

  * UI & Image Browser Polish:
    - Use image summaries for both IDP artefacts and traditional
      GOLD_MASTERs in the image list
    - Move firmware selection into the device block in the image
      browser
    - Refactor options handling for IDP preference, and offer
      differential security options for IDP and naked provisioning
    - Allow the IDP storage selection to be overridden from the UI
    - Fix the OS image browser bounding box
    - Fix 1440-minute image deletion timing
    - Use a .sha256 sidecar for OS image SHA256 recording in
      manufacturing-data
    - Update Connect terminology in provisioner scripts to match the
      product naming
    - Fix Connect public key handling in manufacturing-data
    - Fix CM5 Lite board identifier in manufacturing-data output and
      UI rendering
    - Correct default boot order comments in the shipped defaults

  * Packaging:
    - Add missing zlib1g-dev and libzstd-dev build dependencies
    - Update rpi-eeprom and rpiboot runtime dependencies
    - Create the lockfile directory in postinst on install
    - Fix a missing comma in debian/rpi-sb-provisioner.udev

 -- Tom Dewey <tom.dewey@raspberrypi.com>  Thu, 21 May 2026 16:30:00 +0100

rpi-sb-provisioner (2.2.0) unstable; urgency=medium

  * Features:
    - Add PKCS#11 HSM signing support for secure signing operations
    - Add USB speed attribute detection for better device capability
      identification
    - Add CSRF token validation for critical endpoints to improve security
    - Add kernel_modules.list for configurable kernel module inclusion
    - Integrate rpi-modcopy for improved kernel module handling and
      dependency resolution
    - Add RPI_DEVICE_RPIBOOT_GPIO configuration for Pi 4 family secure-boot
      provisioning (fixes GitHub issue #242)

  * WebUI Accessibility:
    - Add lang="en" attribute to all page templates for screen readers
    - Add semantic HTML structure with <main> landmark and skip navigation
    - Add ARIA roles and live regions to status/error messages
    - Add table captions and scope attributes for screen reader navigation
    - Add aria-label attributes to action buttons with contextual information
    - Add accessible tab interfaces with proper ARIA roles and keyboard support
    - Add screen-reader-only descriptions for D3 visualizations
    - Add role="log" and aria-live to service log container
    - Improve focus visibility with enhanced CSS outline styles
    - Add aria-hidden to decorative icons and emojis

  * Provisioning Improvements:
    - Refactor signing infrastructure into rpi-sb-common.sh for better
      modularity and reuse
    - Enhance cleanup process with guard against multiple invocations
    - Fix temporary directory creation path to /srv/rpi-sb-provisioner,
      avoiding Trixie tmpfs issue
    - Improve orphan cleanup logic to preserve base temp directory
    - Fix Pi 4/CM4 secure-boot provisioning by programming RPIBOOT GPIO
      into OTP alongside the secure boot public key

  * WebUI:
    - Add JSON endpoints for firmware listing and management
    - Improve device visualization with edge avoidance force and better
      link rendering
    - Conditionally display firmware select button for cleaner interface
    - Add RPIBOOT GPIO configuration field for Pi 4 secure-boot provisioning
      with validation and clear guidance recommending GPIO 8

  * Documentation:
    - Revise provisioning capacity and throughput metrics
    - Update Pi 4 device guidance with RPIBOOT GPIO requirements
    - Add RPI_DEVICE_RPIBOOT_GPIO to configuration variables documentation

  * Naked Provisioner & Image Customisation:
    - Expand naked-provisioner stages to include bootfs-mounted and
      rootfs-mounted phases for full image customisation support
      (fixes GitHub issue #258)
    - Use cp --reflink=auto for image copying, optimising performance
      and reducing disk usage on btrfs/xfs hosts
    - Add checks to ensure customisation scripts are executable before
      running
    - Flash modified image when customisation scripts are executed

  * Provisioning Hooks:
    - Add provision-started stage and hook, allowing actions (e.g. LED
      control, rig signaling) to execute at the start of provisioning

  * Customisation WebUI:
    - Add copy-sources functionality allowing users to select and copy
      scripts from other provisioners during stage editing
    - Display dropdown for available scripts from other provisioners,
      improving usability and efficiency

  * Provisioning Improvements:
    - Consolidate loop device management functions (ensure_next_loopdev,
      ensure_loopdev_partitions, unmount, unmount_image) into
      rpi-sb-common.sh for better code reuse and maintainability
    - Improve cleanup logic in provisioner scripts to properly unmount
      images before deletion, preventing dangling loop devices
    - Enhance error handling in losetup operations with clearer exit
      messages on failure
    - Remove redundant get_variable function from rpi-fde-provisioner.sh

  * Packaging:
    - Add rpi-modcopy dependency for kernel module handling
    - Fix CMake configuration for jsoncpp dependency
    - Improve upgrade experience by shipping config defaults to
      /usr/share/rpi-sb-provisioner/defaults/ with user overrides in
      /etc/rpi-sb-provisioner/. This eliminates dpkg conffile prompts
      during upgrades when configuration has been modified.

 -- Tom Dewey <tom.dewey@raspberrypi.com>  Tue, 11 Feb 2026 12:00:00 +0000

rpi-sb-provisioner (2.1.3) unstable; urgency=medium

  * Features:
    - Add test mode for USB device topology simulation
    - Add timestamp handling and formatting for service logs
    - Integrate D3.js for enhanced graph visualization in device topology UI
    - Enhance collision resolution for device layout in UI
    - Add configuration field validation endpoint with real-time UI feedback
    - Add Nginx reverse proxy example configuration with PAM authentication

  * Bootstrap Improvements:
    - Run metadata_gather before post-flash for better data collection
    - Simplify IPv6 address extraction with awk

  * WebUI:
    - Allow boot package generation for both secure-boot and fde-only
      provisioning styles
    - Display last updated timestamps for services in user-friendly format
    - Improve device visualization with D3.js graph rendering and tooltips

  * Manufacturing Data:
    - Add support for capturing Zero W and unknown device values

  * Packaging:
    - Update to debhelper-compat version 13
    - Use jsoncpp as a vendored dependency in CMake configuration
    - Update static files path configuration for improved clarity
    - Update lintian overrides for embedded libjsoncpp

  * Dependencies:
    - Update rpiboot and rpi-eeprom to catch November 2025 firmware

 -- Tom Dewey <tom.dewey@raspberrypi.com>  Thu, 16 Jan 2026 12:00:00 +0000

rpi-sb-provisioner (2.1.2) unstable; urgency=medium

  * Bootstrap: Fix USB path extraction for hub-connected devices
  * WebUI: Add serial-based fallback matching for device status display
  * WebUI: Add dynamic validation for options form fields
  * Fix: Devices connected via USB hubs now correctly show provisioning
    status in the topology map view
  * WebUI: Fixup device state retrieval race condition
  * Packaging:
    - Fix maintainer-script-ignores-errors by adding set -e to postinst
    - Fix man page table formatting by preprocessing with tbl
    - Add lintian override for boot image generator

 -- Tom Dewey <tom.dewey@raspberrypi.com>  Mon, 17 Nov 2025 16:45:00 +0100

rpi-sb-provisioner (2.1.1) unstable; urgency=medium

  * WebUI: Fix bootimg generator bashisms
  * WebUI: Style bootimg generator buttons
  * WebUI: Add explicit bootimg generation button
  * WebUI: Fix hangs on navigation on pages using AJAX

 -- Tom Dewey <tom.dewey@raspberrypi.com>  Fri, 10 Oct 2025 16:45:00 +0100

rpi-sb-provisioner (2.1.0) unstable; urgency=medium

  * Features:
    - Add automatic boot image generator for creating signed boot.img
      from uploaded OS images
    - Add Debian package generation for boot image updates
    - Add RPI_SB_PROVISIONER_ENABLE_PRIVATE_KEY_API security option
    - Add pagination and ordering for service log API

  * Documentation:
    - Complete README rewrite with beginner-friendly content
    - Add architecture documentation with workflow diagrams
    - Add boot image generator documentation
    - Add Debian package creation examples
    - Enhance API documentation with more examples

  * Bootstrap Improvements:
    - Fixup EEPROM update for naked provisioning on 2711/2712
    - Remove unnecessary writeSig() function, use rpi-eeprom-digest
    - Improve handling of special re-provisioning cases for Pi 5
    - Add better logging for edge cases (empty serials, missing firmware)
    - Improve non-secure-boot EEPROM update flow

  * WebUI:
    - Add new device status UI based on USB topology
    - Enhance audit logging across all endpoints

  * Script Improvements:
    - Strip GNU-specific and Bash-specific syntax for POSIX compliance
    - Fix shell option detection in customization scripts
    - Fix regex in simg_expanded_size() function
    - Fix depmod handling for kernel modules

  * Manufacturing Database:
    - Fixup metadata collection (secure vs signed distinction)
    - Improve accuracy of device state tracking

  * Packaging:
    - Update rpiboot dependency to >= 20251002~150524
    - Update rpi-eeprom dependency to >= 28.5
    - Update embedded curl to 8.16.0

 -- Tom Dewey <tom.dewey@raspberrypi.com>  Fri, 10 Oct 2025 16:45:00 +0100

rpi-sb-provisioner (2.0.7) unstable; urgency=medium

  * WebUI: Expand manufacturing DB API, table

 -- Tom Dewey <tom.dewey@raspberrypi.com>  Thu, 21 Aug 2025 16:30:00 +0100

rpi-sb-provisioner (2.0.6) unstable; urgency=medium

  * WebUI: Fixup SHA256 calculation cancellation
  * naked: Fixup bootloader configuation file default
  * all: Fixed up post-flash customisation script arguments
  * naked: Fix up TCP connection set-up
  * bootstrap: Drop non-secure-boot EEPROM handling

 -- Tom Dewey <tom.dewey@raspberrypi.com>  Wed, 16 Jul 2025 16:30:00 +0100

rpi-sb-provisioner (2.0.5) unstable; urgency=medium

  * WebUI: Add audit logging to images API
  * WebUI: Add firmware selection UI
  * WebUI: Asynchronous services loading
  * WebUI: Add SHA256 cancellation when images are deleted
  * WebUI: Fix CORS issues with WebUI
  * provisioner: Fix 2711 Fastboot utility OS signing

 -- Tom Dewey <tom.dewey@raspberrypi.com>  Tue, 24 Jun 2025 16:30:00 +0100

rpi-sb-provisioner (2.0.4) unstable; urgency=medium

  * packaging: Enforce modern (>= 28.2) rpi-eeprom dependency to fix nvme boot
    issues
  * manufacturing: Fix crash when provisioning NVMe devices
  * WebUI: Fix navigation bar disruption when updates are available

 -- Tom Dewey <tom.dewey@raspberrypi.com>  Tue, 10 Jun 2025 14:00:00 +0100

rpi-sb-provisioner (2.0.3) unstable; urgency=medium

  * packaging: Enforce modern (>= 20250519~100421) rpiboot dependency
  * packaging: Enforce modern (>= 27.10) rpi-eeprom dependency
  * packaging: Update Standards-Version to 4.7.2
  * provisioner: Fixup 2712 re-provisioning

 -- Tom Dewey <tom.dewey@raspberrypi.com>  Wed, 28 May 2025 17:00:00 +0100

rpi-sb-provisioner (2.0.2) unstable; urgency=medium

  * WebUI: Don't hide CUSTOMER_KEY_FILE_PEM in options
  * WebUI: Introduce naked provisioning customisation hook
  * WebUI: Customsiation: Fix script name handling

 -- Tom Dewey <tom.dewey@raspberrypi.com>  Mon, 12 May 2025 17:00:00 +0100

rpi-sb-provisioner (2.0.1) unstable; urgency=medium

  * provisioner: Fixup xxd dependency
  * provisioner: Fixup ethernet handling
  * readme: Remove manual cache clearing instructions
  * WebUI: Add manufacturing database API

 -- Tom Dewey <tom.dewey@raspberrypi.com>  Thu, 08 May 2025 11:59:55 +0000

rpi-sb-provisioner (2.0.0) unstable; urgency=medium

  * Architecture and Core Changes:
    - Split pre-and-post-Fastboot phases for better reliability
    - Add support for multiple Fastboot devices with improved
      device tracking
    - Add support for state tracking database with SQLite
    - Add WebUI for provisioning management and monitoring
    - Remove TUI (Python-based) provisioning interface

  * Provisioning Modes and Device Support:
    - Introduce FDE-Only, Naked and Secure-Boot provisioning modes
    - Add support for Zero 2W (2710) devices
    - Improve JTAG lock configuration

  * Performance Improvements:
    - Implement ethernet data transfer when available for faster
      provisioning
    - Use sparse boot images for improved performance
    - Restructure environment setup and cleanup processes
    - Enhance timeout handling with reduced fatal timeouts

  * Security and Reliability:
    - Add branch protection and other hardening measures
    - Enhance error management and logging across all provisioners
    - Add manufacturing database integration and device metadata
      collection
    - Remove DEMO_MODE from all components
    - Switch from system curl to minimal embedded curl library
    - Reduce attack surface by disabling unneeded curl features

  * UI and Configuration:
    - Add customisation scripts to the WebUI
    - Add version check and GitHub link in WebUI
    - Add Debian hardening options and build improvements
    - Removed RPI_DEVICE_FETCH_METADATA, we now always fetch metadata

  * System Integration:
    - Add systemd service integration
    - Fix lintian warnings and implement proper systemd service
      handling
    - Increase specificity in device handling and ignore rpiboot 
      mass-storage endpoint
    - Add lintian override for embedded curl library

 -- Tom Dewey <tom.dewey@raspberrypi.com>  Mon, 14 Apr 2025 11:59:55 +0000

rpi-sb-provisioner (1.3.7) unstable; urgency=medium

  * provisioner: Clean up ephemeral RPI_SB_WORKDIRs

 -- Tom Dewey <tom.dewey@raspberrypi.com>  Wed, 12 Feb 2025 15:20:00 +0000

rpi-sb-provisioner (1.3.6) unstable; urgency=medium

  * provisioner: Always sparse the rootfs for improved transfer speeds

 -- Tom Dewey <tom.dewey@raspberrypi.com>  Wed, 12 Feb 2025 15:15:00 +0000

rpi-sb-provisioner (1.3.5) unstable; urgency=medium

  * provisioner: Fixup ethernet handling
  * provisioner: Move device keypair retreival to earlier phase
  * provisioner: Fix up 64-bit serial handling
  * provisioner: Fix up multi-device provisioning

 -- Tom Dewey <tom.dewey@raspberrypi.com>  Wed, 12 Feb 2025 14:15:00 +0000

rpi-sb-provisioner (1.3.4) unstable; urgency=medium

  * provisioner: Use rpi-make-boot-image for better updates
  * provisioner: Add initial support for ethernet data plane transfer
  * packaging: Ignore .git directory for source tarball creation
  * packaging: Fixup manpage generation

 -- Tom Dewey <tom.dewey@raspberrypi.com>  Mon, 10 Feb 2025 16:15:00 +0000

rpi-sb-provisioner (1.3.3) unstable; urgency=medium

  * provisioner: Remove unsafe automatic pre-signd device retry behaviour
  * provisioner: Add special-skip-keywriter file check to handle pre-signed
      devices
  * packaging: Add autogenerated manpage
  * packaging: Fix up Homepage item in control file

 -- Tom Dewey <tom.dewey@raspberrypi.com>  Wed, 05 Feb 2025 16:45:00 +0000

rpi-sb-provisioner (1.3.2) unstable; urgency=critical

  * provisioner: Update cryptroot to address GHSA-pwq3-7gpg-qpcj
  * provisioner: Attempt automatic pre-signed device recovery on
      Rapsberry Pi 5-family devices
  * provisioner: Use rpi-eeprom-update logic to find source eeprom image

 -- Tom Dewey <tom.dewey@raspberrypi.com>  Mon, 03 Feb 2025 12:15:00 +0000

rpi-sb-provisioner (1.3.1) unstable; urgency=medium

  * provisioner: Generate depmod information for pre-boot
      authentication environment
  * provisioner: Introduce LUKSv2 cipher selection mechanism
  * provisioner: Device-unique key retreival fixes

 -- Tom Dewey <tom.dewey@raspberrypi.com>  Fri, 13 Dec 2024 11:15:00 +0000

rpi-sb-provisioner (1.3.0) unstable; urgency=medium

  * provisioner: Add optional manufacturing database
  * provisioner: Add optional device keypair archiving

 -- Tom Dewey <tom.dewey@raspberrypi.com>  Thu, 21 Nov 2024 13:57:00 +0000

rpi-sb-provisioner (1.2.1) unstable; urgency=medium

  * provisioner: Remove extraneous disk-id argument

 -- Tom Dewey <tom.dewey@raspberrypi.com>  Thu, 10 Oct 2024 13:57:00 +0000

rpi-sb-provisioner (1.2.0) unstable; urgency=medium

  * monitor: Rework finding completed & failed devices
  * monitor: Make in-progress devices selectable
  * monitor: Prune extraneous text from device names
  * provisioner: Add timeouts to commands that may hang in failure cases
  * packaging: Add missing python3-venv build dep

 -- Tom Dewey <tom.dewey@raspberrypi.com>  Tue, 08 Oct 2024 18:57:00 +0000

rpi-sb-provisioner (1.1.1) unstable; urgency=medium

  * config: Added RPI_DEVICE_FETCH_METADATA, to control device metadata
    gathering
  * packaging: Numerous lintian fixes
  * packaging: Remove automatic user group additions
  * monitor: Sort failed devices by most recent
  * monitor, config: Require sudo

 -- Tom Dewey <tom.dewey@raspberrypi.com>  Tue, 24 Sep 2024 18:57:00 +0000

rpi-sb-provisioner (1.1.0) unstable; urgency=medium

  * Add 2712 support
  * Add metadata gathering support for 2711, 2712
  * Demo mode: Expand coverage to all Fastboot commands
  * Copyright: Correct license
  * Config: Prefix DEVICE_SERIAL_STORE with RPI to match docs

 -- Tom Dewey <tom.dewey@raspberrypi.com>  Wed, 05 Sep 2024 18:57:00 +0000

rpi-sb-provisioner (1.0.2) UNRELEASED; urgency=low

  * rpi-sb-provisioner: Changed Debian Packaging

 -- Ben Benson <ben.benson@raspberrypi.com>  Fri, 23 Aug 2024 11:24:00 +0000
