Skip to main content

production-deployment

The integration flagship — a field-deployable IoT product that frames factory provisioning → secure boot → OTA → remote attestation in one app. Every other example covers one library surface; this one covers the production lifecycle that ties them all together.

Source: examples/connectivity/production-deployment/.

:::warning This is a lifecycle skeleton, not a working product The example ships the shape of a production app — the declarative board.yaml stance and the stage sequencing — not a verified end-to-end implementation. Under native_sim each stage reaches its open() call, observes the documented NOSUPPORT / NOT_READY return, prints the transition, and proceeds; the framing is what the test covers. The substantive verification gates on HiL. Treat it as a starting skeleton you fork, not as evidence any stage is proven. :::

Lifecycle stages

  1. Factory provisioning read-back. On boot, <alp/hw_info.h> reads the EEPROM manifest programmed at factory test. Production firmware treats this as the authoritative per-unit identity (SKU, serial, HW revision, factory date).
  2. Secure-boot attestation. MCUboot verifies the application's ECDSA-P256 signature in slot0_partition before chain-loading it, then the application reports back the slot it's running from and the signature key ID. On AEN the verify step is bench-proven on real silicon — see Building the MCUboot chain on AEN for the measured result and the mandatory SB_CONF_FILE argument.
  3. Application work. Runs whatever the device actually does — for the example, a small connected sensor reporting over MQTT with TLS.
  4. OTA receive + verify. Updates are pulled via Mender (Yocto) or signed MCUboot images (Zephyr); both paths go through <alp/security.h> for signature verification before slot swap.
  5. Remote attestation. Intended stage: periodically sign a freshness nonce with the on-module OPTIGA Trust M and publish the signature alongside the EEPROM manifest data, so a back-end can prove the running firmware matches the provisioned identity. Not available today — the OPTIGA Trust M driver is probe-only: it confirms the part responds, but signing, key generation, and raw APDUs all return ALP_ERR_NOSUPPORT until the Infineon host-library transport is integrated. The example's source calls no signing API; security.psa.attestation_root: optiga_trust_m declares the intended trust root, it does not implement one.

board.yaml

Every declarative block applied at production stance on the AEN801 — the security-target SoM (dual-Cortex-M55 with on-module OPTIGA Trust M + TF-M secure-boot fabric):

libraries:
- name: mbedtls # required by iot.tls: true (validator R3)
cores: [m55_hp]

som:
sku: E1M-AEN801

preset: e1m-evk

cores:
m55_hp:
app: ./src
peripherals: [i2c, gpio]
iot: { wifi: true, mqtt: true, tls: true }
memory:
stack_kib: 8
heap_kib: 64
isr_stack_kib: 2
power:
sleep_mode: standby # disabled | idle | standby | deep
wakeup_sources: [uart, gpio]

boot:
method: mcuboot
signing:
algorithm: ecdsa_p256 # AEN family allow-list (validator R2)
key_file: keys/mcuboot_dev_ecdsa_p256.pem
swap_algorithm: scratch
build_type: Release # propagates to MCUboot + TF-M sysbuild

ota:
provider: mender # provider-driven dispatch (ADR 0009)
artifact_name: alp-prod-1.0.0
signing_key: keys/ota.pub.pem
server:
url: https://hosted.mender.io
tenant: ${MENDER_TENANT_TOKEN}
poll_interval_s: 1800

storage:
- name: app_data
size_kib: 256
fs: littlefs
mount: /lfs/app
flash_device: mram_main # references the SoM preset's memory_map: region

security:
psa:
persistent_slots: 16
its_storage: mram_secure # SoM memory_map: region
ps_storage: app_data # OR a storage[].name from above
tfm: true
attestation_root: optiga_trust_m # rejected if the SoM doesn't ship OPTIGA

chips:
- optiga_trust_m
- eeprom_24c128

diagnostics:
log_level: info
modules:
alp_iot: warn
alp_security: debug

:::danger boot.signing.key_file is the private signing key key_file: becomes SB_CONFIG_BOOT_SIGNATURE_KEY_FILE — the key imgtool signs with, and from which MCUboot extracts the public half. It must be a private PEM; a .pub.pem there cannot sign. keys/mcuboot_dev_ecdsa_p256.pem is the local development key, matching zephyr/sysbuild/aen/sysbuild.conf; it is gitignored and generated once per clone by keys/generate_dev_key.sh. Despite this example's name, a production image is re-signed in the manufacturing CA's HSM and never uses a build-time key from the repo.

ota.signing_key: is the opposite half and correctly stays a .pub.pem — it verifies incoming OTA artefacts, it does not sign them. :::

For V2N targets, switch som.sku to E1M-V2N101, the board preset: to e1m-x-evk, and add an a55_cluster: core with image: alp-image-edge; the application surfaces are identical. Mender stays the same — provider: mender now accepts either a Yocto or a Zephyr core per ADR 0009.

Build

cd alp-workspace
tan --project alp-sdk/examples/production-deployment build
tan --project alp-sdk/examples/production-deployment flash

Building the MCUboot chain on AEN

The shared AEN MCUboot profile lives at zephyr/sysbuild/aen/sysbuild.conf and now ships real signature verification:

SB_CONFIG_BOOTLOADER_MCUBOOT=y
SB_CONFIG_BOOT_SIGNATURE_TYPE_ECDSA_P256=y
SB_CONFIG_BOOT_SIGNATURE_KEY_FILE="keys/mcuboot_dev_ecdsa_p256.pem"
SB_CONFIG_MCUBOOT_MODE_SINGLE_APP=y

The MCUboot child image that comes out of it carries CONFIG_BOOT_VALIDATE_SLOT0=y, so the bootloader verifies the application's ECDSA-P256 signature in slot0 before chain-loading it. This is bench-proven on real E1M-AEN801 silicon (AE822FA0E5597LS0 Rev A0), not inferred: verification completes with PC=80012FBC, VTOR=80010800, and flipping a single byte of the TLV 0x22 signature produces E: Unable to find bootable image — a clean refusal, not a hang. The verified backend is TinyCrypt (CONFIG_BOOT_ECDSA_TINYCRYPT=y).

The signing key is no longer bundled in an example's own sysbuild.conf. You must point the build at the shared profile explicitly, which is what supplies SB_CONFIG_BOOT_SIGNATURE_KEY_FILE:

-DSB_CONF_FILE=<abs>/alp-sdk/zephyr/sysbuild/aen/sysbuild.conf

The path must be absolute — sysbuild resolves a relative SB_CONF_FILE against APP_DIR, not your shell's working directory. Building without it now hard-fails at configure time instead of silently resolving a stale relative path.

:::warning No A/B swap on AEN SB_CONFIG_MCUBOOT_MODE_SINGLE_APP=y (i.e. CONFIG_SINGLE_APPLICATION_SLOT=y on the MCUboot child image) is unchanged: MCUboot boots slot0_partition only and manages no secondary or scratch slot. Every measurement on this part to date ran single-slot, so the swap_algorithm: scratch declared above buys you nothing on AEN today — swap-using-scratch's rollback-to-previous-slot and mid-swap power-loss recovery are untested on E8. Do not rely on A/B or on-device rollback here. :::

What it ties together

SDK surfaceStage
<alp/hw_info.h>Stage 1 — factory manifest read
MCUboot + <alp/security.h>Stage 2 — boot attestation
<alp/iot.h> + <alp/security.h>Stage 3 — application traffic
MCUboot signed slot swap / MenderStage 4 — OTA
<alp/chips/optiga_trust_m.h>Stage 5 — attestation signing

See also

Questions about this page? Discuss in Community Forum