Spore pairs a small, portable solver with SSC (Spore System Configuration), a declarative language for describing which combinations of characteristics a device can legally have. Every value is either committed — a user pick, a policy default, or a live sensor reading — or derived: a domain narrowed by constraints and recomputed fresh on every solve, so sensor input, human input, and inference never fight each other. The solver never touches I/O itself. It decides which combination is legal and what must follow; sensors, actuators, and the UI sit in a thin host around it. It answers legality, not price, BOM, or routing.
The name: a spore is small and self-contained, dormant until it lands somewhere and germinates unmodified - the same idea as one portable engine.c.
engine.c is plain, allocation-free C11. The same file compiles with gcc for a PC, emcc for WebAssembly in the browser, avr-gcc for a microcontroller — a real ATtiny1614 with 2 KB of RAM, 800-byte heap — and the ESP-IDF toolchain for an ESP32-C3.
A characteristic is either committed (a user pick, a policy default, or a sensor reading) or derived (a domain narrowed by constraints and recomputed fresh on every solve). The split keeps sensor input, human input, and inference from ever fighting each other.
A slot marked observed is a live reading — written with no admission gate, because the world cannot be illegal. applied is the opposite direction: the host reads the solver’s answer and drives a relay, LED, or PWM. Unmarked slots are user or policy input.
A characteristic declares a typed slot (SINGLE, MULTI, or RANGE). A class groups them. Constraints narrow what is legal; procedures write values; preconditions hide or require a slot; a variant table is a row-wise fact instead of a hand-written condition.
characteristic MODE { names EN "Mode" textLength 8 values 'ECO' names EN "Eco", 'COMFORT' names EN "Comfort", 'BOOST' names EN "Boost" } class Thermostat { characteristics TEMP observed, MODE defaultValues ['ECO'], FAN noinput } constraint fan_high_CS { objects: ?t is_a Thermostat condition: ?t.MODE = 'BOOST' restrictions: ?t.FAN in ['HIGH'] }
On a microcontroller, the compiled .kb.img lives in its own flash slot, separate from the firmware - the solver and its host code don't change when what a device is allowed to configure does. A board only needs flashing once; after that, changing its legal combinations is a KB upload over UART (KB_BEGIN/KB_DATA/KB_END framing, acknowledged chunk by chunk), not a firmware rebuild.
The whole engine is five small, dependency-free C files: engine.c and kb_loader.c, plus the headers they need (engine.h, format.h, kb_schema.h) - the same source this site's own configurator demo compiles to WebAssembly. Also included: a minimal main.c that loads a compiled .kb.img from the command line and prints the settled configuration.
Want the SSC compiler itself (the grammar and kb-emit)? It's not public yet - email mi@mipem.co.