diff --git a/.env.example b/.env.example
index bc6123dc..adde01c1 100644
--- a/.env.example
+++ b/.env.example
@@ -28,6 +28,40 @@ DASHBOARD_QUOTE_CACHE_TTL=86400 # in seconds; default 24h
# Frontend settings (build-time)
PUBLIC_DEFAULT_LOCALE=en # UI default locale: en | de
+# Book import search providers
+# At least one source must be able to answer. If every source below is disabled, the
+# backend refuses to start and names the variables in the error.
+OPEN_LIBRARY_IMPORT_SEARCH_ENABLED=true
+
+# BnF (Bibliotheque nationale de France) - no API key needed, HTTPS
+BNF_IMPORT_SEARCH_ENABLED=true
+
+# Inventaire.io - no API key needed, HTTPS, ISBN searches only
+INVENTAIRE_IMPORT_SEARCH_ENABLED=true
+
+# Hardcover and Google Books need credentials before they can answer anything, so they
+# are set to false here. The built-in default is true so that upgrading never silently
+# drops a source; set either to true once its key or token is configured:
+# HARDCOVER_APP_API_TOKEN (see https://codebude.github.io/librislog/guide/api-keys)
+# GOOGLE_BOOKS_API_KEY
+# A source left enabled without its key makes no request and is reported as skipped,
+# so leaving the default is harmless, just noisier in the search progress list.
+# Setting GOOGLE_BOOKS_IMPORT_SEARCH_ENABLED=false also disables the on-demand
+# "Search Google Books too" button.
+HARDCOVER_IMPORT_SEARCH_ENABLED=false
+GOOGLE_BOOKS_IMPORT_SEARCH_ENABLED=false
+
+# Library of Congress - no API key needed
+# WARNING: This source is reached over PLAIN HTTP (unencrypted) on port 210; the LC
+# offers no HTTPS for its SRU endpoint. Book queries and responses travel unencrypted
+# and can be observed or tampered with on the network path. Only public bibliographic
+# data is transferred. Set this to false if unencrypted egress to port 210 is not
+# acceptable in your environment. All other sources use HTTPS.
+LIBRARY_OF_CONGRESS_IMPORT_SEARCH_ENABLED=false
+
+# Deutsche Nationalbibliothek (German National Library) - no API key needed, HTTPS
+DNB_IMPORT_SEARCH_ENABLED=true
+
# Cover scraper settings
THALIA_COVER_SEARCH_ENABLED=false # For research only. Do not use in production without understanding the legal implications of scraping.
diff --git a/.gitignore b/.gitignore
index ab13a837..3bf39411 100644
--- a/.gitignore
+++ b/.gitignore
@@ -50,6 +50,8 @@ coverage.xml
.hypothesis/
.pytest_cache/
cover/
+# Vitest 5 writes generated reports and artifacts here
+.vitest/
# Translations
*.mo
@@ -232,6 +234,7 @@ node_modules/
*-snapshot.md
# AI tools
+/AGENTS.md
/.memsearch
/.playwright-mcp
/.sverklo
diff --git a/README.md b/README.md
index 417c77ef..c9f0be61 100644
--- a/README.md
+++ b/README.md
@@ -22,7 +22,7 @@
🔒
{$_('publicProfile.page.loginRequiredDesc')}
- {$_('publicProfile.page.loginButton')} + + {$_('publicProfile.page.loginButton')}{book.title}
-({formatAuthors(book.authors)})
-{book.title}
+({formatAuthors(book.authors)})
{$_('search.noResultsFor', { values: { query: libraryQuery.trim() } })}
@@ -534,9 +551,10 @@ {/each} {#if filteredBooks.length > libraryLimit} - + {/if} {/if} {/if} @@ -553,16 +571,21 @@