Blog

Notes on Elixir, Rust, Python and the Linux command line — written while building things, mostly so I can find the answer again next year.

The engineering here is mine; some of the prose had help. How these posts get written.

All writing

39 posts
Series · 8 parts · in progress

Secure boot and a verified root filesystem on an STM32MP157F-DK2

Four links of an authenticated boot chain on an STM32MP157F-DK2 — ROM, TF-A, U-Boot FIT and dm-verity — built, signed, broken on purpose and put back, with the one-time-programmable fuses left completely blank the whole way through.

▸ show all 8 parts
  1. Why I am not burning the fuses on an STM32MP157F-DK2
  2. Four different things people call “security” on an embedded device
  3. What secure boot actually is on an STM32MP1: the header, field by field
  4. Signing BL2 and the FIP tamper test: one bit flipped, EAUTH, clean halt
  5. Signing the kernel with a FIT image, and the escape hatch that makes it theatre
  6. dm-verity on a signed rootfs, and two console messages that were simply untrue
  7. Breaking dm-verity on purpose: one bit in 52 MB, and the board refuses
  8. Moving the kernel out of the rootfs, and feeding the bootloader garbage
Series · 5 parts · in progress

Driving the display on an STM32MP157F-DK2 with Elixir and Scenic

Getting a real, touchable user interface onto the DK2's 4-inch MIPI DSI panel from Elixir on Nerves, with no GPU — the display stack, host previewing, a drift-free timer, and the Scenic gotchas that compile cleanly and fail on the device.

▸ show all 5 parts
  1. The STM32MP157F-DK2 display stack, and the Vivante GPU I did not need
  2. Previewing a Scenic UI on the host, with no screen and no board
  3. A drift-free countdown on an STM32MP157F-DK2, tested in milliseconds
  4. Scenic on Nerves: scenes, graphs, drivers, and four things that bit me
  5. nerves_system_br: what it actually is, and where your OTP version is decided
11 Sep 2026
15 min

Beyond mTLS: identity for a fleet of IoT devices

Certificates and mTLS are the easy part. The hard part is identity over a device's whole life: first enrolment, rotation, power loss mid-rotation, and a gateway that was offline for two years.

architectureelixiriotnervessecurity
03 Sep 2026
5 min

Compile-time Markdown in Phoenix, and why I reversed it

I built this blog to parse every post at compile time, wrote up why it was the right call, and then reversed it three weeks later. Both of my premises were false. Here is what replaced it, and how it keeps every property the compile-time version actually earned.

architecturebuildelixirphoenix
Series · 5 parts · complete

A Nerves system for the IOT-DIN gateway

Writing a custom Nerves base image for a CompuLab IOT-DIN-IMX8PLUS by hand: the packaging bugs, the flashing dead end, and the DDR failure that stopped it booting.

▸ show all 5 parts
  1. Authoring a Nerves system by hand
  2. It compiles. It still won't package.
  3. Flashing without the vendor's flashing tool
  4. Ran out all six config attempts
  5. Provisioning the next board
Series · 4 parts · in progress

A custom Nerves gateway on the STM32MP1

Building a Nerves system from scratch for the STM32MP157F-DK2: the boot chain, device trees, and the first attempt at a working build.

▸ show all 4 parts
  1. Why build a custom Nerves gateway from scratch
  2. The STM32MP1 boot chain, from power-on to Linux
  3. Device trees demystified: reading the hardware map
  4. Building the Nerves system: the first attempt
Series · 5 parts · in progress

Secure boot on Nerves, on a CompuLab IOT-DIN-IMX8PLUS

Carrying everything learned on the lab unit into a real, purpose-built Nerves system — without touching the build running in the field.

▸ show all 5 parts
  1. From a lab unit to a production Nerves system on the IOT-DIN-IMX8PLUS
  2. Forking the Nerves system, and enabling HAB inside a Buildroot build
  3. Signing the Nerves kernel on i.MX8M Plus, clean on the first try
  4. Making the Nerves build sign imx-boot and the kernel automatically
  5. It boots signed — and the HAB gate in booti I did not know I had
Series · 10 parts · complete

Secure boot on a CompuLab IOT-DIN-IMX8PLUS (NXP i.MX8M Plus)

Adding proper secure boot to an industrial IoT gateway, on a disposable lab unit, making every mistake where it costs nothing.

▸ show all 10 parts
  1. The hardware: a CompuLab IOT-DIN-IMX8PLUS, and how to confirm what you actually have
  2. The HABv4 key hierarchy: SRK, CSF, IMG and the fuses
  3. Generating HAB keys with NXP's Code Signing Tool (CST 4.0.1)
  4. Backing up the eMMC: boot partitions, rootfs, and the force_ro trap
  5. Pinning the U-Boot build environment for the IOT-DIN-IMX8PLUS
  6. Building an unsigned imx-boot for the IOT-DIN-IMX8PLUS
  7. The board stopped booting: LPDDR4 training, and recovering an IOT-DIN-IMX8PLUS over USB
  8. Signing imx-boot and turning on HAB on the i.MX8M Plus
  9. Signing the kernel, and a HAB failure the chip caught
  10. The SRK fuse and closing the device: what is left, and why I stopped