> [!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/ src/ # Source assets (committed) index.html # Single-page app template css/ style.css js/ app.js # ES module entry point: step navigation, flow orchestration kobo-device.js # KoboModels, KoboDevice class kobo-software-urls.js # Fetches download URLs from JSON, getSoftwareUrl, getDevicesForVersion nickelmenu.js # NickelMenuInstaller: downloads/bundles NickelMenu + config patch-ui.js # PatchUI: loads patches, parses YAML, renders toggle UI patch-runner.js # KoboPatchRunner: spawns Web Worker per build patch-worker.js # Web Worker: loads WASM, runs patchFirmware() wasm_exec.js # Go WASM runtime (copied from Go SDK by build.sh, gitignored) patches/ index.json # Available patch manifest downloads.json # Firmware download URLs by version/serial (may be auto-generated) patches_*.zip # Patch files per firmware version nickelmenu/ # NickelMenu assets (generated by setup.sh, gitignored) NickelMenu.zip kobo-config.zip favicon/ dist/ # Build output (gitignored, fully regenerable) bundle.js # esbuild output (minified, content-hashed) index.html # Generated with cache-busted references css/ favicon/ patches/ nickelmenu/ wasm/ js/wasm_exec.js build.mjs # esbuild build script + asset copy package.json # esbuild, jszip nickelmenu/ setup.sh # Downloads NickelMenu.zip and bundles kobo-config.zip kobopatch-wasm/ main.go # Go entry point go.mod setup.sh # Clones kobopatch source, copies wasm_exec.js build.sh # Compiles WASM, copies to web/dist/wasm/ and web/src/js/ integration_test.go test-integration.sh tests/ e2e/ integration.spec.js # Playwright E2E tests playwright.config.js run-e2e.sh ``` ## Adding a new software version 1. Add the patch zip to `web/src/patches/` and update `index.json` 2. Add download URLs to `web/src/patches/downloads.json` (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/dist/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/src/nickelmenu/`. ## Building the frontend The JS source lives in `web/src/js/` as ES modules. esbuild bundles them into a single `web/dist/bundle.js`. ```bash cd web npm install npm run build # production build (minified) npm run dev # dev server with watch mode on :8889 ``` ## Running locally ```bash ./serve-locally.sh ``` This serves the app at `http://localhost:8888`. The script automatically: 1. Sets up NickelMenu assets if missing (`web/src/nickelmenu/`) 2. Builds the JS bundle (`web/dist/bundle.js`) 3. Builds the WASM binary if missing (`web/dist/wasm/kobopatch.wasm`) You can delete the entire `web/dist/` folder and re-run `serve-locally.sh` to regenerate everything. ## Testing Run all tests (WASM integration + E2E): ```bash ./test.sh ``` This builds the web app, compiles the WASM binary, runs the WASM integration tests, and then runs the full E2E suite. ### 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, build failure with "Go Back" recovery - **Device detection** — firmware version validation (4.x supported, 5.x incompatible), unknown model warning - **Back navigation** — verifies every back button returns to the correct previous screen in both auto and manual mode - **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 and [esbuild](https://esbuild.github.io/) for bundling. Software patches and discussion on the [MobileRead forums](https://www.mobileread.com/forums/forumdisplay.php?f=247). ## License [MIT](LICENSE).