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
- 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). - Secure-boot attestation. MCUboot verifies the application's ECDSA-P256 signature in
slot0_partitionbefore 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 mandatorySB_CONF_FILEargument. - Application work. Runs whatever the device actually does — for the example, a small connected sensor reporting over MQTT with TLS.
- 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. - 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_NOSUPPORTuntil the Infineon host-library transport is integrated. The example's source calls no signing API;security.psa.attestation_root: optiga_trust_mdeclares 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 surface | Stage |
|---|---|
<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 / Mender | Stage 4 — OTA |
<alp/chips/optiga_trust_m.h> | Stage 5 — attestation signing |