# ---- API ---------------------------------------------------------------- # Port the Hono API listens on. PORT=3000 # Database driver: sqlite (default) | postgres | mysql DB_DRIVER=sqlite # SQLite: path to the database file (created on first migrate). DATABASE_PATH=./data/bookguardian.db # Postgres / MySQL: connection URL (only read when DB_DRIVER matches). # DATABASE_URL=postgres://user:password@localhost:5432/bookguardian # DATABASE_URL=mysql://user:password@localhost:3306/bookguardian # Timezone the API uses for "today" — the default read date when a book is # marked as read and the "no future dates" check. Match your phone's timezone, # especially in Docker, where the container defaults to UTC. # TZ=Europe/Madrid # Serve the built web app (apps/web/dist) from the API as well, so one process # is one origin. Leave unset for `pnpm dev`; the Docker image sets /app/web. # WEB_DIST=../web/dist # ---- Web build ---------------------------------------------------------- # Build-time only (read by apps/web/vite.config.ts, never at runtime): the # origin baked into the landing page's canonical, Open Graph and Twitter card # tags, which crawlers want absolute because they do not run the SPA. There is # no default host: unset means relative tags (/ and /og.png), which are correct # everywhere but give a chat unfurl no absolute image URL. Every real # deployment should set it to the same value as AUTH_BASE_URL. docker compose # picks it up from here as a build arg; plain Docker: --build-arg PUBLIC_ORIGIN=... # PUBLIC_ORIGIN=https://books.example.com # ---- Accounts (Google sign-in) ------------------------------------------ # OAuth client of type "Web application" from Google Cloud Console → APIs & # Services → Credentials (see README "Google sign-in"). Without these two the # API boots but /api/auth/google answers 503 auth_not_configured. # GOOGLE_CLIENT_ID= # GOOGLE_CLIENT_SECRET= # Public origin people open the app at (no trailing slash). The redirect URI # to register in Google Cloud is exactly ${AUTH_BASE_URL}/api/auth/google/callback. # Cookies are marked Secure when this is https. Default: http://localhost:$PORT; # for `pnpm dev` use the Vite origin (http://localhost:5173), which proxies /api. # AUTH_BASE_URL=https://books.example.com # Signs the short-lived cookie that carries the in-flight sign-in state # (≥ 32 characters, e.g. `openssl rand -base64 32`). Generated per process # when unset, which only means a restart interrupts sign-ins in progress. # AUTH_COOKIE_SECRET= # Optional allow-list: only these emails may create an account (existing # accounts keep working). Comma-separated (spaces, semicolons or newlines are # accepted too), case-insensitive. Unset = anyone with a Google account can # sign in — set it on a personal instance. # AUTH_ALLOWED_EMAILS=you@example.com,partner@example.com # Session lifetime in days (sliding: renewed on use past the halfway point). # AUTH_SESSION_DAYS=30 # ---- Web ---------------------------------------------------------------- # Leave unset: the Vite dev server and `vite preview` proxy /api to the API, # so the SPA uses relative URLs and its session cookie travels with them. # Setting VITE_API_URL bakes an absolute API origin into the bundle, which is # only right for a static deployment against a remote API on the same site. # VITE_API_URL=http://localhost:3000 # Dev/preview proxy target when the API is not on http://localhost:3000. # API_PROXY_TARGET=http://localhost:3000 # ---- Book metadata lookup ----------------------------------------------- # Open Library is queried first, Google Books when it has no record. # OPEN_LIBRARY_URL=https://openlibrary.org # GOOGLE_BOOKS_URL=https://www.googleapis.com/books/v1 # Optional. Google's anonymous quota is tiny; create a key in Google Cloud # (Books API). With a key, Google Books is also the second source for covers # (better coverage for Spanish editions); without one that step is skipped. # Read by the API process: `.env` here or in apps/api/, or a runtime variable # in Docker / Coolify. Never baked into the image. # GOOGLE_BOOKS_API_KEY= # Per-provider request budget and how long free-text search results stay # cached in memory. ISBN lookups are not cached in memory: they are persisted # in the shared `catalog_books` table (see below). # LOOKUP_TIMEOUT_MS=8000 # LOOKUP_CACHE_TTL_SECONDS=86400 # ---- Shared ISBN catalogue ---------------------------------------------- # Metadata fetched for an ISBN is stored in the database, shared by every user # and survives restarts, so each ISBN is fetched online once. Rows older than # CATALOG_REFRESH_DAYS are served immediately and refreshed in the background; # an ISBN no provider knows is retried after CATALOG_MISS_DAYS. # CATALOG_REFRESH_DAYS=180 # CATALOG_MISS_DAYS=7 # ---- Book covers -------------------------------------------------------- # Covers are WebP files on disk (never rows in the database), one per image # content hash, shared by every book with the same ISBN. Directory defaults # to `covers` next to the SQLite file (/data/covers in Docker). # COVERS_DIR=./data/covers # Image host of Open Library (the metadata host is OPEN_LIBRARY_URL above). # OPEN_LIBRARY_COVERS_URL=https://covers.openlibrary.org # How long "no provider has a cover for this ISBN" is remembered. # COVERS_MISS_DAYS=30 # Unreferenced shared covers are deleted once older than this (daily GC). # COVERS_GC_DAYS=90 # Pause between provider requests; Open Library asks for at most 1 per second. # COVERS_MIN_INTERVAL_MS=1000