> [!NOTE]
> If this project has been useful to you, I ask that you **please star the repository**, that way I know that the software is being used. Also, please consider [sponsoring](https://nicoverbruggen.be/sponsor) to support my open source projects, as this is something I work on in my free time. **Thank you!** ⭐️
# KoboPatch Web UI
A web application for customising Kobo e-readers. It supports two modes:
- **NickelMenu** — installs [NickelMenu](https://pgaskin.net/NickelMenu/) [fork](https://github.com/nicoverbruggen/NickelMenu) with an optional [curated configuration](https://github.com/nicoverbruggen/kobo-config) (custom menus, fonts, screensavers, UI tweaks). Works with most Kobo devices regardless of software version. Can also remove NickelMenu from a connected device.
- The safest patch to install. These modifications tend to persist with system updates as long as NickelMenu remains functional.
- Will automatically uninstall itself if Kobo releases an incompatible update in the future, which may happen with software v5.x at some point.
- **Custom patches** — applies community [kobopatch](https://github.com/pgaskin/kobopatch) patches to your Kobo's system software. Requires a supported software version and device model, which is currently limited to Kobo Libra Color, Kobo Clara Color and Kobo Clara BW models.
- A more experimental solution -- you need to choose what tweaks to apply.
- These changes are wiped when system updates are released. Requires re-patching when system updates are installed.
- Gives you a lot of customization options, but not all of them may work correctly.
## How it works
The app uses the **Filesystem Access API** (Chromium) to interface with connected Kobo devices, or falls back to manual model/software version selection with a downloadable ZIP on other browsers.
If you choose to apply custom patches, **patching happens fully client-side** — no backend needed, can be hosted as a static site. Patches are community-contributed via the [MobileRead forums](https://www.mobileread.com/forums/forumdisplay.php?f=247) and need to be manually updated when new Kobo software versions come out.
> [!NOTE]
> This project is not affiliated with Rakuten Kobo Inc. Patching modifies system files on your Kobo and will void your warranty. If something goes wrong, you may need to [manually reset your device](https://help.kobo.com/hc/en-us/articles/360017605314).
## User flow
1. **Connect or download** — auto-detect your Kobo via File System Access API on Chromium, or choose manual download mode (any browser)
2. **Choose mode** — NickelMenu (install/configure/remove) or custom patches
3. **Configure** — for NickelMenu: select install options (fonts, screensaver, tab/homescreen tweaks) or removal; for patches: enable/disable patches (or select none to restore original software)
4. **Review** — confirm your selections before proceeding
5. **Install** — write directly to the device (Chromium auto mode) or download a ZIP/tgz for manual installation
## File structure
```
web/public/ # Webroot — serve this directory
index.html # Single-page app, multi-step wizard
css/
style.css
js/
app.js # Step navigation, flow orchestration, firmware download with progress
kobo-device.js # KOBO_MODELS (serial prefix → name), FIRMWARE_DOWNLOADS (version+prefix → URL),
# getDevicesForVersion(), getFirmwareURL(), KoboDevice class (File System Access API)
nickelmenu.js # NickelMenuInstaller: downloads NickelMenu.zip + kobo-config.zip, installs to
# device or builds download ZIP, handles config file filtering and modification
patch-ui.js # PatchUI class: loads patch zips (JSZip), parses YAML, renders toggle UI,
# generates kobopatch.yaml config with overrides
kobopatch.js # KobopatchRunner: spawns Web Worker per build, handles progress/done/error messages
patch-worker.js # Web Worker: loads wasm_exec.js + kobopatch.wasm, runs patchFirmware(),
# posts progress back, transfers result buffer zero-copy
wasm_exec.js # Go WASM support runtime (copied from Go SDK by setup.sh, gitignored)
jszip.min.js # Bundled JSZip library
wasm/
kobopatch.wasm # Compiled WASM binary (built by build.sh, gitignored)
patches/
index.json # Contains a list of available patches
patches_*.zip # Each contains kobopatch.yaml + src/*.yaml patch files
nickelmenu/ # NickelMenu assets (built by nickelmenu/setup.sh, gitignored)
NickelMenu.zip # NickelMenu release
kobo-config.zip # Curated configuration files (fonts, screensaver, menu items)
nickelmenu/
setup.sh # Downloads NickelMenu.zip and bundles kobo-config.zip from kobo-config repo
kobopatch-wasm/ # WASM build
main.go # Go entry point: jsPatchFirmware() → patchFirmware() pipeline
go.mod
setup.sh # Clones kobopatch source, copies wasm_exec.js
build.sh # GOOS=js GOARCH=wasm go build, copies .wasm to web/public/wasm/
integration_test.go # Go integration test: validates SHA1 checksums of patched binaries
test-integration.sh # Downloads firmware and runs integration_test.go
tests/
e2e/ # Playwright E2E tests
integration.spec.js # Full browser tests: NickelMenu flows, custom patches, mock device
playwright.config.js
run-e2e.sh # E2E runner (downloads firmware, sets up NickelMenu assets, installs browser)
```
## Adding a new software version
1. Add the patch zip to `web/public/patches/` and update `index.json`
2. Add download URLs to `FIRMWARE_DOWNLOADS` in `js/kobo-device.js` (keyed by version then serial prefix)
3. The Kobo CDN prefix per device family (e.g. `kobo12`, `kobo13`) is stable; the date path segment changes per release
## Building the WASM binary
Requires Go 1.21+.
```bash
cd kobopatch-wasm
./setup.sh # first time only
./build.sh # compiles WASM, copies to web/public/wasm/
```
## Setting up NickelMenu assets
```bash
nickelmenu/setup.sh
```
This downloads `NickelMenu.zip` and clones/updates the [kobo-config](https://github.com/nicoverbruggen/kobo-config) repo to bundle `kobo-config.zip` into `web/public/nickelmenu/`.
## Running locally
```bash
./serve-locally.sh
```
This serves the app at `http://localhost:8888`. If the WASM binary or NickelMenu assets haven't been set up yet, the script handles that automatically.
## Testing
### E2E tests (Playwright)
The E2E tests cover all major user flows:
- **NickelMenu** — install with config (manual download), install NickelMenu only, remove option disabled without device
- **Custom patches** — full patching pipeline, restore original firmware
- **With simulated Kobo Libra Color** — install NickelMenu with config, remove NickelMenu, install custom patches, restore firmware
The simulated device tests mock the File System Access API with an in-memory filesystem that mimics a Kobo Libra Color (serial prefix N428, firmware 4.45.23646).
Custom patches tests download firmware 4.45.23646 (~150MB, cached in `kobopatch-wasm/testdata/`), enable a single patch, and verify SHA1 checksums of all 4 patched binaries. This specific combination is used because the author has tested it on an actual device.
```bash
cd tests/e2e
./run-e2e.sh
```
To run with a visible browser window:
```bash
./run-e2e.sh --headed
```
To slow down each action (500ms delay) for debugging:
```bash
./run-e2e.sh --headed --slow
```
Extra Playwright arguments can be passed after `--`:
```bash
./run-e2e.sh --headed --slow -- --grep "NickelMenu"
```
### WASM integration test
Calls `patchFirmware()` directly in Go/WASM via Node.js:
```bash
cd kobopatch-wasm
./test-integration.sh
```
## Output validation
The WASM patcher performs several checks on each patched binary before including it in the output `KoboRoot.tgz`:
- **File size sanity check** — the patched binary must be exactly the same size as the input. kobopatch does in-place byte replacement, so any size change indicates corruption.
- **ELF header validation** — verifies the magic bytes (`\x7fELF`), 32-bit class, little-endian encoding, and ARM machine type (`0x28`) are intact after patching.
- **Archive consistency check** — after building the output tar.gz, re-reads the entire archive and verifies the sum of entry sizes matches what was written.
## Credits
Built on [kobopatch](https://github.com/pgaskin/kobopatch) and [NickelMenu](https://pgaskin.net/NickelMenu/) by pgaskin. Uses [JSZip](https://stuk.github.io/jszip/) for client-side ZIP handling. Software patches and discussion on the [MobileRead forums](https://www.mobileread.com/forums/forumdisplay.php?f=247).
## License
[MIT](LICENSE).