| scripts | ||
| src | ||
| static | ||
| .gitignore | ||
| .npmrc | ||
| .prettierignore | ||
| .prettierrc | ||
| Agents.md | ||
| Dockerfile | ||
| eslint.config.js | ||
| flake.nix | ||
| LICENSE | ||
| module.nix | ||
| package.json | ||
| package.nix | ||
| pnpm-lock.yaml | ||
| pnpm-workspace.yaml | ||
| README.md | ||
| svelte.config.js | ||
| tsconfig.json | ||
| vite.config.ts | ||
JACWF (Just Another Calibre Web Frontend)
JACWF is a lightweight, unpretentious web frontend for your Calibre library. It's designed to be fast, simple, and easy to deploy on NixOS or via Docker.
Features
- Calibre Integration: Reads directly from your
metadata.dband usescalibredbfor uploads. - In-Browser Reader:
- EPUB: Custom "Coffee & Parchment" theme with synced reading progress.
- PDF: Native browser integration.
- Text/Markdown: Simple iframe-based viewer.
- NixOS Ready: Includes a Flake and a NixOS module for easy service management.
- Toggleable Auth: Simple password-based login that can be disabled for local-only use.
- Responsive Design: A bold, "neo-brutalist" aesthetic that works on mobile and desktop.
AI Disclaimer
This project was developed with significant assistance from GitHub Copilot (using Gemini 3 Flash, et al.). The architecture, styling, and Nix integration were co-authored by AI to ensure rapid development and adherence to modern SvelteKit 5 patterns.
Installation
NixOS (Recommended)
Add JACWF to your flake inputs:
inputs.jacwf.url = "github:youruser/jacwf";
Then enable the service in your configuration:
services.jacwf = {
enable = true;
booksPath = "/path/to/your/calibre/library";
# Optional: enable authentication
# auth.enable = true;
# auth.passwordFile = "/run/secrets/jacwf-password";
};
Docker
docker build -t jacwf .
docker run -p 3000:3000 -v /path/to/books:/app/books jacwf
Manual Development
pnpm install
pnpm run dev
Configuration
JACWF uses environment variables for configuration:
JACWF_BOOKS_PATH: Path to your books or Calibre library (default:./books).JACWF_DB_PATH: Path to the internal SQLite database for settings/progress (default:./data/library.sqlite3).JACWF_AUTH_ENABLED: Set totrueto enable the login page.JACWF_PASSWORD: The password for authentication (if enabled).
License
This project is licensed under the GNU General Public License v3.0. See the LICENSE file for details.
- On first visit you will be prompted to pick or create a profile.
- EPUB progress is saved/restored per profile.
- PDF progress is not synced (PDFs open in a new tab, so the app does not control the viewer).
Build & preview
pnpm run build
pnpm run preview
Container (nerdctl / containerd)
This app is a server (SvelteKit routes + SQLite), so it must run as a Node process.
Build
nerdctl build -t library:dev .
Run (bind-mount books + data)
mkdir -p data
nerdctl run --rm -p 3000:3000 \
-v "$PWD/books:/app/books:ro" \
-v "$PWD/data:/app/data" \
-e LIBRARY_DB_PATH=/app/data/library.sqlite3 \
library:dev
Or use the helper script:
chmod +x scripts/nerdctl-build-run.sh
./scripts/nerdctl-build-run.sh
Then open http://localhost:3000.
Notes:
books/is mounted read-only by default. To enable uploads in the container, mount it writable (or run the helper script withBOOKS_RW=1).data/holds the SQLite DB (profiles + progress).
Configuration
LIBRARY_DB_PATH(default:./data/library.sqlite3)- Path to the SQLite database file.
BOOK_SCAN_CACHE_TTL_MS(default:5000)- In-memory scan cache TTL. Set to
0to disable caching.
- In-memory scan cache TTL. Set to
UPLOAD_MAX_BYTES(default:536870912)- Max upload size in bytes (default is 512MB).
Notes / limitations
- Scanning happens on request (cached with a TTL, but no persistent index yet), so very large libraries may feel slower.
- Metadata is intentionally simple right now (filename/folder based).
- EPUB progress sync requires selecting a profile.
- PDF progress is not synced.
- Deployment needs filesystem access to the
books/directory; depending on where you deploy, you may want a dedicated SvelteKit adapter and/or a storage strategy.
Logical next steps
-
Indexing & caching
- Persist a book index in SQLite (upsert on scan) to avoid filesystem walks for every request.
- Add incremental updates by comparing
mtimeand removing missing paths.
-
Better metadata
- EPUB: parse title/author/cover from metadata.
- PDF: extract title/author when available.
- Markdown/TXT: derive title from first heading / first line.
-
Security hardening for file serving
- Add extension allow-listing in
GET /api/book/[...path](defense-in-depth).
- Add extension allow-listing in
-
UX improvements that stay simple
- Display “pretty” title in the reader header (currently shows the path).
- Add a clear “back to library” and show author/title consistently.
-
Progress & “currently reading”
- Show the last 5 in-progress books for the active profile (by last updated progress).
- Consider moving TXT/MD rendering into the app (instead of iframe) so progress can be tracked.
-
PDF progress sync (optional)
- Add an in-app PDF viewer (e.g. pdf.js) if you want cross-device PDF progress.
-
Quality & maintainability
- Add a couple of tests for the scanner (filename parsing, extension filtering, directory walking).
- Add a small API contract test for
/api/books.