Making a Kobo Clara BW Our Own—Without Replacing Its OS

The screen rotated beautifully. The touch controls went the other way.

We had added a landscape shortcut to a Kobo Clara BW. The page looked right, but taps landed somewhere else. Worse, the menu needed to undo the change was now difficult to operate. A small quality-of-life improvement had turned into a very practical lesson in recovery engineering.

That moment captures our Kobo project better than a screenshot of the finished launcher. We wanted an account-free reader with Cobalt, KOReader, Plato, useful controls, and a way back when an experiment failed. Getting there meant identifying the actual hardware, keeping firmware changes out of the critical path, measuring touch input, and learning that two things called “dark mode” can change completely different parts of a device.

We ended up with a much more capable reader. We did not need a replacement operating system to get it.

Prefer the project overview? The Kobo Clara BW case study summarizes the brief, approach, verified outcomes, and remaining work. This article is the detailed engineering account.

The finished setup, before the interesting mistakes

Our active device is a Kobo Clara BW, physically labelled N365B, electronically identified as N365 / device code 391. We retained its existing 4.45.23655 stock firmware.

Layer What we installed or retained Its job
Stock Kobo / Nickel Existing firmware and reading interface Keep the original reader available
Account-free setup Stock sideloaded mode Reach the reading interface without registering a Kobo account
Cobalt 0.3.31 Launcher and app platform Add applications without replacing the stock OS
NickelMenu 0.6.0 Shared menu integration Launch tools and expose selected controls
KOReader 2026.07.1 Alternative reading application Provide another reading environment
Plato 0.9.45 Alternative reading application Provide a second, different reading environment
NickelTouchFix 0.1.0 Targeted touch-transform correction Make forced stock landscape usable on our tested combination
SSH Dedicated key plus a unique password fallback Diagnose and recover without relying entirely on touch

Those are the versions we tested, not a promise about whichever releases happen to be newest when you read this. The final rotation menu is deliberately small: Portrait and Landscape (USB right), available in both the main interface and the stock reader.

The goal was a better reader, not the largest possible menu.

We started with a bigger idea—and a different Kobo

The wider project began with a Clara Colour and an independent-OS ambition. We researched recovery tools, built host-side inspection and backup tooling, and produced a reference ARM userspace image with Buildroot.

That reference image was a host build. It was not a hardware-boot-tested Kobo OS, and we did not treat it as something ready to flash.

The project then moved to a separate Clara BW. That change mattered more than the similar name suggests. We kept the Colour work separate and started the BW's identity and backup record again. A useful discovery on one reader is not automatically evidence about another reader's board, storage layout, or recovery path.

The change also sharpened the question. Did we actually need a new OS, or did we need our existing reader to do more?

Cobalt, NickelMenu, KOReader, and Plato already covered much of the second goal. Using them let us leave the vendor kernel and boot chain alone.

That was a substantial reduction in the amount of new system behavior we needed to prove.

First: identify the device, then decide about firmware

The temptation with a newly powered-on device is to connect Wi-Fi, finish setup, and worry about customization afterwards. For this project, we wanted to know what was already installed before accepting an update.

We inspected the BW's USB-exported storage and its version information. The physical N365B label and electronic N365/code 391 identity were recorded separately; we did not pretend that a model code proved every board-revision detail. We also kept the N365 distinct from the later P365/code 395 family.

The stock firmware was already 4.45.23655. Cobalt's published device matrix gave us reference combinations, but the matrix was not the only question. We checked the actual installer behavior against our genuine device information, verified the selected release, and then tested the resulting runtime.

Our existing firmware passed the relevant installer checks and subsequent on-device acceptance. We did not falsify a version file, remove a compatibility gate, or upgrade simply to make the version number look more familiar.

The useful distinction is between “listed upstream,” “accepted by the installer,” and “observed working on this device.” They are related, but they are not interchangeable.

This is also why this post is a case study, not a universal installation script. Check the current upstream instructions for your exact model and firmware. Do not assume our result authorizes a downgrade, another board revision, or an unrelated firmware branch.

Backups: useful copies are not the same as a proven rescue

Before modifying the BW, we copied its USB-visible files—including hidden content—and verified copies on two separate storage devices. The initial baseline contained 388 files, approximately 163 MB.

That gave us something concrete to compare against and a way to recover individual exported files and settings. We took further snapshots around installation and later fixes rather than relying on one distant “before” folder.

But there was an important boundary: USB book storage is not the whole eMMC.

Our wider investigation had already shown how long raw captures can fail when a connection disappears. Even a complete, checksummed image read from a live system is not automatically a consistent filesystem snapshot. And none of those facts, by themselves, proves that a recovery tool can restore a damaged boot layer.

We did not establish complete BW boot-layer recovery during this project. That is one reason we kept the work at the application, configuration, launcher, and userspace-hook layers.

The practical rule was to match the recovery plan to the change. Preserve the files you will change; keep an independent route back; and do not use a successful file copy as permission to experiment with a bootloader.

Account-free did not have to mean offline forever

We chose stock Kobo's sideloaded mode rather than inventing a fake registration database entry. On our firmware, the relevant setting was:

[ApplicationPreferences]
SideloadedMode=true

We preserved the original configuration and made the change additively. After restart, the reader reached its Books interface without an account. The initially empty library was expected, not a failed setup.

Later, the BW joined the home network and could go online. Account-free setup and internet access are separate decisions. It also does not mean every optional app or service will be account-free, or that the sideloaded setting blocks firmware updates.

We kept the tested software combination stable while qualifying it: Cobalt/app automatic updates were disabled, and known Kobo update/API hosts were blocked on this unit. That is a limited guard against drift, not a guarantee that every possible update path is disabled.

Installing applications still required checking the edges

We verified Cobalt's selected stable release and signed payload rather than treating an installer download as sufficient evidence. We also hit a distinctly Linux-shaped complication: the host's FAT mount presented script files with executable permissions that caused the direct setup path to stop at its bootstrap permission check.

We did not solve that by changing the Kobo's identity or weakening the installer's firmware checks. We used the unchanged, verified release against an exact host-side copy of the pre-install export, reviewed the generated output, and staged those outputs onto the actual reader with readback checks. All 422 resulting exported files matched the prepared tree after unmounting and remounting.

That was our host-environment workaround, not a recommended shortcut around upstream installation instructions. The important part was that the real device identity, release verification, and subsequent runtime checks remained intact.

Activation was a separate milestone. A Cobalt menu entry meant the menu had loaded; it did not yet prove applications rendered correctly, touch worked, or stock Kobo would come back. We tested those transitions with the reader in hand, then checked restart persistence and repeat launch/return behavior.

KOReader and Plato were added as separate entries in the same menu rather than as competing startup systems. We also excluded the hidden application directories from stock library scanning. One useful launcher adaptation was extending Plato's process-stop list for the watchdog processes present on this firmware, following the behavior already used by KOReader.

These were small integration details, but they lived at exactly the boundaries where an application can appear installed and still hand control back badly.

Keep a way in that does not depend on the screen

SSH became particularly valuable once rotation experiments started.

We installed a dedicated key, verified fresh connections, and later retained a unique, memorable password fallback at the owner's request. Key login remained enabled; empty-password login did not. This was a deliberate access tradeoff, not a claim that password authentication is universally preferable.

For a reader you are customizing, the useful question is how you will recover if the convenient path fails. Protect the key, use a reader-specific password if you keep that fallback, and keep administrative access on a trusted network rather than exposing it as a public service.

There was another trap worth recording: the stock SSH setup wrapper ignored ordinary remote command arguments and started a shell. We had to verify what the remote side actually executed rather than assuming every normal SSH or file-transfer idiom would work unchanged.

That sort of detail is easy to dismiss—until the screen is sideways and you are depending on the connection to undo it.

The landscape failure that made the project interesting

NickelMenu exposes native orientation controls, and stock Kobo can be configured to allow landscape on a device without an orientation sensor. We initially added the controls.

On our N365/code 391 with firmware 4.45.23655, uncorrected forced stock landscape misaligned display and touch. The interface rotated; the input mapping did not agree with the displayed locations. Recovering through SSH was much easier than trying to hit the right menu item through the wrong coordinate system.

We removed the unsafe controls and restored normal portrait before trying to improve anything else.

The existing project NickelTouchFix addressed this exact class of problem. Instead of replacing the input driver, it hooks the touch plugin's Qt orientation transform and applies a 180-degree correction in configured orientations. Rendering and unselected orientations are left alone.

We pinned its released v0.1.0 binary and source, checked its dependencies against the installed libraries, and confirmed the running hook. We then tested a controlled configuration:

[TouchFix]
FlipOrientations=landscape, inverted_landscape
Debug=true

These are the settings we qualified on this unit—not an instruction to enable uncorrected landscape on an arbitrary Kobo. The orientation controls were guarded, and the experiments had a non-touch return path.

Four deliberate taps beat a lot of enthusiastic tapping

For each pose, the operator held the reader fixed and tapped blank areas in this order: upper-left, upper-right, lower-right, lower-left. A bounded, shared input capture recorded the contacts without taking exclusive ownership of touch input.

We compared the contacts with the measured portrait mapping and the actual Qt transforms logged by the hook. Then came a separate usability check: could the operator select a menu item and return to portrait accurately?

There were messy attempts. Accidental taps produced extra contacts, and we kept those traces without counting them as either a clean pass or a mysterious hardware fault. We repeated the structured sequence.

Pose, with text upright Native orientation action Observed result
USB at bottom portrait Four-corner baseline passed; no extra flip
USB at right inverted_landscape Corrected corners, menu selection, and manual return passed
USB at left landscape Corrected corners, menu selection, and manual return passed
USB at top inverted_portrait Native corners, menu selection, and manual return passed; no extra flip

The landscape dimensions were 1448 × 1072; portrait was 1072 × 1448. Matching dimensions alone would not have proved input direction. The prescribed contacts and menu checks were the evidence that mattered.

The tests establish orientation and corner direction, not pixel-accurate calibration or exhaustive swipe and text-selection behavior. Ordinary restart checks also confirmed the retained hook and normal portrait state.

After qualifying all four poses, we removed two of the shortcuts. Only Portrait and Landscape (USB right) remain. More tested capability did not need to become more permanent clutter.

“Dark mode” turned out to mean two different things

The next apparent bug was a dark-mode toggle that seemed to do nothing on the stock home screen.

In NickelMenu, the built-in action we used changes ReadingSettings.DarkMode. It affects stock book pages and refreshes an active reading view; it does not make the Books screen or the whole stock interface dark. Outside a book, it can return silently and look unresponsive.

We renamed it Book-page dark mode (toggle) and added explanatory feedback to the main-menu action. That fixed an expectation problem without pretending we had implemented a system-wide theme.

The more interesting symptom came from Plato: switching it to dark mode could leave stock Kobo looking inverted afterwards.

On this BW's MTK display path, the Plato release we inspected writes a global display-driver flag through /proc/hwtcon/cmd: night_mode 4 enables the relevant inversion, and night_mode 0 clears it. Its return-to-Nickel launcher did not clear that state. KOReader's MTK code explicitly handles the same cleanup on exit.

We added a narrowly scoped light-mode reset before Plato starts the stock animation and Nickel, preserving Plato's own saved preference. The driver accepted the idempotent reset command, the modified launcher passed shell checks, and file comparisons confirmed unrelated integration files stayed unchanged.

That is not the same as claiming a complete visual regression test. The dedicated dark-Plato → normal Exit → light-stock check was deferred when the owner accepted the current behavior for now. A successful driver write and an installed fix are recorded as such, not promoted into an observation we did not make.

The general lesson is useful beyond e-readers: an application's preferences, a framework's rendering choices, and a driver's global state can all look like one setting to the person using the screen. Exit cleanup is part of the feature.

We sent the useful part back upstream

NickelTouchFix's documentation welcomed compatibility reports. Its listed working combination differed from our firmware, so we submitted a Clara BW compatibility report covering 4.45.23655, both landscape directions, and the native portrait checks.

The report includes selected actual debug lines, the measurement method, and explicit limits. It does not include serial numbers, credentials, network information, private library data, or raw backups.

A useful report is not just “works for me.” It gives the maintainer enough context to know what worked, on which combination, and what was not tested. The credit for the targeted touch correction belongs to the upstream project; our contribution was another measured compatibility result.

What we would repeat on the next device

  1. Identify the actual unit. Keep physical labels, electronic model codes, installed firmware, and measured behavior distinct.
  2. Preserve before editing. Verify off-device copies, and do not confuse exported files with complete firmware recovery.
  3. Try the smallest useful layer first. Existing applications and a targeted userspace fix achieved this project's goals without a new kernel.
  4. Test transitions, not just launches. Enter the app, use it, exit it, check what came back, and repeat after restart.
  5. Prepare recovery before breaking touch. A menu button is not an independent rollback route when the experiment changes where taps land.
  6. Use deliberate physical observations. Four prescribed taps tell a clearer story than an unstructured stream of contacts.
  7. Keep the everyday interface small. Diagnostics can be extensive; the menu you use to read should not have to be.
  8. Publish the boundary of the result. Installed, statically plausible, operator-observed, restart-verified, and deferred are different states.

What is still unfinished

Cobalt's core launch, touch, stock return, restart persistence, and repeat use passed our initial checks. KOReader launched and exited back to stock. Plato's initial interface operation was observed and stock control returned afterwards. The four stock orientation poses passed the corner/menu checks described above.

That does not amount to testing every book format, every app, long-duration battery behavior, suspend/resume, USB handoff after all changes, alternate-reader launches from forced stock landscape, or complete removal of the retained hook. Those remain separate work. For the already-qualified app-launch workflow, we return stock Kobo to portrait first.

The earlier Clara Colour OS investigation also remains separate and paused. We have not turned a host-built root filesystem or a collection of backups into a claim of demonstrated boot-layer rescue.

What we have is more modest and more useful: an account-free Clara BW with multiple reading environments, an app platform, working selected rotation controls, and documented ways to diagnose and undo our changes.

Making it ours did not mean replacing everything. It meant knowing which layer to change, proving the part we changed, and leaving the rest available to read a book.

Projects and further reading

Kobo and the upstream projects are credited here as independent projects and products. This account describes our tested unit and software versions; it is not an official compatibility guarantee.

Explore related work

Read the Kobo case study for the outcome-focused view, or browse all case studies, including our Cog Wars website and search-visibility project. More implementation stories live on the blog; reference material lives in documentation.