Data Model¶
PlainShelf is filesystem-first: the shelf directory on disk is the source of truth, and the server reads and writes it directly. There is no separate database.
Shelf layout¶
A typical shelf looks like this:
{shelf}/
├─ books/
│ ├─ {book1-folder}.bookpkg/
│ ├─ {layer1}/
│ │ └─ {book2-folder}.bookpkg/
│ └─ {layer2}/
│ └─ {layer3}/
│ └─ {book3-folder}.bookpkg/
└─ app/
├─ library.lock
└─ tmp/
books/¶
Source of truth. This directory contains all user-owned data: book metadata, text files, cover images, and other long-lived files. Books can be nested under layers by placing them inside sub-directories.
app/¶
Runtime state used by the server (file lock, temporary files). This data is considered rebuildable and is not user data.
Book folder (.bookpkg/)¶
Each book is stored as a directory whose name ends with .bookpkg:
{book-folder}.bookpkg/
├─ book.json
├─ CURRENT_VERSION_LOCATION.txt
├─ cover.(jpg|png|webp)
└─ sources/
└─ {source-id}/
├─ source.txt
└─ meta.json
| Path | Description |
|---|---|
book.json |
Book metadata (title, authors, tags, language, …) |
CURRENT_VERSION_LOCATION.txt |
Points to the active source used for reading |
cover.(jpg\|png\|webp) |
Optional cover image |
sources/{source-id}/source.txt |
The plain-text content for this source |
sources/{source-id}/meta.json |
Source-level metadata |
Book IDs¶
The book ID is derived from the folder name, not from the display title. This means you can rename a book's title in book.json without breaking reading progress, bookmarks, or any external references.
Design principles¶
- Human-readable — the shelf directory can be opened and inspected with any file manager or text editor.
- Backup-friendly — because everything is plain files, the shelf is trivially backed up with
cp,rsync, or committed to Git. - Rebuildable runtime state — the
app/directory can be deleted and the server will recreate it on the next startup.