Website · Web Flasher · Telegram · Roadmap
Kern is a young open-source project building an air-gapped Bitcoin signing device on the ESP32-P4. The chip has no radio, so keys are generated and used on hardware that physically cannot reach a network. Transactions cross the air gap as QR codes or over an SD card.
It signs PSBTs for single-sig, multisig and miniscript policies on both native segwit and taproot, built on libwally, the same core library used by Blockstream Jade.
Warning: Kern is under active development and has not been audited. Secure boot is not enabled by default and builds are unvetted development snapshots. Do not use Kern to manage real savings.
Kern supports four Waveshare ESP32-P4 boards and one Elecrow CrowPanel board:
| Board | Display | Touch | Camera |
|---|---|---|---|
ESP32-P4-WiFi6-Touch-LCD-4B (wave_4b) |
720x720 MIPI DSI | GT911 | sold separately, OV5647 with autofocus recommended |
ESP32-P4-WiFi6-Touch-LCD-3.5 (wave_35) |
320x480 SPI | FT5x06 | OV5647, included |
ESP32-P4-WiFi6-Touch-LCD-5 (wave_5) |
720x1280 MIPI DSI | GT911 | OV5647, included |
ESP32-P4-WiFi6-Touch-LCD-4.3 (wave_43) |
480x800 MIPI DSI | GT911 | OV5647, included |
CrowPanel Advanced 10.1" ESP32-P4 and 7" siblings (crowpanel) |
1024x600 MIPI DSI | GT911 | SC2336, included |
ESP32-P4 does not contain radio (WiFi, BLE), but these boards have a radio in a secondary chip (ESP32-C6 mini). Later the project will migrate to use radio-less, simpler and cheaper boards with ESP32-P4 only.
A MIPI CSI camera module is required for all boards. Kern ships drivers for the OV5647 and SC2336 sensors and probes for whichever one is attached at boot, so either sensor works on any board. The 4B is the one board sold without a camera: pair it with an OV5647 module carrying a DW9714 voice coil motor, which is the only combination that gets autofocus.
Kern targets ESP-IDF v6.0.2. Install it for the esp32p4 target:
git clone --depth 1 --recurse-submodules --shallow-submodules -b v6.0.2 https://github.com/espressif/esp-idf.git ~/esp/esp-idf
~/esp/esp-idf/install.sh esp32p4
. ~/esp/esp-idf/export.shThis project uses git submodules. You have two options:
When cloning the project for the first time, make sure to clone it recursively to include all submodules:
git clone --recursive https://github.com/odudex/Kern.gitIf you've already cloned the repository without the --recursive flag, you can initialize and update the submodules with:
git submodule update --init --recursiveBuild with just (recommended) or idf.py directly. All just commands accept a board parameter, one of wave_4b (default), wave_35, wave_5, wave_43, or crowpanel:
just build # Build for wave_4b (default)
just build wave_35 # Build for wave_35
just build wave_5 # Build for wave_5
just build wave_43 # Build for wave_43
just build crowpanel # Build for CrowPanel 7" / 10.1"
just flash wave_5 # Flash for wave_5
just monitor # Serial monitor
just clean # Wipe all build_<board> dirs + sdkconfigOr using idf.py directly:
# wave_4b
idf.py -D 'SDKCONFIG_DEFAULTS=sdkconfig.defaults;sdkconfig.defaults.wave_4b' build
# wave_35
idf.py -D 'SDKCONFIG_DEFAULTS=sdkconfig.defaults;sdkconfig.defaults.wave_35' build
# wave_5
idf.py -D 'SDKCONFIG_DEFAULTS=sdkconfig.defaults;sdkconfig.defaults.wave_5' build
# wave_43
idf.py -D 'SDKCONFIG_DEFAULTS=sdkconfig.defaults;sdkconfig.defaults.wave_43' build
# crowpanel (7" / 10.1")
idf.py -D 'SDKCONFIG_DEFAULTS=sdkconfig.defaults;sdkconfig.defaults.crowpanel' buildNote:
justbuilds each board into its ownbuild_<board>/directory, so switching boards needs no clean. The rawidf.pycommands above share the defaultbuild/directory andsdkconfig: runidf.py fullclean && rm sdkconfigwhen switching boards that way.
Note: The first build auto-generates
dev_signing_key.pem(gitignored) and every build is signed with it. This is required to boot: the firmware refuses to run unsigned images. This key is a per-clone development key that carries no trust and never leaves your machine. Official releases are signed offline with the project's release key instead, so a self-built device only accepts SD-card updates built from the same clone; flash releases over USB.
The simulator renders the full LVGL UI in an SDL2 window, matching each board's resolution:
just sim # Run simulator as wave_4b (720x720) with webcam (V4L2)
just sim wave_35 # Run simulator as wave_35 (320x480)
just sim wave_5 # Run simulator as wave_5 (720x1280)
just sim wave_43 # Run simulator as wave_43 (480x800)
just sim crowpanel # Run simulator as crowpanel (1024x600)
just sim-build wave_35 # Build only
just sim-clean # Remove simulator build artifacts
just sim-reset # Wipe simulator data (factory reset)
just sim-qr IMG # Run with a QR image
just sim-no-cam # Run without cameraSwitching simulator boards also requires just sim-clean first. See simulator/README.md for details.
After updating ESP-IDF or switching branches with significant build changes, do a full clean to avoid stale artifacts:
idf.py fullclean
rm sdkconfig
idf.py set-target esp32p4
idf.py buildTo enable camera auto-focus, enable camera focus motor on menuconfig:
CONFIG_CAM_MOTOR_DW9714=y
CONFIG_CAMERA_OV5647_ENABLE_MOTOR_BY_GPIO0=y
The easiest way to flash Kern is the browser-based flasher, which requires no local toolchain. It works in Google Chrome or Microsoft Edge (version 89+) via the Web Serial API.
Live flasher: https://odudex.github.io/Kern/flash/
The flasher offers two modes:
- Latest CI Build: fetches firmware built by the most recent
masterpush directly from the site and flashes it to the selected board. - Custom ZIP Bundle: accepts a
firmware-<board>.zipartifact downloaded from the Actions tab to flash any PR or older build.
Warning: CI builds are unvetted development snapshots from a young, unaudited project. Secure boot is not enabled; do not use flashed firmware to manage real savings.
Note: The project site and flasher are deployed automatically on every successful push to
master, fromsite/in this repository. To enable it for your fork, go to Settings → Pages and set the source to GitHub Actions. To preview the site locally, runjust siteand open http://localhost:8000. Web Serial works on localhost, so you can flash a real board from the local copy.
Every pull request and push to master produces a firmware artifact for each supported board via the Test All Builds workflow. These builds are useful for testing unreleased changes without setting up a local toolchain.
- Python 3
- USB cable connected to the board
-
Open the Actions tab of the repository on GitHub and select the workflow run you want.
-
Scroll to the Artifacts section at the bottom of the run summary and download the zip for your board (e.g.
firmware-wave_4b). -
Unzip the package:
unzip firmware-wave_4b.zip -d firmware-wave_4b cd firmware-wave_4bThe zip contains, among other files:
bootloader.bin: bootloaderpartition-table.bin: partition tableota_data_initial.bin: OTA data partitionkern.bin: application firmwareflasher_args.json/flash_args: pre-computed flash offsets
-
Create a Python virtual environment and install esptool:
python3 -m venv venv source venv/bin/activate pip install esptool -
Flash using the pre-computed offsets from
flash_args:esptool --chip esp32p4 --baud 460800 write_flash $(cat flash_args)
Pre-release firmware is provided for testing purposes only. Do not use pre-release builds as a signer for real savings.
| Device | Board | Display |
|---|---|---|
wave_4b |
Waveshare ESP32-P4-WiFi6-Touch-LCD-4B | 720x720 MIPI DSI |
wave_35 |
Waveshare ESP32-P4-WiFi6-Touch-LCD-3.5 | 320x480 SPI |
wave_5 |
Waveshare ESP32-P4-WiFi6-Touch-LCD-5 | 720x1280 MIPI DSI |
wave_43 |
Waveshare ESP32-P4-WiFi6-Touch-LCD-4.3 | 480x800 MIPI DSI |
crowpanel |
CrowPanel Advanced 7" / 10.1" ESP32-P4 | 1024x600 MIPI DSI |
- Python 3
- USB cable connected to the board
-
Download the zip for your device from the Releases page (e.g.
kern-wave_4b-v0.0.3.zip). -
Unzip the package:
unzip kern-wave_4b-v0.0.3.zipThe zip contains:
bootloader.bin: bootloaderpartition-table.bin: partition tablefirmware-signed.bin: application firmware (signed; also the file for SD-card updates)kern-v0.0.3.hex: single-file image (all of the above, Intel HEX)
- Create a Python virtual environment and install esptool:
python3 -m venv venv
source venv/bin/activate
pip install esptool- Flash the single-file image:
esptool --chip esp32p4 --baud 460800 write-flash 0x0 kern-v0.0.3.hexNote: The
.heximage is sparse: it writes only the regions it contains, so the NVS partition (PIN, settings) and stored data survive reflashing. For a factory-clean install, erase everything first:esptool --chip esp32p4 erase-flash, then flash the image.
A device already running signed firmware updates without any computer: copy firmware-signed.bin from the zip onto a SD card, insert it, and go to Settings → Firmware Update. The device verifies the signature before installing and keeps the previous firmware as an automatic fallback.
Development chat, testing reports and questions happen in the Telegram group: t.me/kern_custody.
Bug reports and pull requests are welcome on GitHub.
Kern is strongly inspired by Krux, sharing similar but simplified UI elements and flow.
Blockstream Jade was a strong inspiration for the decision to use C language for efficient use of the hardware. Additionally, Kern uses the same core library Jade does, libwally, is shared with Jade.
The simplicity and UI polish of SeedSigner and the security focus of the pioneering Specter-DIY were also strong inspirations.
