RFC: embed the build configuration in the identify dictionary

A Klipper .config lives in one file at the repo root, overwritten every time you run make menuconfig for a different board. Once firmware is flashed there’s no record of how it was produced. Anyone running multiple boards runs into this eventually: a board is running firmware that is hard to reproduce, there’s no way to confirm a restored .config matches what’s running. Any tool that wants to rebuild firmware for an existing board has to ask a human first.

Proposal

Have firmware report its own build configuration, in minimal savedefconfig form, through the identify dictionary it already serves over the wire. One new key, kconfig:

"kconfig": {
  "defconfig": "CONFIG_MACH_STM32=y\nCONFIG_MACH_STM32F407=y\n"
}

The (compressed) dictionary and the identify command both already exist, so the protocol wouldn’t need to change. It’d take one make rule plus a small handler in buildcommands.py. savedefconfig.py is already part of the vendored kconfiglib, just not wired up.

Since savedefconfig only records what deviates from Kconfig defaults, the payload would track how customized a board’s config is, not the size of the whole tree. A near-default board would cost next to nothing to embed. A board with a lot of non-default options set would cost more, but it’d still be a handful of lines compared to a multi-KB .config dump, compressing alongside data that’s already going into the dictionary.

What this would touch: a make step that regenerates the minimal config on every build, wired so a config change couldn’t leave a stale payload behind, plus exposing it on the host the same way existing build info already works.

This is self-reported metadata, not an attempt at attestation, and does not guarantee that replaying it later reproduces a byte-identical binary, since defaults can shift between revisions.

Happy to open a PR if this sounds interesting.

1 Like

FWIW, under normal circumstances, menuconfig is pretty small, for example:

[*] Enable extra low-level configuration options
    Micro-controller Architecture (STMicroelectronics STM32)  --->
    Processor model (STM32F407)  --->
    Bootloader offset (32KiB bootloader)  --->
    Clock Reference (8 MHz crystal)  --->
    Communication interface (USB (on PA11/PA12))  --->
    USB ids  --->
[*] Optimize stepper code for 'step on both edges' (NEW)
()  GPIO pins to set at micro-controller startup

Where one only cares about the MCU model, bootloader offset, clock reference, communication type, and pins.

They are partially defined in the “config” section:

{
  "config": {
    "ADC_MAX": 4095,
    "BUS_PINS_i2c1": "PB6,PB7",
...
    "CLOCK_FREQ": 168000000,
    "MCU": "stm32f407xx", # < this
    "PWM_MAX": 32768,
    "RECEIVE_WINDOW": 192,
    "RESERVE_PINS_CAN": "PB8,PB9", # < this
    "RESERVE_PINS_USB": "PA11,PA12", # < this
    "RESERVE_PINS_crystal": "PH0,PH1",
    "STATS_SUMSQ_BASE": 256,
    "STEPPER_OPTIMIZED_EDGE": 21,
    "STEPPER_STEP_BOTH_EDGE": 1
  },
}

So, from my PoV, if one wants to save something crucial to reproduce the firmware, it could be the clock reference crystal or the bootloader offset.

Otherwise, there were some old ideas about how to deal with firmware updates: Experiment with automated mcu flashing

I’m not saying that you should or shouldn’t do, I’m just adding some context.

My personal solution is pretty simple; I save the configs:

ls -l ./.config*
-rw-r--r-- 1 user user 4375 Aug 16 23:35 ./.config
-rw-r--r-- 1 user user 4153 Nov  8  2025 ./.config_back
-rw-r--r-- 1 user user 4098 Nov 21  2025 ./.config_carto
-rw-r--r-- 1 user user 4306 Nov 19  2025 ./.config_ebb42
-rw-r--r-- 1 user user 4102 Aug 16 23:33 ./.config.old
-rw-r--r-- 1 user user 2322 Jan 26  2025 ./.config_rp2040
-rw-r--r-- 1 user user 3858 Dec 11  2025 ./.config_stm32

IIRC, there were some automated solutions with saved configs.

Hope that explains something,
-Timofey

Yes, this is exactly why I am proposing the savedefconfig, it would just save the delta to the menuconfig. It’s an integral part of the kconfig lib. This is an example from an Octopus Pro (H723):

{
    "mcu_kconfig": {
        "defconfig": "CONFIG_LOW_LEVEL_OPTIONS=y\nCONFIG_MACH_STM32=y\nCONFIG_MACH_STM32H723=y\nCONFIG_STM32_CLOCK_REF_25M=y\n# CONFIG_USB_SERIAL_NUMBER_CHIPID is not set\n# CONFIG_WANT_HX71X is not set\nCONFIG_INITIAL_PINS=PD12\n"
    }
}

So it only saves the things you mentioned like MCU type, clock frequency and other overrides like the initial pins.

Saving the configs on disk is obviously a working solution - this would just enable tooling to query the board instead of relying on on-disk configs (after the initial flash).

For what it is worth, the reason I did not originally have the code save the kconfig in the final binary is that some chips have very tiny flash sizes and the kconfig can be quite large. If savedefconfig can actually produce a relatively small description then I think it could be useful.

Cheers,
-Kevin

Thank you both for your insights. I’ll clean up my prototype and send a PR on GitHub.

I’ll try to collect some real world size comparisons. I think the V0 display might be a good example. Any other configs that are size constrained and would make good test subjects?

PR: build: expose minimal Kconfig in MCU identify data by mjonuschat · Pull Request #7357 · Klipper3d/klipper · GitHub

Here’s an initial tool that builds on top of this work and tries to make updating MCUs easier: GitHub - mjonuschat/aldis: MCU firmware updater for Klipper printers · GitHub

1 Like