Ottershelf is an open-source Android app for reading, tracking and annotating the books on a self-hosted BookOrbit server. It opens EPUB and the other ebook formats, PDFs and comics from your server, keeps books on the phone for offline reading, syncs your reading position and sessions back to the server, and brings BookOrbit’s dashboard, statistics, highlights and achievements to a native Kotlin and Jetpack Compose interface. It requires your own BookOrbit server (or an account on one); it contains no server, no store and no books of its own.
Ottershelf is a modified version of BookOrbit and is not an official BookOrbit release. It is not affiliated with, approved, sponsored or endorsed by the BookOrbit project.
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
http:// addresses are refused, and certificates
from user-installed certificate authorities are not trusted./api/v1). It was built and tested against the
BookOrbit source of 23 September 2026 (upstream commit 1dd29c7) [BookOrbit release version].
Older servers may lack routes it uses; newer ones may change them.99:3B:5B:6A:81:9E:7E:22:A3:60:C9:96:CD:1F:D8:BA:93:9E:5C:BF:63:EA:A9:B6:F4:EF:9A:8F:0F:68:54:D6.The Play and GitHub builds use the same package ID (io.github.ottershelf) and the same signing
key: the project’s own key is also the app signing key on Google Play (Play App Signing with an
uploaded key), so either build can update the other without reinstalling.
You need JDK 21 and the Android SDK (compile SDK 37). The Gradle wrapper downloads the matching Gradle version.
./gradlew assembleDebug # debug APK: app/build/outputs/apk/debug/
./gradlew bundleRelease # release App Bundle: app/build/outputs/bundle/release/
./gradlew assembleRelease # release APK (unsigned unless a signing key is configured)
./gradlew testDebugUnitTest # JVM and Robolectric unit tests
./gradlew verifyRoborazziDebug # screenshot tests against recorded images
./gradlew recordRoborazziDebug # record the screenshot images again
On Windows, use gradlew.bat in place of ./gradlew.
Signing. The repository contains no keys, passwords or other secrets. A release build is
signed only when a properties file outside the repository provides the key: the file named by the
Gradle property ottershelf.keystore (for example -Pottershelf.keystore=/path/to/keystore.properties),
or else ~/.ottershelf/keystore.properties. It holds four values:
storeFile=release.jks # relative to this file's folder, or an absolute path
storePassword=...
keyAlias=...
keyPassword=...
Without the file, or with a value missing (a build warning names it), assembleRelease produces
an unsigned APK and everything else, including the debug build and the tests, works as usual.
.gitignore excludes *.jks, *.keystore, *.p12 and keystore.properties.
| Path | Contents |
|---|---|
app/src/main/java/.../core/ |
Data layer: API client and models, session and token encryption, sync, downloads, tracking, settings, theme preferences |
app/src/main/java/.../ui/ |
Navigation shell, theme, icons and shared Compose components |
app/src/main/java/.../feature/ |
One package per feature: login, home, library, book, reader, pdf, comics, downloads, notes, quotes, scan, requests, bookedit, seriesnext, timer, calendar, history, stats, achievements, settings |
app/src/main/assets/foliate/ |
foliate-js (from BookOrbit’s copy, modified) and its vendored libraries |
app/src/main/assets/reader/ |
The ebook reader’s page and its bridge to the app |
app/src/test/ |
Unit, Robolectric and Roborazzi screenshot tests |
app/src/androidTest/ |
On-device tests (see CONTRIBUTING.md) |
tools/ |
Icon generation, device-test runner, reader script tests |
docs/brand/ |
Artwork and icon sources |
fastlane/metadata/ |
Store listing texts and images |
docs/play/ |
Google Play Console drafts |
ARCHITECTURE.md describes the packages, patterns and conventions in detail.
Ottershelf has no analytics, advertising, crash reporting or tracking of any kind, and contains no third-party SDK that collects data. It connects only to the server address you type and, only when you tap Look up on selected text, to Wiktionary and Wikipedia, through a separate connection that never carries your account’s token. The camera is used only when you choose to scan an ISBN, photograph a page for a quote or take a cover photo; images are processed on the phone, and a cover photo is sent only to your own server, only when you confirm it. Sign-in tokens are encrypted with an Android Keystore key, and the app’s data is excluded from backups. See PRIVACY.md for the full privacy policy.
Ottershelf is free software: you can redistribute it and/or modify it under the terms of the
GNU Affero General Public License, version 3 only (AGPL-3.0-only), together with
BookOrbit’s additional terms, as permitted by section 7 of that licence. It
is distributed without any warranty. Copyright notices and the list of modifications are in
NOTICE.
Ottershelf contains material from BookOrbit (its copy of foliate-js, and code ported from its web client and API types). As BookOrbit’s additional terms require:
Ottershelf is built with open-source components that keep their own licences, including foliate-js, zip.js, fflate, Lucide icons, Jetpack Compose and AndroidX, CameraX, OkHttp, Coil, Telephoto, zxing-cpp and Tesseract. See THIRD_PARTY_NOTICES.md.
Bug reports, feature requests and pull requests are welcome. See CONTRIBUTING.md, the code of conduct, and SECURITY.md for reporting vulnerabilities.