Cadmus
Cadmus is a document reader for Kobo’s e-readers.
Documentation
This site is the primary source of documentation for Cadmus. Use the sidebar to navigate, or start at the overview for installation, usage, and workflows.
Supported firmwares
Any 4.X.Y firmware, with X ≥ 6, will do.
Supported devices
- Libra Colour.
- Clara Colour.
- Clara BW.
- Elipsa 2E.
- Clara 2E.
- Libra 2.
- Sage.
- Elipsa.
- Nia.
- Libra H₂O.
- Forma.
- Clara HD.
- Aura H₂O Edition 2.
- Aura Edition 2.
- Aura ONE.
- Glo HD.
- Aura H₂O.
- Aura.
- Glo.
- Touch C.
- Touch B.
Supported formats
Features
- Crop the margins.
- Continuous fit-to-width zoom mode with line preserving cuts.
- Rotate the screen (portrait ↔ landscape).
- Adjust the contrast.
- Define words using dictd dictionaries.
- Annotations, highlights and bookmarks.
- Retrieve articles from online sources through hooks.
Screenshots
![]()
Acknowledgments
Cadmus is a fork of Plato, a document reader created by Bastien Dejean.
Installation
Cadmus comes in different packages. Pick the one that matches your needs.
Available packages
| Package | What’s included | Installs to |
|---|---|---|
KoboRoot.tgz | Cadmus only | /mnt/onboard/.adds/cadmus |
KoboRoot-nm.tgz | Cadmus + NickelMenu | /mnt/onboard/.adds/cadmus |
KoboRoot-test.tgz | Test build only | /mnt/onboard/.adds/cadmus-tst |
KoboRoot-nm-test.tgz | Test build + NickelMenu | /mnt/onboard/.adds/cadmus-tst |
Which one should I pick?
- Normal installs: Use
KoboRoot.tgzorKoboRoot-nm.tgz - If you use NickelMenu: Pick a package that includes it (
-nmversions) - Testing a new feature: Use test packages (
-testversions) for trying out changes that haven’t been released yet
First-time setup
- Go to the latest release.
- Download the package you want from the table above.
- Connect your Kobo to your computer via USB.
- Copy the downloaded file to
/mnt/onboard/.kobo/KoboRoot.tgzon the device. - Eject the device and reboot.
Updating
Once installed, you can update Cadmus directly through its built-in OTA feature
- no computer required, just WiFi. See OTA updates for details.
Test builds
First-time install
- Open the Cadmus GitHub Actions page.
- Select the run for the change you want to test.
- Download the
cadmus-kobo-test-<suffix>file.
- Extract it and pick the package that matches your setup.
- Copy the selected KoboRoot file to:
/mnt/onboard/.kobo/KoboRoot.tgz - Eject the device and reboot.
Updating an existing test build
Use the OTA feature to download updates from a PR number directly on your device. This lets you test changes without connecting to a computer.
OTA updates
Once Cadmus is installed, you can update it wirelessly without connecting to a computer. The OTA (Over-The-Air) feature downloads updates directly from GitHub.
What you need
- A WiFi connection
For main branch or PR builds, you also need a GitHub personal access token in
your Settings.toml file:
[ota]
github-token = "ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
Stable releases do not require a token. See the OTA settings reference for details on getting a token.
How to update
Open Main Menu → Check for Updates. You’ll see options for where to get the update from:
| Source | Description |
|---|---|
| Stable Release | Latest official release from GitHub |
| Main Branch | Latest development build (most recent changes) |
| PR Build | Test a specific pull request |
Note: The Stable Release option is not shown in test builds.
Updating from the main branch
Select Main Branch to get the most recent development build. This includes changes that have been merged but not yet released officially.
The update downloads from GitHub, installs automatically, and prompts you to reboot to finish.
Testing a pull request
Select PR Build to try out a specific change before it’s released. Enter the PR number when prompted.
This downloads the update from that pull request, installs it, and asks you to reboot.
Tip: Find the PR number in the GitHub URL. For example, in
github.com/OGKevin/cadmus/pull/42the PR number is 42.
Normal vs test builds
OTA works for both types of builds. The type you’re currently using determines what gets downloaded:
- Normal builds update to
KoboRoot.tgzin/mnt/onboard/.adds/cadmus - Test builds update to
KoboRoot-test.tgzin/mnt/onboard/.adds/cadmus-tst
See the available packages table for all options.
First-time setup
OTA only works for updating an existing installation. To install Cadmus for the first time, follow the installation guide or the test builds guide to copy a KoboRoot file via USB.
Settings
Cadmus reads settings from Settings/Settings-*.toml.
Settings can be changed on your Kobo through Main Menu → Settings, which opens the built-in settings editor.
Legend:
- ✏️ Editable in the settings editor
- 🔑 Required for feature to work
General Settings
keyboard-layout
✏️
Keyboard layout to use for text input.
- Possible values:
"English","Russian".
keyboard-layout = "English"
sleep-cover
✏️
Handle the magnetic sleep cover event.
sleep-cover = true
auto-share
✏️
Automatically enter shared mode when connected to a computer.
auto-share = false
auto-suspend
✏️
Number of minutes of inactivity after which the device will automatically go to sleep.
- Zero means never.
auto-suspend = 30.0
auto-power-off
✏️
Delay in days after which a suspended device will power off.
- Zero means never.
auto-power-off = 3.0
button-scheme
✏️
Defines how the back and forward buttons are mapped to page forward and page backward actions.
- Possible values:
"natural","inverted".
button-scheme = "natural"
Libraries
✏️
Document library configuration. Each library has a name, path, and mode.
[[libraries]]
name = "On Board"
path = "/mnt/onboard"
mode = "database"
libraries.name
✏️
Display name for the library.
libraries.path
✏️
Directory path containing documents.
libraries.mode
✏️
Library indexing mode.
- Possible values:
"database","filesystem".
Intermissions
✏️
Defines the images displayed when entering an intermission state.
[intermissions]
suspend = "logo:"
power-off = "logo:"
share = "logo:"
intermissions.suspend
✏️
Image displayed when the device enters sleep mode.
- Possible values:
"logo:"(built-in logo),"cover:"(current book cover), or a path to a custom image file.
intermissions.power-off
✏️
Image displayed when the device powers off.
- Possible values:
"logo:"(built-in logo),"cover:"(current book cover), or a path to a custom image file.
intermissions.share
✏️
Image displayed when entering USB sharing mode.
- Possible values:
"logo:"(built-in logo),"cover:"(current book cover), or a path to a custom image file.
OTA
The OTA feature downloads builds from GitHub.
ota.github-token
GitHub personal access token needed to download development and test builds. Not required for stable releases.
- Configure it under the
[ota]section.
[ota]
github-token = "ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
To create a token:
- Go to https://github.com/settings/personal-access-tokens/new
- Under Repository access, select Public repositories
- No additional permissions are required
- Generate and copy the token to the latest settings file in
Settings
Logging
Cadmus writes JSON logs to disk. When the build enables the otel feature, it
can also export logs to an OpenTelemetry endpoint.
logging
[logging]
enabled = true
level = "info"
max-files = 3
directory = "logs"
# otlp-endpoint = "https://otel.example.com:4318"
Environment overrides:
OTEL_EXPORTER_OTLP_ENDPOINTtakes precedence overlogging.otlp-endpoint.
Settings Retention
Cadmus stores each version’s settings in a separate file in the Settings/ directory (for example, Settings-v1.2.3.toml).
This ensures backward and forward compatibility when you upgrade.
settings-retention
Number of recent version settings files to keep. Only the most recent N version files are kept. When a new version is saved, older versions beyond this limit are deleted automatically.
- Default:
3 - Set to
0to keep all version files
settings-retention = 3
Development Environment Setup
Cadmus uses devenv with Nix to provide a reproducible development environment. This guide covers setup on both Linux and macOS.
Prerequisites
- Install Nix with flakes enabled. The easiest way is using the Determinate Nix Installer.
- Install devenv.
Quick Start
-
Clone the repository and enter the devenv shell:
git clone https://github.com/OGKevin/cadmus.git cd cadmus devenv shell -
Run the one-time setup to build native dependencies:
cadmus-setup-native -
Run the emulator:
./run-emulator.sh
Available Commands
Once inside the devenv shell, these commands are available:
| Command | Description |
|---|---|
cadmus-setup-native | Build MuPDF for native development (run once) |
cargo test | Run the test suite |
./run-emulator.sh | Run the emulator |
cadmus-build-kobo | Build for Kobo device (Linux only) |
cadmus-dev-otel | Run emulator with OpenTelemetry instrumentation |
devenv up | Start observability stack (Grafana, Tempo, Loki) |
cadmus-docs-build | Build documentation portal (mdBook + Cargo docs) |
cadmus-docs-serve | Serve documentation portal locally on port 1111 |
Tasks
The devenv environment uses tasks to manage build dependencies.
Tasks are defined in devenv.nix and can be run with devenv tasks run <task>.
Available Tasks
| Task | Description | Dependencies |
|---|---|---|
docs:build | Build documentation EPUB (only rebuilds if files changed) | None |
docs:zola-build | Build documentation portal with mdBook and Cargo docs | None |
deps:native | Build MuPDF and wrapper for native development | None |
build:kobo | Build for Kobo device (Linux only) | docs:build |
How Tasks Work
Tasks with dependencies automatically run their dependencies first. For example:
# This will first run docs:build (if needed), then build for Kobo
devenv tasks run build:kobo
The docs:build task uses execIfModified to only rebuild when documentation files have actually changed.
Documentation Portal
Cadmus provides a unified documentation portal that combines user guides, API reference, and contribution guides in one place.
Building and Serving Locally
To build the documentation portal:
cadmus-docs-build
This runs the full build pipeline:
- Builds the mdBook user guide (
docs/book/html/) - Generates Rust API documentation (
target/doc/) - Builds the Zola landing page and integrates all documentation
To serve the portal locally with live reload:
cadmus-docs-serve
The portal will be available at http://localhost:1111 with automatic rebuilds when you change documentation files or Rust code.
Documentation Structure
The portal provides three integrated sections:
- Landing Page (
/) - Overview and feature highlights - User Guide (
/guide/) - User-facing documentation from mdBook - API Reference (
/api/) - Auto-generated Rust API documentation
All three sections are deployed as a single artifact to GitHub Pages at https://ogkevin.github.io/cadmus/.
Continuous Integration
Documentation is automatically built and validated on every pull request and deployed on push
to main or master. The CI pipeline checks:
- mdBook documentation compiles
- Rust code documentation is valid
- Zola landing page builds successfully
Running Tests
Tests require the TEST_ROOT_DIR environment variable to be set:
TEST_ROOT_DIR=$(pwd) cargo test
This is automatically configured in CI but must be set manually for local testing.
Platform Support
Linux (Full Support)
Linux provides full development capabilities including:
- Native development (emulator, tests)
- Cross-compilation for Kobo devices using the Linaro ARM toolchain
- Git hooks (actionlint, shellcheck, shfmt, markdownlint, prettier)
The Linaro toolchain is automatically added to PATH and provides arm-linux-gnueabihf-* commands.
macOS (Native Development Only)
macOS supports native development but has some limitations:
| Feature | Status | Notes |
|---|---|---|
| Native builds | Supported | Emulator and tests work |
| Cross-compilation | Not supported | Linaro toolchain is Linux-only |
macOS-Specific Notes
Cross-compilation for Kobo: The Linaro ARM cross-compilation toolchain consists of x86_64 Linux ELF binaries that cannot run on macOS. To build for Kobo devices on macOS, use Docker with a Linux container or a Linux VM.
MuPDF build: On macOS, the native setup script manually gathers pkg-config CFLAGS for system libraries because MuPDF’s build system doesn’t properly detect them on Darwin.
Observability Stack
The devenv includes a full observability stack for development:
# Start all services
devenv up
# In another terminal, run the instrumented emulator
cadmus-dev-otel
Services available after devenv up:
| Service | URL | Purpose |
|---|---|---|
| Grafana | http://localhost:3000 | Dashboards and exploration |
| Tempo | http://localhost:3200 | Distributed tracing |
| Loki | http://localhost:3100 | Log aggregation |
| Prometheus | http://localhost:9090 | Metrics |
| OTLP Collector | http://localhost:4318 | Telemetry ingestion |
For more details on telemetry, see OpenTelemetry Integration.
Troubleshooting
Shell takes a long time to start
The first devenv shell invocation downloads and builds dependencies, which can take several
minutes. Subsequent invocations are cached and should be fast.
Tests fail with “TEST_ROOT_DIR must be set”
Set the environment variable before running tests:
TEST_ROOT_DIR=$(pwd) cargo test
Local Configuration
Create devenv.local.nix to override settings without modifying the tracked configuration:
{ pkgs, ... }:
{
env = {
# Example: Set TEST_ROOT_DIR automatically
TEST_ROOT_DIR = builtins.getEnv "PWD";
};
}
This file is gitignored and won’t affect other contributors.
OpenTelemetry Integration
Cadmus supports exporting logs and traces to OpenTelemetry-compatible backends when built with the otel feature flag.
Overview
The OpenTelemetry (OTEL) integration allows Cadmus to export both structured logs and distributed traces to observability platforms like Grafana Loki/Tempo, Jaeger, or any OTLP-compatible service. Both logs and traces are first-class features that work together to provide comprehensive observability for monitoring application behavior, debugging issues, and analyzing performance.
Architecture
The telemetry system consists of three main components:
- Logging: JSON-structured logs written to disk via
tracing_subscriber - Tracing: Distributed traces capturing execution flow and timing
- OTLP Export: Optional export of both logs and traces to a remote OTLP endpoint
When the otel feature is enabled, Cadmus initializes:
- Tracer Provider: Exports distributed traces to
<endpoint>/v1/tracesusing batch span processors for async delivery - Logger Provider: Exports structured logs to
<endpoint>/v1/logsusing batch log processors
Each Cadmus run is assigned a unique Run ID (UUID v7) that ties together all logs and traces for that session, enabling correlation between trace spans and log events.
Building with OTEL Support
To enable OpenTelemetry, build Cadmus with the otel feature:
cargo build --features otel
Configuration
Settings File
Configure OpenTelemetry in your Settings.toml:
[logging]
enabled = true
level = "info"
max-files = 3
directory = "logs"
otlp-endpoint = "https://otel.example.com:4318"
Configuration Options
- enabled: Enable or disable logging entirely
- level: Minimum log level (
trace,debug,info,warn,error) - max-files: Number of log files to retain (0 = keep all)
- directory: Path to log directory (relative to installation directory)
- otlp-endpoint: OTLP HTTP endpoint URL (optional)
Environment Variables
You can override the OTLP endpoint using an environment variable:
export OTEL_EXPORTER_OTLP_ENDPOINT="https://otel.example.com:4318"
./cadmus
Environment variables take precedence over Settings.toml configuration.
Log Level Control
The log level can be controlled via the RUST_LOG environment variable, which overrides the level setting:
# Enable debug logs for all modules
export RUST_LOG=debug
./cadmus
# Enable trace logs only for specific modules
export RUST_LOG=cadmus::view=trace,info
./cadmus
Distributed Tracing
Distributed tracing captures the execution flow of operations through the application, providing timing information and context about how different components interact.
How Tracing Works in Cadmus
When the otel feature is enabled, Cadmus automatically instruments key operations using the tracing crate.
Each instrumented function creates a span that records:
- Function name and module path
- Input parameters (selectively captured)
- Execution duration
- Return values (at TRACE level)
- Hierarchical relationships between spans
Spans are organized hierarchically, showing which operations triggered which other operations, making it easier to understand execution flow and identify performance bottlenecks.
Instrumentation
View components in Cadmus are instrumented at critical chokepoints:
handle_eventmethods: Capture event flow through the UI hierarchy with event type and return valuerendermethods: Capture rendering operations with rectangle dimensions for layout debugging
All instrumentation uses conditional compilation (#[cfg_attr(feature = "otel", ...)]) to ensure zero runtime overhead
when the feature is disabled.
For detailed instrumentation guidelines and examples, see .github/instructions/rust-instrumentation.instructions.md.
Resource Attributes
Each telemetry export (both logs and traces) includes the following resource attributes:
- service.name: Always
cadmus - service.version: Git version from build metadata
- cadmus.run_id: Unique identifier for the application run
- hostname: System hostname
Log File Format
Logs are written as newline-delimited JSON to files named:
cadmus-<run_id>.json
Each log entry includes:
- timestamp: ISO 8601 formatted timestamp
- level: Log level (TRACE, DEBUG, INFO, WARN, ERROR)
- target: Module path where the log originated
- fields: Structured log data
- spans: Active tracing spans providing context
Documentation Deployment
Cadmus documentation is deployed to Cloudflare Pages.
URLs
- Production: https://cadmus-dt6.pages.dev/
- PR Preview:
https://pr-{NUMBER}.cadmus-dt6.pages.dev/
Reviewing Documentation Changes
When you open a pull request that modifies documentation files, a preview deployment is automatically created. The PR will show a deployment status with a link to the preview URL.
Preview URLs follow the pattern: https://pr-{NUMBER}.cadmus-dt6.pages.dev/
Local Development
Build and serve documentation locally:
devenv shell
cadmus-docs-build # Build all documentation
cadmus-docs-serve # Serve at http://localhost:1111
Or manually:
cd docs && mdbook build && cd ..
cargo doc --no-deps --document-private-items
cd docs-portal && zola serve --base-url http://localhost
Build Process
Documentation is built from three sources:
- mdBook (
docs/) - User and contributor guides - Cargo doc (
crates/) - Rust API documentation - Zola (
docs-portal/) - Documentation portal that combines everything
The GitHub Actions workflow (.github/workflows/cadmus-docs.yml) handles building and deploying automatically on every push to main and for every pull request.