Skip to content

ESPM Overview

ESPM (the Espilon Portable Module system) is the dynamic code loader at the heart of the platform. It turns a hollow-shell ESP32 firmware into a runtime-extensible system: every offensive, recon, or utility capability arrives as a signed, relocatable ELF object pushed over the encrypted Management Protocol channel, loaded into RAM, executed, and erased. Nothing capability-bearing lives in flash at rest.

ESPM is not a git submodule

ESPM lives at espilon-test/ESPM/ and is copied into the firmware at Espilon-Firmware/components/espm/. Older documentation that called it a submodule is stale.

At a glance

Property Value
Version v0.2.0
Syscalls 102, grouped in ~20 categories (see Syscall Reference)
Module format Relocatable ELF object (ET_REL), Xtensa or RISC-V
Signature HMAC-SHA256, 32-byte tag prepended to the ELF
Execution One FreeRTOS task per module, runs from IRAM (or PSRAM on S3)
Isolation Per-module watchdog (30 s default) + Xtensa panic isolation
Concurrency Up to 8 modules (ESPM_MAX_MODULES)
Size limits 64 KB code, 32 KB data per module

What ESPM gives you

  • A stable syscall ABI. Modules call firmware services (sockets, WiFi, BLE, GPIO, crypto, C2 messaging) through a function-pointer table (espm_sys_t) rather than linking against ESP-IDF. A module is a plain C file that includes one header.
  • Architecture-aware loading. The loader applies Xtensa or RISC-V relocations at load time, so the same source compiles for any supported chip with the right toolchain.
  • Strong isolation. Each module runs in its own task with an independent watchdog. On Xtensa, a faulting module is killed without taking the device down (panic isolation). On RISC-V the device reboots.
  • No persistence by default. Module memory (IRAM/PSRAM) is zeroed on unload. A power cycle leaves no trace unless a module was explicitly persisted to encrypted NVS.

Architecture

flowchart LR
    OP["C3PO operator"] -->|"compile + sign"| BLOB["[32B HMAC][ELF]"]
    BLOB -->|"mod_chunk x N + mod_load (C2)"| DEV
    subgraph DEV["ESP32 agent"]
        V["Verify HMAC"] --> R["Relocate ELF"]
        R --> L["Load to IRAM/PSRAM"]
        L --> T["FreeRTOS task<br/>espm_init -> espm_exec"]
        T --> WD["Watchdog + panic isolation"]
    end

Where to go next