尚未收集仓库信息。
尚未检测到更新。GitHub 发布版本后会显示在这里。
Self-hosted personal library catalog — barcode-first cataloging, multi-media (books, BD, audio, films), loans, runs on your NAS. Rust + Axum + MariaDB + HTMX. FR/EN. WCAG 2.2 AA.
翻译准备中
取自仓库 README
正在加载完整 README。
查看完整 README ↗Personal library cataloging for home collectors.
Status: in production since v1.1.1 (2026-05-14). 12 epics shipped, the latest refining how an AI agent's proposals are reviewed, after its first real use. Current release: v1.22.0. Pre-built images on Docker Hub at gcorbaz/mybibli. See ROADMAP.md for what's coming next.
mybibli is a self-hosted web app to catalog, locate, and loan your personal library. It is designed for a single household, running on your own hardware (typically a NAS or home server). No cloud sync, no telemetry — all data stays on your local network.
Built for collectors who want more than a spreadsheet:
/api/mcp lets an AI agent (Claude Code, or any MCP client over streamable HTTP) with a propose API key read the catalog and propose new series and their missing tomes, missing metadata or covers — with the sources it used. It never writes the catalog: a librarian reviews each proposal on the Proposals page (side-by-side comparison, edit, accept or reject with a reason). LAN-only, Host/Origin-checked; see chapter 13 of the manual and docs/auth-threat-model.md.unsafe-inline/unsafe-eval), CSRF synchronizer-token middleware on every state-changing request (with a server-rendered "session expired" feedback when the token drifts — see docs/auth-threat-model.md), scanner-guard against burst-keyboard input leaking into modals.Live production install (v1.22.0, household NAS, 140+ volumes catalogued and growing):
.sqlx/)script-src 'self', style-src 'self')HttpOnly, SameSite=Lax) + per-session CSRF synchronizer token; argon2 password hashingcargo test (~525 lib unit), #[sqlx::test] (~95 DB integration tests across 10+ files), Playwright (~160 E2E specs across two CI lanes — the seeded-stack suite and a dedicated wizard-E2E lane that runs on a fresh empty database)Pre-built images are published to Docker Hub at gcorbaz/mybibli — :latest tracks the highest semver, individual tags pin to the exact release. For development against the source tree, see Development below.
mybibli is not built to face the open internet. It is designed for one household on one local network, and several deliberate choices follow from that: no limit on login attempts (delegated to a reverse proxy if you run one), no second authentication factor, no Secure attribute on the session cookie unless you ask for it, and a catalogue that is readable without signing in — that last one is a feature, not an oversight. The reasoning for each is written down in docs/auth-threat-model.md §5.
Do not forward a port to it. If you need your library from outside the house, two shapes are supported:
MYBIBLI_COOKIE_SECURE=true as well. That second half is the one people forget: without it the session cookie is still accepted over plain HTTP, and the proxy has bought you less than you think.Report anything that contradicts this posture through SECURITY.md — privately, not in a public issue.
Install v1.1.0 or later. Pre-1.1.0 images (v1.0.0 … v1.0.5) shipped seed migrations that created default admin/admin and librarian/librarian credentials on every fresh install, bypassing the first-launch wizard (#173). The seed gate landed in v1.1.0 and is the install floor; pre-1.1.0 Docker Hub tags have been removed. :latest and every published tag from 1.1.0 onwards are safe — a fresh install greets you with the setup wizard. If you happen to have an older deployment, wipe the database and reinstall before adding any data.
Skipping intermediate versions is supported. mybibli ships releases at a brisk pace; you do not need to upgrade through every intermediate tag. The migration runner applies every pending migration in timestamp order at boot, schema migrations are purely additive (no DROP COLUMN / DROP TABLE), and the few data backfills are idempotent — so a jump from, say, v1.3 directly to the latest tag is safe for the database. Take a backup before upgrading (there is no automatic rollback). See chapter 8 ("Upgrade and migration") of the user manual for the full procedure and the one pre-1.1.4 cover-JPG caveat.
docker-compose.yml declares three named volumes. The first two MUST survive container upgrades; the third is forensic-only:
mybibli_db_data → /var/lib/mysql — your catalog (titles, volumes, loans, etc.) — mandatorymybibli_covers → /app/covers — downloaded cover JPGs (issue #213) — mandatorymybibli_logs → /var/log/mybibli — daily-rotating log files (CR #301, v1.7.0+) — optional but recommended for production debuggability; can be wiped at any timeIf you deploy docker-compose.yml from the repo unchanged (v1.7.0+), you already have all three. Back the data + covers volumes up together — losing one without the other leaves DB references pointing at missing files (or vice versa). The logs volume is forensic-only; it doesn't need backup.
Upgrading from a pre-1.1.4 install? Pre-1.1.4 docker-compose.yml did not declare mybibli_covers, so the cover JPGs lived inside the container's writable layer and were lost on every docker compose up -d after a pull. Adding the volume now preserves covers fetched from this point forward, but does NOT restore the ones that disappeared on prior upgrades. To recover, re-trigger metadata fetch from each affected title's detail page (the "Re-fetch metadata" button). A bulk-fetch admin action is tracked at issue #214.
Upgrading from a pre-1.7.0 install? The mybibli_logs volume is new in v1.7.0. Without it, log files write to the container's ephemeral writable layer and are lost on every docker compose up -d after a pull — which defeats the purpose of CR #301's persistent-log feature. Add this block to your existing docker-compose.yml:
services:
mybibli:
volumes:
- mybibli_logs:/var/log/mybibli # ← add this line
volumes:
mybibli_logs: # ← and this declaration
(Or use a bind mount: - /your/host/path:/var/log/mybibli if you prefer logs visible directly in DSM File Station / your journald shipper. See chapter 12 of the manual.)
Synology DSM / bind-mount users: comment out the mybibli_covers:/app/covers line in docker-compose.yml and uncomment the bind-mount line right below it, then set COVERS_HOST_PATH in your .env to the host path you want — Synology File Station / your rsync routine will see the covers directly. The same pattern applies to mybibli_logs via LOGS_HOST_PATH.
All deployment-time settings are environment variables — there is no
config file. .env.example is the canonical reference: every variable
the Rust binary reads or that docker-compose.yml interpolates is
listed and commented there. Copy it to .env and adjust for your
deployment:
cp .env.example .env
$EDITOR .env
docker compose up
The variables are grouped in seven sections:
DATABASE_URL plus the MYSQL_* parts
used by the bundled db service.HOST, PORT, HOST_PORT (the host-side port
published by Docker).MYBIBLI_LOG_LEVEL (v1.7.0+, tracing filter;
prod-safe default info; also flippable at runtime from
/admin > System without a redeploy), MYBIBLI_LOG_DIR (v1.7.0+,
in-container path for daily-rotating log files; default
/var/log/mybibli; mapped to the mybibli_logs named volume — see
"Persistent storage" above. LOGS_HOST_PATH is an optional
bind-mount override), RUST_LOG (legacy fallback, honored when
MYBIBLI_LOG_LEVEL is unset), APP_LANGUAGE (en, fr, de, or
it — v1.7.0 added DE + IT), COVERS_DIR (filesystem path for
downloaded cover images — in Docker, the /app/covers directory is
mapped to the persistent mybibli_covers named volume.
COVERS_HOST_PATH is an optional bind-mount override).MYBIBLI_COOKIE_SECURE (set to true
only behind HTTPS, see issue
#94),
CSP_REPORT_ONLY, and for the MCP endpoint /api/mcp (story
11-6) MYBIBLI_MCP_ALLOWED_HOSTS (comma-separated Host values
accepted; default localhost,127.0.0.1,::1; on a NAS list the LAN
name/IP your AI agent uses) and MYBIBLI_MCP_ALLOWED_ORIGINS
(Origin values accepted from clients or proxies that forward one;
default none — browser pages are not supported, no CORS).GOOGLE_BOOKS_API_KEY,
OMDB_API_KEY, TMDB_API_KEY. Migrated ONCE into the settings
table at boot; afterwards the admin can rotate or clear them via
/admin > System > Metadata Providers. Re-set them in .env only
when you want the deployment-time value to win on the next reboot.Boolean variables across the codebase use the same strict accept-set,
which avoids the classic footgun where a stale shell value like 0
reads as "set" and silently flips an opt-out.
Shell-level env vars used for build / test commands (SQLX_OFFLINE,
TEST_ADMIN_PASSWORD, MYBIBLI_SETUP_E2E, …) are documented inline
in the Development section below — they do not belong in .env.
# Start the full stack (app + MariaDB + mock metadata providers).
# `MYBIBLI_SKIP_SETUP=1` is baked into the test compose so existing
# seeded specs reach their target routes without going through the
# first-launch wizard.
cd tests/e2e
docker compose -f docker-compose.test.yml up --build
The app listens on http://localhost:8080. The seed migrations create an admin user (admin / admin, role admin) and a librarian (librarian / librarian, role librarian) only when MYBIBLI_SEED_DEV_USERS=1 is set — which is baked into both docker-compose.dev.yml and tests/e2e/docker-compose.test.yml.
ℹ️ Seed users are now gated (issue #173, fixed in 1.1.0). On a fresh install where
MYBIBLI_SEED_DEV_USERSis unset, the seed migrations still apply but the gate insrc/services/seed_gate.rsimmediately hard-deletes any user whose hash still matches the documented seed value, along with the seeded session row whose token is equally public. The first-launch wizard at/setupis therefore reachable on every fresh production deployment, and nothing seeded is left in the Trash for a later Restore click to revive (issue #480 — before that fix the rows were only soft-deleted). An instance upgrading from v1.18.0 or earlier purges the leftovers on its first boot. Set the env var to1only for local development and the E2E test stack — never in production.
Fresh-install wizard. Story 8-8 introduced a first-launch wizard at /setup whose gate predicate is (active_admin_count == 0) AND (settings.setup_completed_at IS NONE). Because the seed migrations create an admin before the gate is first evaluated, the wizard never triggers in practice on a default install — the password-rotation step above is the effective onboarding flow. The wizard can still be exercised by running cargo run against an empty DB with the seed migrations skipped. MYBIBLI_SKIP_SETUP=1 (strict accept-set: 1 / true / TRUE) is the explicit bypass.
cargo check # Fast type-check
cargo build # Full debug build
cargo clippy -- -D warnings # Lint (zero-warnings policy)
SQLX_OFFLINE=true cargo test --lib # ~525 unit tests, ~5 s
cargo test config:: # Module-scoped
cargo test <name> -- --nocapture # Single test with output
docker compose -f tests/docker-compose.rust-test.yml up -d
SQLX_OFFLINE=true \
DATABASE_URL='mysql://root:root_test@localhost:3307/mybibli_rust_test' \
cargo test --test find_similar \
--test find_by_location_dewey \
--test metadata_fetch_dewey \
--test metadata_fetch_race \
--test seeded_users \
--test setup_wizard
Each test gets a fresh DB via #[sqlx::test(migrations = "./migrations")]. The CI db-integration job runs the same allowlist — when adding a new tests/*.rs file, append --test <name> to both this command and .github/workflows/_gates.yml::db-integration.
The Playwright suite has two CI lanes:
cd tests/e2e
# Lane 1 — seeded-stack (most specs). MYBIBLI_SKIP_SETUP=1 baked in.
docker compose -f docker-compose.test.yml up --build -d
npm test # Full suite, parallel mode
# Lane 2 — wizard E2E (story 8-8). Fresh DB, MYBIBLI_SKIP_SETUP unset.
docker compose -f docker-compose.test.yml -f docker-compose.wizard.yml up -d --build --wait
docker compose -f docker-compose.test.yml -f docker-compose.wizard.yml exec -T db \
mariadb -uroot -proot_test mybibli_test -e "DELETE FROM sessions; DELETE FROM users;"
docker compose -f docker-compose.test.yml -f docker-compose.wizard.yml restart mybibli
MYBIBLI_SETUP_E2E=1 npx playwright test specs/journeys/setup-wizard.spec.ts
# Single spec from the seeded suite
npx playwright test specs/journeys/<spec>.spec.ts
A waitForTimeout(...) grep gate (tests/e2e only) blocks any new arbitrary-sleep call — use DOM-state assertions instead. Enforced both locally and in the CI e2e job.
./scripts/e2e-reset.sh does a single-command teardown + rebuild + wait-for-ready when local DB state is polluted. Use after long-running dev sessions where E2E specs see stale rows from prior runs.
Migrations live in migrations/. SQLx offline cache in .sqlx/ is checked into the repo and must stay in sync:
cargo sqlx prepare # Regenerate after query changes
cargo sqlx prepare --check --workspace -- --all-targets
Locale files in locales/{en,fr,de,it}.yml — four locales, and a key
must exist in all four. tests/locale_parity.rs fails the build on any
key present in one file and missing from another (it also checks that
every translation carries the same %{...} placeholders as the English
reference). After adding or renaming keys:
touch src/lib.rs && cargo build # Force proc-macro rebuild (rust-i18n)
cargo test --test locale_parity # All four files agree
src/
├── routes/ # HTTP handlers — thin, delegate to services
│ ├── admin.rs # Admin shell + tab routing + user management (8-1, 8-3)
│ ├── admin_reference_data.rs # Genres / states / roles / node types CRUD (8-4)
│ ├── admin_system.rs # System settings forms (8-5)
│ ├── auth.rs # Login / logout
│ ├── catalog.rs # Cataloging routes
│ ├── locations.rs # Storage location tree
│ ├── loans.rs # Loans + borrowers
│ ├── setup.rs # First-launch setup wizard (8-8)
│ └── …
├── services/ # Business logic, domain rules
│ ├── admin_health.rs # Health-tab data builders (8-1)
│ ├── admin_system.rs # K/V settings save + cache reload (8-5/8-8)
│ ├── auth.rs # Shared session-rotation chain (8-8)
│ ├── auto_purge.rs # 30-day soft-delete hard-purge (8-7)
│ ├── locking.rs # Optimistic-lock check helpers
│ ├── password.rs # argon2 hashing
│ ├── setup.rs # Setup wizard step resolution + writers (8-8)
│ ├── soft_delete.rs # Soft-delete with table whitelist
│ └── …
├── middleware/ # Axum middleware
│ ├── auth.rs # Session extractor + role gating
│ ├── csp.rs # Content-Security-Policy + hardening headers (7-4)
│ ├── csrf.rs # CSRF synchronizer-token middleware (8-2)
│ ├── htmx.rs # HTMX request/response helpers
│ ├── locale.rs # Locale resolution chain (7-3)
│ ├── logging.rs # tracing layer
│ ├── pending_updates.rs # OOB metadata-update delivery
│ └── setup_gate.rs # First-launch wizard gate (8-8)
├── models/ # DB models + queries (SQLx)
├── metadata/ # External metadata providers + KEYED_PROVIDERS const
├── tasks/ # Background tokio tasks
│ ├── anonymous_session_purge.rs # Daily purge of stale anon sessions (8-2)
│ ├── auto_purge_scheduler.rs # Daily soft-delete hard-purge (8-7)
│ ├── metadata_fetch.rs # Async ISBN→metadata resolution
│ └── provider_health.rs # 5-min provider reachability pings (8-1)
├── config.rs # Env vars + `AppSettings` (DB-backed K/V cache)
├── lib.rs # `AppState` definition
├── main.rs # Startup chain (migrations → settings → registry → routes)
├── templates_audit.rs # Architectural-invariant tests (CSP / CSRF / hx-confirm)
└── error/ # AppError enum + IntoResponse
templates/
├── layouts/ # base.html (admin + library) and bare.html (login + setup)
├── pages/ # Full-page templates (catalog, admin, setup, …)
├── components/ # Reusable Askama macros (cover, similar_titles, setup_progress, …)
└── fragments/ # HTMX partial responses + admin form fragments
static/
├── css/ # Tailwind output
└── js/ # ES modules (csrf.js, scanner-guard.js, inline-form.js, …)
migrations/ # SQLx migrations (timestamped)
locales/ # rust-i18n YAML files (en.yml, fr.yml — keys at root, no language wrapper)
docs/ # Coding conventions + architectural references
tests/
├── *.rs # DB integration tests (#[sqlx::test])
└── e2e/ # Playwright specs + Docker test stacks
├── docker-compose.test.yml # Seeded-stack lane (MYBIBLI_SKIP_SETUP=1)
└── docker-compose.wizard.yml # Wizard-E2E override (MYBIBLI_SKIP_SETUP="")
docs/manual/{en,fr}/, built with docs/manual/build.sh.SECURITY.md — supported versions, and how to report a vulnerability privately.CLAUDE.md — coding conventions, architecture patterns, and the Foundation Rules. The de-facto architecture reference for the shipped code.CONTRIBUTING.md and CODE_OF_CONDUCT.md — how to file, patch, and behave.docs/ci-cd.md — the four CI gates, Docker Hub publishing, release procedure.docs/auth-threat-model.md — the auth surface (CSRF, cookies, session policy) and its accepted posture for the single-tenant LAN/NAS shape. Read it before touching anything in that area.docs/route-role-matrix.md — every route with its role gate and CSRF status.docs/permanent-delete-and-purge.md — soft delete, Trash, FK ordering, and the auto-purge scheduler.docs/error-message-style.md — the contract every error.* i18n key answers to.docs/unimarc-mapping.md — field-to-zone mapping for the UNIMARC-aligned cataloging fields.docs/accessibility-audit.md — the WCAG 2.2 AA audit, with its date and its scope.Versioned under _bmad-output/:
planning-artifacts/product-brief-mybibli.md — product visionplanning-artifacts/prd.md — functional requirements (121 FRs), NFRs, user journeysplanning-artifacts/architecture.md — technical decisions + ARsplanning-artifacts/ux-design-specification.md — UX design (30 UX-DRs)planning-artifacts/epics.md — epic breakdown + FR coverage mapimplementation-artifacts/sprint-status.yaml — live sprint stateimplementation-artifacts/epic-*-retro-*.md — per-epic retrospectives| Epic | Title | Status |
|---|---|---|
| 1 | Je catalogue mon premier livre | ✅ done |
| 2 | Je sais où sont mes livres | ✅ done |
| 3 | Tous mes médias sont gérés | ✅ done |
| 4 | Je gère mes prêts | ✅ done |
| 5 | Mes séries et ma collection | ✅ done |
| 6 | Pipeline CI/CD et fiabilité | ✅ done |
| 7 | Accès multi-rôle & Sécurité | ✅ done |
| 8 | Administration & Configuration | ✅ done |
| 9 | Polish UX & Accessibilité | ✅ done |
| 10 | Mobile UX & sécurité closeout | ✅ done |
| 11 | An AI assistant proposes, the librarian decides | ✅ done |
mybibli has been live in production since v1.1.1 (2026-05-14) on the household NAS that drove the project. v1.0.0 shipped after Epic 9 close (2026-05-10) as the first production-ready build; v1.1.0 added the seed-gate + audit trio (mandatory install floor — see "Installation notes" above); the themed minors v1.2 through v1.8 then delivered the original feature roadmap (browse & find, wishlist, HTTP API, valuation & stats, catalog hygiene, de/it locales + runtime logging, cover handling), each followed by production-driven patch trains. Since v1.8 the project runs in GH-issue-driven polish mode.
Current release: v1.22.0 (2026-11-01) — the closing release of epic 12, proposals after a first real use: review an agent's proposals in any order (staleness per field, #542), rejections that block by field or by image (#535), the agent told where mybibli already searched (#560), title corrections (#563), a proposals list that leads straight to each proposal (#561), ISBNs typed with hyphens (#502) and truncated JPEG covers refused (#558). No migration. On its first afternoon on a real household library, an agent suggested 352 improvements; the librarian kept 245, and 190 books came out better described — most of them titles the usual catalogs had no record of. Before it, v1.21.0 (2026-10-29), the closing release of epic 11 (#493): AI agents, under supervision. A built-in MCP server at /api/mcp lets an AI agent with a new Read + propose API key read the catalog through twelve tools and propose series, missing tomes, metadata and covers, citing its sources; it never changes the catalog. Librarians review each proposal on the new Proposals page (side-by-side comparison, edit, accept, or reject with a reason; outdated proposals apply nothing; a rejection blocks equivalent re-filings for 90 days, admin-configurable). Series can name expected tomes (one click to the wish list, filled automatically when the book is scanned), and the navigation bar stays at the top and switches to the menu below 1280 px (#496). Six additive migrations — back up first. To let an agent on another machine connect, set MYBIBLI_MCP_ALLOWED_HOSTS (default: localhost only). The preceding release, (2026-10-27), carried three production fixes from the same epic: ISBN-10 recognised at scan and in search (), the Library of Congress read through SRU on port 210 (), and the triangle (). Before it, (2026-09-23), showed the last volume number and the last shelf number in use on the admin Health tab, with the next free number after each (). Before it, (2026-09-11), carried the three change requests of a security review run against the 1.18.0 code (, , ).
Release-by-release history lives in ROADMAP.md — one section per version, and the canonical copy. It is deliberately not repeated here: the GitHub releases page carries the same notes as published artifacts, chapter 8 of the user manual carries them offline, and the website roadmap tells the same story for a different audience. See epics.md for the epic breakdown and sprint-status.yaml for the story-by-story state.
Licensed under the GNU Affero General Public License v3.0 or later (AGPL-3.0-or-later). See LICENSE for the full text.
AGPL was chosen deliberately to keep mybibli and any fork freely modifiable by end users, including forks that are hosted as a service: if you run a modified version, you must offer the corresponding source to your users.
/setup until completion. Idempotent — interruptions resume at the right step server-side.<select> dropdown), full keyboard navigation with shortcuts cheat-sheet (?), contextual help-icon tooltips, and an axe-core CI gate that covers every reachable surface including entity-detail routes and the first-launch wizard.MYBIBLI_SKIP_SETUP,
MYBIBLI_SKIP_STARTUP_PURGE. Strict accept-set: only 1 / true
/ TRUE count as "on"; anything else is ignored.v1.20.1v1.20.0v1.19.0