You already own this
Exact ISBN match and at least one copy on your shelf.
Put it back — here is the shelf.
Librero is a small, self-hosted catalogue of the books you own. Scan the barcode and it answers in seconds — including which edition is already on your shelf.
docker compose up -dRuns on a Raspberry Pi, a NAS or a small VPS. One directory to back up.
Dune · Frank Herbert
Ace, 2010 paperback
Placeholder — scan verdict screen, 1170×2532
Exact ISBN match. Librero names the shelf your copy is on, so you can put this one back.
Four verdicts, no ambiguity. The scanner and the answer are the product; the catalogue is the supporting cast.
Everything else in the application exists to keep these four accurate.
Exact ISBN match and at least one copy on your shelf.
Put it back — here is the shelf.
Same book, different printing. Librero lists the editions you hold.
A real decision, not an accident.
You recorded wanting this book, and you do not own it yet.
Buy it.
No match anywhere in your library or wishlist.
Add it in one tap, or wishlist it.
Open Librero, aim at the barcode. Or type a title.
Against your shelf first, then your wishlist.
With the edition you hold and the shelf it sits on.
One tap, then back to browsing the shop.
They are different questions, and the second is the one that matters at the till. Librero keeps a Work, its Editions and the Copies you actually own as three separate things — which is why it can tell you that the hardcover in your hand is not the paperback at home.
Placeholder frames — drop real captures into public/screenshots/ and they replace these one for one.
Requirements: Docker and Docker Compose. Runs comfortably on a Raspberry Pi, a NAS or a small VPS.
cp .env.example .env # Fill in SESSION_SECRET and ADMIN_PASSWORD: openssl rand -base64 48 # -> SESSION_SECRET docker compose up -d
Then open https://localhost (or your LIBRERO_HOSTNAME), sign in with ADMIN_USERNAME and ADMIN_PASSWORD, and choose a real password when asked — you are prompted immediately.
Browsers only grant camera access on a secure origin, so a phone talking to http://… cannot scan. The bundled Caddy service handles this automatically: a real hostname in LIBRERO_HOSTNAME gets a Let’s Encrypt certificate, and the default gets a local one. localhost counts as a secure origin, so development works on the machine’s own webcam.
| Variable | Required | Notes |
|---|---|---|
SESSION_SECRET | yes | ≥32 characters. The container refuses to start without it. Changing it signs everyone out. |
ADMIN_PASSWORD | first run | Temporary; the holder must change it at first sign-in. |
ADMIN_USERNAME | no | Default admin. Used only when the users table is empty. |
LIBRERO_HOSTNAME | no | Caddy's site address. A real domain gets Let's Encrypt automatically. |
LIBRERO_DATA_DIR | no | ./data locally, /data in Docker. |
LIBRERO_CONTACT_EMAIL | recommended | Sent to Open Library so they can reach you about traffic, per their API policy. |
GOOGLE_BOOKS_API_KEY | no | Raises quota only; lookups work without it. |
MAX_UPLOAD_MB | no | Default 100. |
LIBRERO_DATA_DIR holds librero.db and uploads/ (covers and ebooks) and nothing else. Back up that one directory and you have backed up the whole application; there is no other state.
tar czf librero-backup.tar.gz ./data
Being explicit here is a feature. It is how you decide quickly.
| Application | Next.js 16 (App Router) + React 19 + TypeScript |
|---|---|
| Database | SQLite via better-sqlite3, Drizzle ORM with checked-in migrations |
| Search | SQLite FTS5 |
| Interface | Tailwind v4 + shadcn/ui |
| Validation | Zod |
| Auth | argon2id password hashing, signed JWT session cookie |
| Tests | Vitest and Playwright |
| Deployment | Docker + Caddy |