Skip to content
audemed44Public

About

Self-hosted book library manager with deep KOReader integration and rich reading statistics

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Shelfloom

Self-hosted book library manager with deep KOReader integration and rich reading statistics. Only meant for my own personal use and it is perpetually in development.

Screenshots

Home dashboard on desktop
Book detail page with the series shelf
Library grid on desktop
Quick search matching books by genre
Reading stats
Web serials row on the home dashboard with new-chapter badges and fetch buttons
Web serials list
Serial chapter list loading more chapters as you scroll
EPUB web reader
A page read in KOReader and the web reader continuing from it
Published ebooks linked to a serial as its first volumes, ahead of generated volumes
Book-length volume suggestions of 500 to 600 pages for a web serial
Serial page offering to merge the ebooks' existing series into the serial's series
Settings
Home dashboard on mobile Book detail page on mobile Library on mobile
Quick search on mobile Reading stats on mobile Web serial detail on mobile
Web reader on mobile

Shelfloom can also be installed to your phone's home screen (Add to Home Screen) and opened full screen like an app. Press ⌘K / Ctrl+K or / anywhere to search.

Quick Start (Docker)

Using the pre-built image (recommended)

Images are published automatically to GHCR on every push to main and on version tags (v*).

Copy the example compose file and edit it for your machine:

cp docker-compose.example.yml docker-compose.yml

The tracked example file is docker-compose.example.yml. Your local docker-compose.yml is ignored so machine-specific paths do not keep showing up as unstaged changes.

Example:

services:
  shelfloom:
    image: ghcr.io/audemed44/shelfloom:latest
    ports:
      - "8000:8000"
    volumes:
      - ./.data:/data
      - ./.data/books:/books
    restart: unless-stopped

Then:

mkdir -p .data/books .data/covers
docker compose up -d

To pin to a specific release, use a version tag instead of latest:

image: ghcr.io/audemed44/shelfloom:v1.0.0

Building from source

git clone https://github.com/audemed44/shelfloom.git
cd shelfloom

mkdir -p .data/books .data/covers

# Start the container (builds the image locally)
docker compose up -d --build

Open http://localhost:8000 — the setup wizard will guide you through creating your first shelf.

Volumes

The default docker-compose.example.yml uses a .data/ directory in the repo root:

Host path Container path Purpose
./.data /data Database and cover images
./.data/books /books Book files (your shelf)

Link back to Foyer

Set HOMEPAGE_URL to your Foyer address (e.g. https://home.example.com) to get a link back to it at the top of the sidebar, and in the More menu on phones.

Add books from Z-Library

With a Z-Library account set (SHELFLOOM_ZLIBRARY_EMAIL and SHELFLOOM_ZLIBRARY_PASSWORD), the Library page gets a Z-Library button: search by title, author or ISBN, and Add to library downloads the EPUB or PDF into the default shelf. Details the file lacks (author, ISBN, year, description, cover) are filled in from Z-Library, then the book opens in the editor to set its series, genres and tags. Each add uses one of the account's daily downloads; a book whose file is already in the library is not downloaded again.

Z-Library has no official API: Shelfloom uses the one its apps use, which can change. Its domain moves too: Settings → Z-Library lists its published domains together with the current list Z-Library's own API gives out, checks which ones answer from your server, and lets you switch domain or add a new one. Take new domains only from Z-Library's own channels: lookalike domains exist to steal account passwords. SHELFLOOM_ZLIBRARY_DOMAIN is only the starting choice (HTTPS_PROXY is honoured if the server needs a proxy).

AI recommendations

With an OpenRouter API key set (OPENROUTER_API_KEY), For You (in the sidebar, or More on phones) suggests what to read:

  • Read next picks from the unread books in your library: the next book of each series and unread standalones. The top picks also show on Home.
  • Discover suggests books and web serials you don't have, optionally searching the web (Royal Road, Reddit, Goodreads, Scribble Hub, Novel Updates) to find newer and lesser-known ones. Books link to a Z-Library search, Royal Road serials to Royal Road.

The model is told what you read, grouped by series and web serial: ratings, reading time, when you last read it, DNFs, genres and tags, the serials you follow and your feedback on earlier picks (not for me, read it, doesn't exist, saved). Reviews and highlights are only sent when allowed in Settings → Recommendations, which is also where the model is picked (any OpenRouter model; the default is Google's Gemini 3.8 Flash) and weekly refreshes are turned on. A refresh sends about 10k tokens per list and costs a cent or a few; each run's tokens, searches and cost are shown under the list.

KOReader Sync and the web reader

Shelfloom runs a sync server for KOReader's built-in Progress sync plugin, and has a web reader for EPUBs (the Read button on a book). Both share one reading position per book, so you can read a chapter in the browser and pick up on your e-reader, or the other way round. No KOReader plugin is needed.

  1. In KOReader, open a book and go to Tools → Progress sync → Custom sync server. Enter http://<your-host>:8000/api/kosync (Settings → KOReader Sync shows the exact address).
  2. Register / Login, with a new account or one created in Settings → KOReader Sync.
  3. Turn on Automatically keep documents in sync. Under Sync behavior, "Sync to a newer state: Silently" and "Sync to an older state: Prompt" work well. Leave Document matching method on Binary.

Books are matched by file, so this works however the files reach the device (Syncthing, USB, OPDS). Positions follow a book when its file changes, including when a web serial volume is rebuilt with chapters added. The web reader converts positions to KOReader's own format; frontend/scripts/koreader-xpointer-check checks that conversion against KOReader's engine.

KOReader .sdr Reading Data

Place .sdr folders alongside their book files in your books directory. Shelfloom will automatically import highlights, bookmarks, and reading sessions during shelf scans.

If you have a KOReader statistics.sqlite3 file, mount it into the container and provide the path when triggering a scan via the API.

Logs

Shelfloom writes one JSON object per line on stderr, for Logbook to collect: every request (failures as errors, changes at info, plain reads at debug) and its background jobs with how long they took: library scans (logged when they change something or fail), serial checks and recommendation runs. A message repeated more than 20 times a minute is held back and counted, at most 600 lines a minute get out, and long values are cut at 4 KB, so a failing Shelfloom can't flood the disk. Docker's own copy is capped in compose (logging:, 3 × 10 MB).

Development

Shelfloom is a Go server (cmd/shelfloom, internal/) that serves the API and the built React frontend (frontend/), with SQLite for storage. Requirements: Go (see go.mod), Node 20+, and poppler-utils for PDFs.

npm install            # root dev tools and the frontend
npm run dev            # Go server on :8000 (data in .data/) and Vite on :5173

Tests

npm test               # go test + vitest
npm run lint           # gofmt + go vet, eslint

Configuration

Variable Default
SHELFLOOM_DB_PATH /data/shelfloom.db Database file
SHELFLOOM_COVERS_DIR /data/covers Cover images
SHELFLOOM_LISTEN :8000 Listen address
SHELFLOOM_SCAN_INTERVAL 300 Seconds between library scans
SHELFLOOM_SERIAL_CHECK_INTERVAL 86400 Seconds between web serial update checks
LOG_LEVEL info debug, info, warn or error (SHELFLOOM_LOG_LEVEL still works)
LOG_FORMAT json text for reading in a terminal
HOMEPAGE_URL Foyer address for the sidebar link
SHELFLOOM_ZLIBRARY_EMAIL Z-Library account email
SHELFLOOM_ZLIBRARY_PASSWORD Z-Library account password
SHELFLOOM_ZLIBRARY_DOMAIN z-library.hn Z-Library domain until one is picked in Settings
OPENROUTER_API_KEY OpenRouter key for recommendations (SHELFLOOM_OPENROUTER_API_KEY wins if both are set)
SHELFLOOM_OPENROUTER_MODEL google/gemini-3.8-flash Model until one is picked in Settings
SHELFLOOM_OPENROUTER_BASE_URL https://openrouter.ai/api/v1 OpenRouter API address (a proxy or test server)
TZ Time zone stats are grouped in

Databases from the earlier Python-based releases open as they are, as long as that release had upgraded them to its last migration (it does so on start).

About

Self-hosted book library manager with deep KOReader integration and rich reading statistics

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages