- Overview
- Component Architecture
- Provisioned System Architecture
- Data Flow
- Design Rationale
- Security Considerations
- Extensibility
- Deployment Considerations
- Troubleshooting Architecture
- Summary
Technical overview of the rpi-sb-provisioner system design
The rpi-sb-provisioner is designed around a core principle: transparency of action. Every operation is explicit, auditable, and uses standard Linux tools and interfaces. The system is built from composable components that can be understood, inspected, and maintained independently.
The architecture prioritizes:
-
Transparency: All operations are visible through standard Linux tools (systemd, journalctl, filesystem)
-
Auditability: Comprehensive logging at every stage; operations can be traced and verified
-
Composability: Separate concerns into distinct components that communicate through well-defined interfaces
-
Simplicity: Prefer shell scripts and standard tools over complex frameworks
-
Security: Minimize attack surface; operations run with minimal necessary privileges
-
Maintainability: Code should be readable by system administrators, not just developers
The system consists of five main components:
| Component | Purpose | Implementation |
|---|---|---|
udev Rules |
Detect device connections and trigger provisioning |
udev rule file |
systemd Services |
Orchestrate provisioning workflow phases |
systemd template units |
Shell Scripts |
Execute provisioning operations |
POSIX shell scripts |
Web Service |
Provide monitoring and configuration interface |
C++ (Drogon framework) |
Configuration |
Store provisioning parameters and state |
Filesystem-based |
The provisioning workflow begins with device detection.
Two rules detect Raspberry Pi devices in different boot states:
Rule 1: RPIBOOT mode detection
ACTION=="add", SUBSYSTEM=="usb",
ATTRS{idVendor}=="0a5c", ATTR{idProduct}=="2764|2711|2712",
TAG+="systemd",
PROGRAM="/usr/bin/systemd-escape -p --template=rpi-sb-bootstrap@.service $env{DEVNAME}",
ENV{SYSTEMD_WANTS}+="%c"
Triggers when: Raspberry Pi device connects in RPIBOOT mode (bootloader USB mode)
Action: Start rpi-sb-bootstrap@.service with the device path as parameter
Product IDs:
* 2764 - Raspberry Pi 3 family devices (Zero 2 W)
* 2711 - Raspberry Pi 4 family devices (Raspberry Pi 4, Compute Module 4)
* 2712 - Raspberry Pi 5 family devices (Raspberry Pi 5, Compute Module 5)
Rule 2: fastboot mode detection
ACTION=="add", SUBSYSTEM=="usb",
ATTRS{idVendor}=="18d1", ATTR{idProduct}=="4e40",
ATTR{manufacturer}=="Raspberry Pi"
TAG+="systemd",
PROGRAM="/usr/bin/systemd-escape -p --template=rpi-sb-triage@.service $attr{serial}",
ENV{SYSTEMD_WANTS}+="%c"
Triggers when: Device has been bootstrapped and enters fastboot mode
Action: Start rpi-sb-triage@.service with the device serial number as parameter
-
Automatic triggering: No manual intervention required; plugging in a device starts provisioning
-
systemd integration: Leverages systemd’s service management, logging, and monitoring
-
Device isolation: Each device gets its own service instance (systemd template units)
-
Standard interface: Uses udev, the standard Linux device management system
Provisioning proceeds through three phases, each implemented as a systemd template service.
Purpose: Initialize device, program security keys, update firmware
Triggered by: udev rule detecting RPIBOOT mode
Key operations: * Load recovery firmware into device via rpiboot * For secure boot mode: Program signing key hash into device OTP memory * Update device EEPROM firmware * Boot device into fastboot mode
Script: /usr/bin/rpi-sb-bootstrap.sh
Logging: /var/log/rpi-sb-provisioner/<serial>/bootstrap.log
State tracking: /var/run/rpi-sb-state/<serial>/bootstrap
Purpose: Determine provisioning mode and start appropriate provisioner
Triggered by: udev rule detecting fastboot mode
Key operations:
* Read configuration to determine PROVISIONING_STYLE
* Verify device is in fastboot mode
* Start appropriate provisioning service:
- secure-boot → rpi-sb-provisioner@.service
- fde-only → rpi-fde-provisioner@.service
- naked → rpi-naked-provisioner@.service
Script: /usr/bin/rpi-sb-triage.sh
Logging: /var/log/rpi-sb-provisioner/<serial>/triage.log
State tracking: /var/run/rpi-sb-state/<serial>/triage
Purpose: Deploy operating system and configure security
Three variants implement different security models:
Secure Boot Provisioner (rpi-sb-provisioner@.service)
-
Create device-unique encryption key and store in OTP memory
-
Partition and format storage
-
Create LUKS2 encrypted container
-
Deploy OS image into encrypted container
-
Create pre-boot authentication environment (kernel + minimal modules)
-
Sign EEPROM bootloader and pre-boot environment with customer key
-
Configure secure boot chain
Full Disk Encryption Provisioner (rpi-fde-provisioner@.service)
-
Create device-unique encryption key and store in OTP memory
-
Partition and format storage
-
Create LUKS2 encrypted container
-
Deploy OS image into encrypted container
-
Create pre-boot authentication environment (unsigned)
Naked Provisioner (rpi-naked-provisioner@.service)
-
Partition and format storage
-
Deploy OS image directly (no encryption)
-
Install standard boot firmware (uses Raspberry Pi retail key)
Scripts:
* /usr/bin/rpi-sb-provisioner.sh
* /usr/bin/rpi-fde-provisioner.sh
* /usr/bin/rpi-naked-provisioner.sh
Logging: /var/log/rpi-sb-provisioner/<serial>/provisioner.log
State tracking: /var/run/rpi-sb-state/<serial>/provisioner
-
Phase separation: Each phase has clear responsibilities and can be debugged independently
-
Service isolation: systemd ensures proper resource management and cleanup
-
Parallel execution: Multiple devices can be provisioned simultaneously
-
Standard monitoring: Use
systemctlandjournalctlfor service inspection -
Transparent state: State files allow external monitoring without querying the service
Core provisioning logic is implemented in POSIX shell scripts.
Common functions (rpi-sb-common.sh)
Shared utilities used by all phases:
-
Configuration reading (
read_config) -
Logging functions
-
Device information extraction
-
USB path resolution
-
Error handling
Phase-specific scripts
Each phase has a dedicated script implementing its operations:
-
Entry point receives device identifier from systemd
-
Sources common functions
-
Implements phase-specific logic
-
Updates state files
-
Records manufacturing data
-
Returns exit code to systemd
-
Transparency: Operations are explicit command invocations, easy to trace
-
Debuggability: Can be run manually for testing and troubleshooting
-
Standard tools: Uses openssl, cryptsetup, fastboot, and other standard utilities
-
Inspectability: System administrators can read and understand the code
-
Portability: POSIX shell is universally available on Linux systems
-
Auditability: Every command is visible in logs with
set -x
A web service provides monitoring, configuration, and API access.
Technology: C++ using Drogon web framework
Purpose: * Web-based configuration interface * Real-time device monitoring * Manufacturing database access * REST API for integration * WebSocket support for live updates
HTTP endpoints:
-
/options/*- Configuration management -
/devices/*- Device status and logs -
/api/v2/services- Service monitoring -
/api/v2/manufacturing- Manufacturing database queries -
/customisation/*- Provisioning script management -
/get-images- OS image management
WebSocket endpoints:
-
/ws/devices- Real-time device topology updates -
/ws/sha256- Live hash calculation progress
Views: Server-side templates (CSP files compiled to C++)
Data sources:
-
systemd API (D-Bus) - Service status
-
Filesystem - Logs, state files, configuration
-
SQLite - Manufacturing database, audit log
-
Separation of concerns: Web service only monitors; shell scripts do the work
-
No privilege escalation: Web service runs as unprivileged user; uses sudo only for specific read operations
-
Standard protocols: HTTP REST API and WebSockets for integration
-
Stateless monitoring: Service reads state from filesystem; does not maintain critical state
-
Fail-safe: If web service crashes, provisioning continues unaffected
Simple key-value file format:
PROVISIONING_STYLE=secure-boot RPI_DEVICE_FAMILY=5 RPI_DEVICE_STORAGE_TYPE=nvme CUSTOMER_KEY_FILE_PEM=/path/to/key.pem GOLD_MASTER_OS_FILE=/path/to/image.img
Read by: All shell scripts via read_config function
Modified by: Web service or manual editing
Why: Simple, transparent, version-controllable, easy to backup
Directory structure:
/var/run/rpi-sb-state/
<serial>/
bootstrap # Bootstrap phase state
triage # Triage phase state
provisioner # Provisioning phase state
State files contain: * Phase status (STARTED, FINISHED, ABORTED) * Timestamps * USB path information
Purpose: Allow web service and external tools to monitor progress without interfacing with systemd
Why tmpfs: State is ephemeral; cleared on reboot; should not persist
Directory structure:
/var/log/rpi-sb-provisioner/
<serial>/
bootstrap.log # Bootstrap phase operations
triage.log # Triage phase operations
provisioner.log # Provisioning phase operations
keypair/ # Device-unique keys (if enabled)
<serial>.der # Private key
<serial>.pub # Public key
Log format: Timestamped entries with explicit command output
Retention: Persists across reboots; managed by logrotate
Why: Comprehensive audit trail; troubleshooting; compliance
Per-device lock directories prevent race conditions:
/var/lock/rpi-sb-provisioner/ <serial>/
Acquired by: Each phase before starting operations
Released by: Phase completion or failure cleanup
Why: Prevents duplicate provisioning if device reconnects during process
Optional database recording device information.
Location: Configurable (typically /var/lib/rpi-sb-provisioner/manufacturing.db)
Schema: Single table with device attributes: * Serial number * Board type and revision * MAC addresses (Ethernet, Wi-Fi, Bluetooth) * Storage information * Security configuration * Provisioning timestamp * OS image details
Purpose: Track provisioned devices for inventory, warranty, and compliance
After provisioning is complete, the device has a specific security architecture depending on the provisioning mode.
In secure boot mode, the device implements a complete authenticated boot chain with encrypted storage.
Boot partition structure:
Partition 1 contains two special files:
-
boot.img: A FAT32 filesystem image file containing all boot components (firmware, kernel, initramfs, pre-boot authentication environment). The EEPROM bootloader mounts this image to access boot files. -
boot.sig: Cryptographic signature forboot.img, signed with customer private key. The EEPROM bootloader verifies this signature before mountingboot.img.
This encapsulation ensures the entire boot environment is authenticated as a single unit.
The boot process implements cryptographic verification at each stage:
Key security properties:
-
Authenticated boot chain: Each component cryptographically verifies the next before execution
-
Customer key in hardware: Key hash burned into device OTP memory (permanent, cannot be changed)
-
Signed components: Only components signed with customer private key will execute
-
Encrypted storage: All OS data protected by LUKS2 encryption
-
Device-unique keys: Each device has unique encryption key stored in OTP memory
-
Automatic unlock: Pre-boot environment retrieves decryption key from OTP memory; no user interaction required
-
Minimal pre-boot environment: Contains only kernel and modules necessary for LUKS unlock and pivot_root
What this prevents:
-
Unauthorized code execution: Device will not execute code not signed with customer key
-
Storage extraction: Removing storage and reading it on another system yields only encrypted data
-
Key extraction from storage: Decryption key is stored in device OTP memory, not on storage device
-
Device cloning: Decryption keys are unique per device and stored in OTP; cannot clone storage to another device
-
Boot chain tampering: Any modification to EEPROM bootloader or pre-boot environment will fail signature verification
What this requires:
-
Secure key management: Customer private key must be protected (used for signing)
-
Trust in device hardware: Security depends on device OTP memory and secure key storage
FDE mode provides encrypted storage without secure boot enforcement.
Boot partition structure:
Partition 1 contains:
-
boot.img: A FAT32 filesystem image file containing boot components (pre-boot authentication environment, kernel, initramfs). The EEPROM bootloader mounts this image to access boot files. -
No signature file: Unlike secure boot mode, there is no
boot.sigfile. Theboot.imgis loaded without verification.
The storage layout is similar to secure boot mode (boot partition + LUKS2 container), but without boot authentication.
Key differences from secure boot mode:
-
Raspberry Pi retail key: EEPROM bootloader authenticated with Raspberry Pi Ltd retail key (not customer key)
-
Firmware substitution: Any official Raspberry Pi firmware release can be substituted
-
No pre-boot verification: Pre-boot authentication environment loaded WITHOUT signature verification
-
Encryption only: Storage is encrypted, but pre-boot environment can be modified
-
Same unlock mechanism: Uses OTP-stored decryption key and pivot_root
Use case: Scenarios requiring encryption without full boot chain authentication restrictions.
Naked mode provides basic OS deployment without security features.
Boot partition structure:
Partition 1 contains boot files directly (traditional Raspberry Pi layout):
-
Standard boot firmware files
-
Kernel and device tree files
-
No encapsulation in
boot.img -
No signature files
Device Powers On → Bootloader → Firmware → Kernel → Your OS
Characteristics:
-
No encryption: Storage contents readable if device is extracted
-
EEPROM authenticated: EEPROM bootloader uses Raspberry Pi retail key authentication
-
Kernel not authenticated: No signature verification of kernel or boot files
-
Standard boot: Conventional Raspberry Pi boot process
-
Fastest provisioning: No encryption operations during deployment
Use case: Development environments, non-security-critical deployments, maximum compatibility.
| Feature | Secure Boot | FDE Only | Naked |
|---|---|---|---|
EEPROM authentication |
✓ Customer key |
✓ RPi retail key |
✓ RPi retail key |
Pre-boot environment auth |
✓ Customer key |
✗ No |
N/A |
Storage encryption |
✓ Yes (LUKS2) |
✓ Yes (LUKS2) |
✗ No |
Device-unique keys |
✓ Yes |
✓ Yes |
✗ No |
Customer firmware signing |
✓ Yes |
✗ No |
✗ No |
OTP customer key |
✓ Yes (permanent) |
✗ No (RPi retail) |
✗ No (RPi retail) |
OTP encryption key |
✓ Yes |
✓ Yes |
✗ No |
Storage extractable |
✗ No (encrypted) |
✗ No (encrypted) |
✓ Yes (unencrypted) |
Firmware substitution |
✗ No (customer-signed only) |
✓ Yes (any RPi firmware) |
✓ Yes (any RPi firmware) |
Pre-boot environment modifiable |
✗ No (signature verification) |
✓ Yes (no verification) |
N/A |
Provisioning time |
Longest |
Medium |
Fastest |
Security level |
Maximum |
Medium |
Minimal |
Use case |
Production devices |
Encryption without boot restrictions |
Development, testing |
1. Device connects in RPIBOOT mode
↓
2. udev detects connection
↓
3. udev starts rpi-sb-bootstrap@<device>.service
↓
4. Bootstrap script:
- Acquires device lock
- Loads device via rpiboot
- Programs security keys (if secure-boot)
- Updates EEPROM firmware
- Boots device into fastboot mode
- Updates state: BOOTSTRAP-FINISHED
↓
5. Device reconnects in fastboot mode
↓
6. udev detects fastboot device
↓
7. udev starts rpi-sb-triage@<serial>.service
↓
8. Triage script:
- Reads configuration
- Determines PROVISIONING_STYLE
- Starts appropriate provisioner service
- Updates state: TRIAGE-FINISHED
↓
9. Provisioner service starts
↓
10. Provisioner script:
- Partitions storage
- Creates encryption (if enabled)
- Deploys OS image
- Configures boot firmware
- Records manufacturing data
- Updates state: PROVISIONER-FINISHED
- Powers down device
↓
11. Device LEDs turn off
↓
12. Provisioning completeBootstrap phase: Device-specific hardware operations that must happen before storage is accessible
Triage phase: Configuration-dependent routing to appropriate provisioner
Benefit: Bootstrap can be tested independently of provisioning; triage logic is simple and fast
Transparency: Every operation is a visible command invocation
Debuggability: Can be executed manually with set -x to see exactly what happens
Maintainability: System administrators can read and modify scripts without recompilation
Auditability: Logs show exact commands executed; no hidden operations
Trust: Users can inspect code to verify no unwanted operations occur
Isolation: Each device gets independent service instance
Resource management: systemd handles process lifecycle, logging, and cleanup
Monitoring: Standard systemctl and journalctl commands work
Dependency management: Service ordering and dependencies are explicit
Transparency: State is visible in filesystem; no hidden state
Simplicity: No database or complex state management needed
Integration: External tools can monitor state by reading files
Durability: Logs persist; ephemeral state is in tmpfs
Security: Web service runs with minimal privileges; cannot disrupt provisioning
Reliability: Provisioning continues if web service fails
Maintainability: Web UI can be updated independently of core provisioning logic
API access: Provides programmatic interface for integration without modifying core scripts
-
Bootstrap/provisioning scripts: Run as root (required for device access and storage operations)
-
Web service: Runs as unprivileged user; uses sudo only for specific read operations
-
Configuration: Protected by filesystem permissions
-
Signing keys: Stored with restricted permissions; never exposed via API
-
No network services on provisioned devices: All communication via USB
-
No persistent state on provisioned devices during provisioning: Device powers down after completion
-
Minimal dependencies: Uses standard Linux tools; reduces potential vulnerabilities
-
Explicit operations: No hidden functionality; all actions logged
-
Create new shell script (e.g.,
rpi-custom-provisioner.sh) -
Add systemd service file (e.g.,
rpi-custom-provisioner@.service) -
Modify triage script to recognize new
PROVISIONING_STYLEvalue -
Script implements provisioning logic using common functions
Customisation scripts can be installed at various hook points:
-
bootstrap-begin- Before bootstrap operations -
bootstrap-end- After bootstrap completion -
mount-os- After OS filesystem is mounted, before unmount -
post-flash- After image flashing, before device power-down
Hooks receive environment variables with device information and can modify mounted filesystems.
-
Platform: Raspberry Pi hardware (ARM64 architecture)
-
Operating system: Raspberry Pi OS Bookworm or later
-
Storage: NVMe SSD recommended for image hosting
-
Memory: Sufficient for image caching (depends on OS image size)
-
Horizontal: Multiple provisioning servers, each handling subset of devices
-
Vertical: Single server with multiple USB hubs for parallel provisioning
-
Database: SQLite adequate for single server; export and merge for multi-server deployments
Critical data to backup:
-
Customer signing keys (
CUSTOMER_KEY_FILE_PEM) -
Configuration file (
/etc/rpi-sb-provisioner/config) -
Manufacturing database (if enabled)
-
OS master images
Not needed for backup:
-
Logs (useful for troubleshooting but not required for operation)
-
State files (ephemeral; recreated during provisioning)
-
Cache directories (recreated as needed)
If provisioning fails, check in order:
-
udev rules:
udevadm test /sys/…- Verify rule matches -
systemd services:
systemctl status rpi-sb-*- Check service state -
Shell scripts: Logs in
/var/log/rpi-sb-provisioner/<serial>/- Examine command output -
State files:
/var/run/rpi-sb-state/<serial>/- Verify phase transitions
Device not detected:
-
Check udev rules are installed:
/lib/udev/rules.d/60-rpi-sb-provisioner.rules -
Verify USB connection and device state
-
Test with
lsusbto confirm device is visible
Service not starting:
-
Check systemd service files are installed:
/lib/systemd/system/rpi-sb-*.service -
Verify scripts are executable:
/usr/bin/rpi-sb-*.sh -
Check for lock directory conflicts:
/var/lock/rpi-sb-provisioner/
Provisioning fails:
-
Examine logs:
/var/log/rpi-sb-provisioner/<serial>/*.log -
Check configuration:
/etc/rpi-sb-provisioner/config -
Verify image file exists and is readable
The rpi-sb-provisioner architecture prioritizes transparency, auditability, and maintainability. By using standard Linux components (udev, systemd, shell scripts) and explicit operations, the system ensures that users can understand, monitor, and trust the provisioning process. The separation of concerns between device detection, workflow orchestration, provisioning operations, and user interface allows each component to be tested, debugged, and maintained independently.
For implementation details, see:
-
Configuration Reference - All configuration options
-
API Documentation - Web service integration
-
Device Guidance - Device-specific procedures
-
Scaling Guide - Production deployment