{"id":49928921,"url":"https://github.com/vitofico/quire","last_synced_at":"2026-05-30T23:00:57.653Z","repository":{"id":356548304,"uuid":"1229689312","full_name":"vitofico/quire","owner":"vitofico","description":"Self-hosted EPUB reading stack: native Android reader (Quire) + FastAPI sync server (opds-sync), backed by calibre-web. No telemetry, no cloud.","archived":false,"fork":false,"pushed_at":"2026-05-24T12:00:21.000Z","size":3526,"stargazers_count":5,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-24T13:16:31.411Z","etag":null,"topics":["android","calibre-web","compose","epub","ereader","fastapi","kotlin","opds","readium","self-hosted","sync"],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vitofico.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":"NOTICE","maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"ko_fi":"vito507767"}},"created_at":"2026-05-05T09:41:43.000Z","updated_at":"2026-05-24T12:00:25.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/vitofico/quire","commit_stats":null,"previous_names":["vitofico/quire"],"tags_count":16,"template":false,"template_full_name":null,"purl":"pkg:github/vitofico/quire","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vitofico%2Fquire","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vitofico%2Fquire/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vitofico%2Fquire/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vitofico%2Fquire/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vitofico","download_url":"https://codeload.github.com/vitofico/quire/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vitofico%2Fquire/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33711058,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-30T02:00:06.278Z","response_time":92,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","calibre-web","compose","epub","ereader","fastapi","kotlin","opds","readium","self-hosted","sync"],"created_at":"2026-05-17T02:05:43.729Z","updated_at":"2026-05-30T23:00:57.641Z","avatar_url":"https://github.com/vitofico.png","language":"Python","funding_links":["https://ko-fi.com/vito507767"],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"fastlane/metadata/android/en-US/images/icon.png\" alt=\"Quire\" width=\"128\" height=\"128\"\u003e\n\u003c/p\u003e\n\n\u003ch1 align=\"center\"\u003eQuire\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cstrong\u003equire\u003c/strong\u003e \u003cem\u003e\u0026nbsp;/ˈkwaɪər/ \u0026nbsp;n.\u0026nbsp; — a gathering of folded leaves, bound into a book.\u003c/em\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cem\u003eA privacy-first Android EPUB reader for calibre-web — gathering your library,\u003cbr\u003e\n  your reading state, and opt-in, self-hosted AI into one bound whole.\u003cbr\u003e\n  No telemetry, no cloud, your data.\u003c/em\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/vitofico/quire/actions/workflows/android-ci.yaml\"\u003e\u003cimg src=\"https://github.com/vitofico/quire/actions/workflows/android-ci.yaml/badge.svg\" alt=\"android-ci\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/vitofico/quire/actions/workflows/server-ci.yaml\"\u003e\u003cimg src=\"https://github.com/vitofico/quire/actions/workflows/server-ci.yaml/badge.svg\" alt=\"server-ci\"\u003e\u003c/a\u003e\n  \u003ca href=\"LICENSE\"\u003e\u003cimg src=\"https://img.shields.io/badge/License-Apache_2.0-blue.svg\" alt=\"License: Apache 2.0\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"fastlane/metadata/android/en-US/images/phoneScreenshots/01_library.png\" alt=\"Library\" width=\"260\"\u003e\n  \u0026nbsp;\u0026nbsp;\n  \u003cimg src=\"fastlane/metadata/android/en-US/images/phoneScreenshots/02_catalog.png\" alt=\"Catalog\" width=\"260\"\u003e\n  \u0026nbsp;\u0026nbsp;\n  \u003cimg src=\"fastlane/metadata/android/en-US/images/phoneScreenshots/05_reader.png\" alt=\"Reader\" width=\"260\"\u003e\n\u003c/p\u003e\n\n## What it is\n\nA self-hosted reading stack for people who already run [calibre-web]:\n\n- **Quire** — native Android EPUB reader (Kotlin / Compose / Readium).\n- **Quire Server** — small FastAPI service that stores reading state and\n  your per-user library mirror in Postgres, and orchestrates the optional\n  AI features. The Python package is `quire_server` and the container image\n  is `ghcr.io/vitofico/quire-server`.\n- **Optional, private AI** — book insights, catalog previews, a reader\n  profile, recommendations, and barcode book-scanning — all routed through\n  an OpenAI-compatible endpoint *you* control. Off by default, opt-in per\n  user. [Jump to AI ↓](#ai-on-your-terms)\n\ncalibre-web stays the source of truth for books. Quire Server is the\nsource of truth for reading state. Quire reconciles both on the device.\n\n```\n[calibre-web]  ──OPDS + HTTP Basic──\u003e  [Android: Quire]\n                                              │\n                                              │  HTTPS + same Basic creds\n                                              ▼\n                                       [quire-server]\n                                              │\n                                              ▼\n                                         [Postgres]\n```\n\n## AI, on your terms\n\nMost reading apps that got \"smart\" did it by shipping your library to\nsomeone else's cloud. Quire does the opposite: the intelligence runs\nagainst an endpoint **you** point it at, it is **off by default**, and\nevery user **opts in** individually. The admin configures any\nOpenAI-compatible endpoint — Ollama, llama.cpp, vLLM, OpenAI,\nOpenRouter, … — and if AI isn't enabled and opted into, Quire makes no\nAI calls at all.\n\nWhat that unlocks:\n\n### 📷 Scan a book before you buy it\n\nPoint your phone at a physical book's barcode (or type the ISBN) and\nQuire tells you, on the spot, whether it belongs on your shelf:\n\n- **Reader-affinity score** — a deterministic 0–100 match score with a\n  band (very low → very high) and plain-language reasons, computed from\n  the authors, series, and subjects already in your library. **No LLM,\n  no AI network call** — instant, private, and works as a snap judgment\n  in a bookshop.\n- **Already-owned detection** — tells you if it's in your library and\n  where you left off (unread / in progress / finished / abandoned).\n- **Cover + one-tap deep dive** — pulls the cover from OpenLibrary and,\n  if you want more, loads the full AI insight card on demand.\n\n### 📖 Insights that actually say something\n\nPer-book insight cards generated by the LLM and **grounded with real\ncitations** from Wikipedia and OpenLibrary — not hallucinated blurbs:\n\n- intro · author context · series placement\n- themes \u0026 **craft notes** (how the book is *made*, not just what it's about)\n- **comparative anchors** (\"if you liked…\")\n- a distinctive take · discussion prompts · content advisory · cited sources\n\nInsights show up **on catalog tiles before you download** (tap the info\nicon), so you can size up a book before it ever touches your device.\nDownload it and the cached insight is promoted onto the owned book\ninstantly. Cards are cached per book and shared across every opted-in\nreader on your instance, so you pay for each generation once.\n\n### 🎚️ In your voice, in your language\n\nSet the tone — neutral, enthusiastic, scholarly, or casual — and the\nlanguage: 100+ options, or **`auto`** to follow the book's own language.\nDon't like a card? Regenerate it with feedback.\n\n### 🧭 A reader profile that recommends\n\nFrom the books you've finished, Quire builds an AI-written narrative of\nyour taste (refresh on demand) that powers three flavors of suggestions:\n\n- **In-library** — unfinished books you *already own* that fit your taste.\n- **Discovery** — more from authors you've finished, fetched from OpenLibrary.\n- **AI-suggested** — books you don't own yet, worth hunting down.\n\nPlus **Library Stats** (finished / in-progress / abandoned counts, top\nauthors, top themes) and an **abandoned-book** status that quietly drops\nDNFs out of your library view until you ask for them.\n\n### 🔧 Built for self-hosters\n\nPer-user daily budgets and quotas, an AI **health endpoint**, and deploy\nmodes for **sync-only**, **AI-only**, or **full-stack** operation. Bring\nyour own model; cap your own spend.\n\n\u003e All of the above is opt-in and routed through your endpoint. For setup,\n\u003e env vars, and deploy modes see [`server/README.md`](server/README.md).\n\n## Why this exists\n\nThe starting point was an OPDS catalog (calibre-web) and a simple need:\nread books from it on Android, with reading progress synced across\ndevices.\n\nThat's harder than it sounds in the self-hosted world:\n\n- **KOReader has KOSync**, but KOSync is shaped around KOReader's\n  identity and document model. Using it as a generic sync layer for\n  other clients means working against the grain.\n- **Stock OPDS readers** on Android either don't sync reading position\n  to a server you control, or sync it through a vendor cloud.\n\nSo Quire Server is the piece that was missing: a small, reader-agnostic\nprogress server that speaks OPDS-style document identity and uses your\n**calibre-web account as the only credential** — no second IdP, no\nseparate sync account. Quire is the Android client built against it;\nnothing in the server design is Quire-specific.\n\n## Privacy\n\nApache-2.0, calibre-web-only, **no telemetry, ever.**\n\n- No analytics, no crash reporting, no third-party SDKs.\n- Network calls go to your calibre-web instance and your Quire Server.\n  If your administrator has enabled AI features and you have opted in,\n  Quire Server will additionally call the AI endpoint your administrator\n  configured (such as a self-hosted Ollama, or a third-party provider you\n  have chosen) and the public Wikipedia and OpenLibrary APIs to ground the\n  generated insights. None of these AI-related calls happen unless you\n  opt in from Quire's settings; the Android app itself talks only to your\n  calibre-web instance and your Quire Server.\n- Credentials are stored in Android Keystore (hardware-backed where the\n  device supports it).\n\n## Install\n\nInstall Quire from [F-Droid] or grab the latest APK from [Releases],\nthen point it at your calibre-web URL on first launch.\n\n\u003ca href=\"https://f-droid.org/packages/io.theficos.quire/\"\u003e\n  \u003cimg src=\"https://fdroid.gitlab.io/artwork/badge/get-it-on.png\"\n       alt=\"Get it on F-Droid\"\n       height=\"80\"\u003e\n\u003c/a\u003e\n\nFor the sync server, see [`server/README.md`](server/README.md) — it\nships two reference docker-compose files (`docker-compose.yml` for\n\"bring your own proxy\"; `docker-compose.full.yml` for a Caddy-fronted\nfull stack with calibre-web + quire-server + TLS behind one base URL).\n\n## Roadmap\n\n**Shipped:** OPDS catalog browsing and search, EPUB rendering with\nReadium, local reading progress, progress sync (server + Android\nclient), single-credential auth via calibre-web Basic, per-user library\nmirror with stats (`GET /library/v1/stats`), optional AI book insights\n(schema v4: themes, craft notes, comparative anchors, discussion\nprompts), optional AI reader profile with in-library and OpenLibrary\ndiscovery recommendations, abandoned-book status.\n\n**Planned:** bookmarks sync, calibre-web read-only consumer plugin.\n\n**Not on the roadmap:** PDF support (deferred), separate IdP or\nnon-calibre-web auth.\n\nThis is pre-1.0 software built for the author's personal eink device.\nIt works and it's tested, but the API and DB schema may still change.\nPin a commit if you depend on it.\n\n## Build from source\n\n```sh\n# Build the debug APK using the project's Docker-based Gradle wrapper.\n# Host JDK and Android SDK are not required.\nscripts/dgradle :app:assembleDebug\n# APK at app/build/outputs/apk/debug/app-debug.apk\n```\n\nSync server development:\n\n```sh\ncd server\nuv venv \u0026\u0026 source .venv/bin/activate\nuv pip install -e \".[dev]\"\nuv run pytest                                  # spins up Postgres in Docker\nQUIRE_SERVER_CWA_BASE_URL=https://library.example.com \\\n  uv run uvicorn quire_server.main:app --reload   # http://localhost:8000\n```\n\n## Repo layout\n\n```\napp/                  Android entry point — Compose UI, navigation, DI wiring\nauth/                 Keystore-backed calibre-web Basic credential store\ncore/identity/        Document identity: hash + dc:identifier normalization\ncore/model/           Domain types (Document, Progress, Bookmark)\ndata/local/           Room database, DAOs\ndata/opds/            calibre-web OPDS client\ndata/sync/            quire-server REST client + WorkManager job\ndata/library/         quire-server /library/v1 HTTP client (stats today)\nreader/               Readium navigator integration\nserver/               quire-server (Python / FastAPI)\ndocs/                 Architecture, development, sync API reference\nscripts/dgradle       Gradle wrapper that runs inside the project's Docker image\nDockerfile            Reproducible Android build environment (linux/amd64)\n```\n\n## Documentation\n\n- [`docs/architecture.md`](docs/architecture.md) — components, document\n  identity, sync model, conflict resolution, auth.\n- [`docs/development.md`](docs/development.md) — module layout, build\n  commands, testing, CI, releases.\n- [`docs/sync-api.md`](docs/sync-api.md) — REST surface of Quire Server.\n\n## Contributing\n\nSee [`CONTRIBUTING.md`](CONTRIBUTING.md). TL;DR: gitmoji + conventional\ncommits, `scripts/dgradle test` and `cd server \u0026\u0026 uv run pytest` must\npass, no telemetry / analytics PRs. External contributors also need to\nsign the [Quire CLA](CLA.md) on the PR before it can be merged — see\nCONTRIBUTING.md for the one-line comment.\n\n## Security\n\nIf you find a vulnerability, please follow [`SECURITY.md`](SECURITY.md)\nrather than opening a public issue.\n\n## License\n\nApache-2.0. See [`LICENSE`](LICENSE) and [`NOTICE`](NOTICE).\n\n## Support\n\nIf Quire is useful to you and you'd like to chip in, you can buy me a coffee:\n\n[![Ko-fi](https://img.shields.io/badge/Ko--fi-Support-FF5E5B?logo=ko-fi\u0026logoColor=white)](https://ko-fi.com/vito507767)\n\n[calibre-web]: https://github.com/janeczku/calibre-web\n[Releases]: https://github.com/vitofico/quire/releases\n[F-Droid]: https://f-droid.org/packages/io.theficos.quire/\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvitofico%2Fquire","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvitofico%2Fquire","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvitofico%2Fquire/lists"}