# Bookhoard logo Bookhoard A modern self-hosted media library system built with Go, PostgreSQL, HTMX, and Tailwind CSS featuring **universal cross-device sync**, beautiful dark themes, and comprehensive media management. ## โœจ Why Bookhoard? **๐Ÿ”„ Universal Sync**: Your reading position, bookmarks, highlights, and notes sync automatically between KOReader and the web - with native Kobo sync and mobile apps coming later. **๐Ÿ“ฑ Multi-Library**: Organize your ebooks, comics, and manga with per-library folders and smart collections. **๐ŸŽจ Beautiful UI**: 11 gorgeous dark themes with responsive design that works on any device. **๐Ÿ”’ Secure**: JWT authentication, bcrypt password hashing, rate limiting, and no passwords on devices. --- ## ๐Ÿš€ Quick Start ### Prerequisites - **Podman** (recommended) or Docker - **5 minutes** of your time ### Installation (no source checkout needed) Create a directory on the Docker host. You only need the compose file, an `.env`, and an `uploads` folder โ€” everything else rides the published image. ```bash mkdir bookhoard && cd bookhoard mkdir uploads ``` **`docker-compose.yml`** โ€” copy this wholesale: ```yaml services: db: image: postgres:15-alpine container_name: bookhoard_db environment: POSTGRES_DB: bookhoard POSTGRES_USER: postgres POSTGRES_PASSWORD: ${DBPASS} PGPORT: 5432 volumes: - postgres_data:/var/lib/postgresql/data - ./uploads:/app/uploads healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres"] interval: 30s timeout: 5s retries: 3 start_period: 10s db-init: image: postgres:15-alpine container_name: bookhoard_db_init environment: POSTGRES_USER: postgres POSTGRES_DB: bookhoard PGHOST: db PGUSER: postgres PGPASSWORD: ${DBPASS} DBSRVPASS: ${DBSRVPASS} entrypoint: ["bash", "-c"] command: - | set -e : "${DBSRVPASS:?DBSRVPASS required in .env}" psql -v ON_ERROR_STOP=1 -v role_pass="$DBSRVPASS" <<'EOSQL' DO $$$$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'bookhoard_srv') THEN CREATE ROLE bookhoard_srv LOGIN; END IF; END $$$$; ALTER ROLE bookhoard_srv LOGIN PASSWORD :'role_pass'; DO $$$$ DECLARE r RECORD; BEGIN FOR r IN SELECT tablename FROM pg_tables WHERE schemaname = 'public' LOOP EXECUTE format('ALTER TABLE public.%I OWNER TO bookhoard_srv', r.tablename); END LOOP; FOR r IN SELECT sequencename FROM pg_sequences WHERE schemaname = 'public' LOOP EXECUTE format('ALTER SEQUENCE public.%I OWNER TO bookhoard_srv', r.sequencename); END LOOP; FOR r IN SELECT p.oid::regprocedure AS fn FROM pg_proc p JOIN pg_namespace n ON p.pronamespace = n.oid WHERE n.nspname = 'public' AND p.prokind = 'f' LOOP EXECUTE format('ALTER FUNCTION %s OWNER TO bookhoard_srv', r.fn); END LOOP; END $$$$; ALTER SCHEMA public OWNER TO bookhoard_srv; ALTER DATABASE bookhoard OWNER TO bookhoard_srv; EOSQL depends_on: db: condition: service_healthy restart: "no" app: image: git.linuxhg.com/bookhoard/bookhoard:${IMAGE_TAG:-latest} container_name: bookhoard restart: unless-stopped environment: DATABASE_HOST: db DATABASE_PORT: 5432 # Runtime role: bookhoard_srv (created above, confirmed by db-init). # NEVER connect the app as the bootstrap superuser โ€” it is subject # to row-level security (per-user library visibility) by design. DATABASE_USER: bookhoard_srv DATABASE_PASSWORD: ${DBSRVPASS} JWT_SECRET: ${JWT_SECRET} SERVER_PORT: 8765 ports: - "8765:8765" depends_on: db: condition: service_healthy db-init: condition: service_completed_successfully volumes: - ./uploads:/app/uploads - bookhoard_conversion_cache:/app/cache/kepub volumes: postgres_data: bookhoard_conversion_cache: ``` **`.env`** โ€” create alongside the compose file and fill in the three secrets (no special characters): ```bash openssl rand -hex 32 # JWT_SECRET openssl rand -hex 16 # DBPASS (bootstrap database password) openssl rand -hex 16 # DBSRVPASS (runtime role password) ``` Then start: ```bash docker compose pull docker compose up -d open http://localhost:8765 ``` Notes: - `db-init` runs on every startup and converges the runtime role (idempotent, ~1 second); the app refuses to start before it succeeds. - `uploads/` on the host is where your book files live; libraries scan folders under it from the Admin โ†’ Libraries page. - First user to register automatically becomes an admin. - Upgrades: `docker compose pull && docker compose up -d` (pin a version with `IMAGE_TAG` in `.env`). --- ## ๐Ÿ“– Key Features ### Universal Cross-Platform Sync - **Real-Time Progress**: Turn a page on your e-reader, see it in your browser - **Format-Aware**: EPUB CFI, page numbers, percentages - all handled correctly - **Offline Queue**: Changes sync when you reconnect, priority-processed - **Conflict Resolution**: Smart handling when same book read on multiple devices - **Book Matching**: Automatic matching using SHA-256, ISBN, UUID - **OPDS Catalog**: Wireless book delivery to e-readers over Wi-Fi - **Format Conversion**: On-the-fly EPUBโ†’KEPUB conversion (for upcoming native Kobo support) ### Media Management - **Calibre Integration**: Automatic metadata import from Calibre `metadata.opf` sidecar files - **Smart Search**: Partial matching with fuzzy search fallback for typos - **Advanced Filtering**: Filter by author, series, genre, language, year, cover images - **Dynamic Sorting**: By title, author, date added, published date, page count, series - **Rich Metadata**: Title, author, series, publisher, ISBN, language, edition, tags - **5-Star Ratings**: Half-star precision (1-10 scale) - **Notes & Highlights**: Color-coded annotations with linked notes - **Usage Analytics**: Reading statistics, device usage, popular books ### Smart Collections - **Auto-Assign Rules**: Automatically add books based on genre, author, series, tags, language, publisher, year - **Device Shelf Mappings**: Map collections to device shelves (used by native Kobo sync, coming soon) - **Test Before Creating**: Preview which books match your rules ### Library Organization - **Multi-Library Support**: Ebooks, Comics, and Manga with type-specific file formats - **Multiple Folders**: Add multiple scanning folders per library - **Visibility Control**: Admins control which libraries each user can see - **Background Scanning**: Auto-scan with per-user frequency settings - **Watch Mode**: Real-time file system monitoring for instant updates ### Security - **JWT Authentication**: Short-lived access tokens (1 hour) with refresh tokens (7 days) - **Strong Passwords**: Complexity requirements enforced (8+ chars, uppercase, lowercase, number, special) - **Account Lockout**: 5 failed attempts = 15-minute lockout - **Rate Limiting**: 10 requests/minute on auth endpoints - **Input Validation**: Comprehensive validation on all inputs - **No Passwords on Devices**: Web-based device approval with QR codes --- ## ๐Ÿ“š Documentation ### For Users & Self-Hosters - **[docs/user/calibre-integration.md](docs/user/calibre-integration.md)** - Calibre library integration - **[docs/user/sync-guide.md](docs/user/sync-guide.md)** - Understanding and using universal sync - **[docs/user/devices/koreader-setup.md](docs/user/devices/koreader-setup.md)** - KOReader configuration - **[docs/user/devices/kobo-setup.md](docs/user/devices/kobo-setup.md)** - Kobo e-reader configuration (coming soon) - **[docs/user/user-guide.md](docs/user/user-guide.md)** - General user guide - **[docs/user/admin-guide.md](docs/user/admin-guide.md)** - Admin features and configuration - **[docs/user/settings-guide.md](docs/user/settings-guide.md)** - Settings and preferences ### For Developers - **[docs/developer/api/api-reference.md](docs/developer/api/api-reference.md)** - Complete API documentation - **[docs/developer/android-app.md](docs/developer/android-app.md)** - Android app design & roadmap - **[docs/contributing/development.md](docs/contributing/development.md)** - Development workflow --- ## ๐ŸŽฏ Supported Devices | Platform | Sync | OPDS | Status | | ---------------- | ---- | ---- | ------------------------------------------------------------- | | **Web Browser** | โœ… | โœ… | Full support | | **KOReader** | โœ… | โœ… | Runs on Kindle, Kobo, PocketBook hardware | | **Kobo Devices** | ๐Ÿšง | ๐Ÿšง | Native Kobo sync coming soon (use KOReader on Kobo today) | | **Mobile Apps** | ๐Ÿšง | ๐Ÿšง | Native Android app in design ([docs](docs/developer/android-app.md)); iOS later | --- ## ๐Ÿ’พ Network Storage (NAS / NFS / SMB) Libraries on network mounts are supported for everyday use โ€” serving, reading, progress, and annotation sync all work over the mount โ€” with a few behaviors worth knowing: - **Outage hysteresis.** When the mount drops, files miss one scan (marked missing, hidden) then a second (archived, still recoverable). When the mount returns โ€” by path or by identical content elsewhere โ€” everything is restored automatically: annotations, progress, and covers included. - **Retention purge.** Archived items older than `ARCHIVE_RETENTION_DAYS` (default **90**, `0` = never auto-purge) are deleted for good by library scans, annotations included. A NAS offline longer than the retention window loses those books' metadata on return. - **Deleting while the storage is down is refused.** Deleting a book cascades its annotations, so deletes are rejected with a clear error while the item's library storage is unreachable. Archive the item instead, or retry when the mount is back. - **Watch mode does not fire on network mounts** (inotify doesn't work over NFS/SMB). The periodic scan is the change detector. - **Latency** โ€” folder reachability checks are cached ~30s; large libraries on slow mounts will still feel slower than local disk. --- ## ๐Ÿ›  Tech Stack - **Backend**: Go 1.25+ with Echo framework - **Database**: PostgreSQL 15+ with pgx v5 - **Frontend**: HTMX + Tailwind CSS + Templ - **Auth**: JWT tokens with bcrypt password hashing - **Container**: Podman (Docker compatible) --- ## ๐Ÿงช Testing ```bash # Run all tests make test-all # Run integration tests (with test mode) make test-integration # Run Bruno OpenCollection YAML API tests npm install -g @usebruno/cli bruno run ``` --- ## ๐Ÿ“Š Project Status **Version**: 1.0 **License**: AGPL-3.0 **Status**: Production-ready โœ… --- ## ๐Ÿค Contributing We welcome contributions! Please see [docs/developer/development.md](docs/developer/development.md) for guidelines. --- ## ๐Ÿ“„ License AGPL-3.0 - See [LICENSE](LICENSE) file for details. --- **Built with โค๏ธ using Go, PostgreSQL, HTMX, and Tailwind CSS**