Module Injection¶
Injecting a module is the core operator workflow: C3PO compiles a C module for the target chip, signs it with that device's key, and streams it onto the live agent. Nothing is written to the device's flash. This page documents what happens under the hood; you drive it from the Modules view.
The flow¶
flowchart LR
SEL["Select module + target device"] --> CC["compile_module()<br/>chip toolchain"]
CC --> ELF["relocatable ELF"]
ELF --> SG["sign_module()<br/>HKDF + HMAC-SHA256"]
SG --> BLOB["[32B HMAC][ELF]"]
BLOB --> INJ["ModuleInjector"]
INJ -->|"mod_chunk x N (1 KB)"| DEV["agent"]
INJ -->|"mod_load"| DEV
DEV --> RUN["verify -> relocate -> run"]
1. Compile¶
c3po/modules/compiler.py:compile_module() selects the toolchain from the target chip and compiles the module's entry .c to a relocatable object:
| Chip | Toolchain |
|---|---|
| esp32 | xtensa-esp32-elf-gcc |
| esp32s2 | xtensa-esp32s2-elf-gcc |
| esp32s3 | xtensa-esp32s3-elf-gcc |
| esp32c3 / esp32c6 / esp32h2 | riscv32-esp-elf-gcc |
Flags are -c -Os -fno-common plus -mlongcalls -mtext-section-literals (Xtensa) or -march=rv32imc -mabi=ilp32 (RISC-V). The ESPM include/ directory is on the include path, and -DCONFIG_DEVICE_ID is defined. Compilation runs off the UI thread (asyncio.to_thread).
2. Sign¶
c3po/modules/signer.py:sign_module() derives a signing key from the device's master_key and prepends an HMAC tag:
sign_key = HKDF(master_key, key_len=32, salt=None, hashmod=SHA256, context=b"espm-sign")
blob = HMAC_SHA256(sign_key, elf) + elf # [32B HMAC][ELF]
The blob is bound to the device whose key signed it.
3. Inject¶
c3po/modules/injector.py:ModuleInjector transfers the blob:
- Split it into 1024-byte chunks.
- Send each as
mod_chunk <name>withbinary_data,binary_offset, andbinary_total. - Send
mod_load <name> <core_id> 30000 [persist]. - Wait up to 30 seconds for the load to complete (tracked by
CommandTracker).
On the device, mod_load verifies the HMAC, relocates the ELF for the chip, allocates IRAM (or PSRAM on the S3), and starts the module task. Progress and any errors stream into the C3PO log.
Toolchain requirements¶
Install the cross-compiler for each chip you target. For RISC-V chips:
Xtensa chips use the xtensa-esp32[-s2|-s3]-elf-gcc compilers from an ESP-IDF install. If a toolchain is missing, the compile step fails with a clear error.