Citadel manages a Calibre library without taking over your ebook reading
Manage your ebook library without frustrations. Calibre compatible.
At a glance
- What is it?
- Citadel is an MIT licensed Rust and Tauri desktop app for managing an ebook library, built on the promise that Calibre must be able to read any library Citadel has edited. It explicitly is not a reader, not a content editor and not a plugin-compatible replacement, and its most recent tags are nightly builds.
- Who is it for?
- Citadel fits someone with a large Calibre library who wants a faster, better looking tool for metadata and organisation while keeping Calibre in the loop, and who is comfortable with a young project. It does not fit someone who wants the reading experience, the content editor or the plugin ecosystem, all of which the project lists as non-goals.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 2 days ago.
- What is it written in?
- Mainly Rust, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 21, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Calibre compatibility is the goal, plugin parity is the non-goal
Citadel is described as a tool to manage your ebook library without frustrations, Calibre compatible, and the compatibility is stated in a way that leaves no ambiguity. The first project goal is that Calibre must be able to read any library that Citadel has edited.
That is a hard constraint on the design, and it rules out a lot. A library manager that stores things in its own format is easy to build and useless to anyone who already has a Calibre collection, so Citadel has to read and write the same library structure and leave it intact.
The non-goals section is just as explicit, and it is the more useful part of the README for a reader. Citadel is not an ebook reader, because there are already excellent ereader apps, and it will open your files in your default apps. It is not a content editor either, and the distinction is drawn precisely: editing ebook content as opposed to metadata like titles is out of scope. And there is no goal of 100% feature parity with Calibre, primarily around plugins, with some advanced Calibre features the project says they will likely never build.
The third goal is performance, phrased as a hard comparative claim: Citadel must feel much faster than Calibre, and never slower. That is a design constraint rather than a benchmark, and it is the one goal a user can check for themselves by feeling it.
The credit section adds the lineage. The project says it would not be possible without the north star created by Kovid Goyal, Calibre, and it thanks Kemie Guaida for a Calibre redesign Figma prototype from which Citadel takes inspiration. So Citadel is positioned as a modern interface over a format Calibre already owns, not as a fork of Calibre itself.
The Rust workspace is four crates, one of which is Calibre compatibility
The Cargo.toml defines a workspace with four members, and the names of three of them describe the whole design.
[workspace]
resolver = "2"
members = [
"crates/citadel-core",
"crates/citadel-opds",
"crates/libcalibre",
"src-tauri",
]citadel-core is the domain logic, citadel-opds is the OPDS feed support, which is the format a content server uses to expose a catalogue over HTTP, and libcalibre is the crate that knows how to read and write a Calibre library. The fourth member, src-tauri, is the desktop application shell, and it is where the Tauri code lives.
The split is the right shape for the compatibility promise, because the Calibre-specific knowledge is isolated in one crate rather than spread through the application. A change to how Calibre stores metadata is a change in one place.
The workspace also pins the toolchain. rust-version is set to 1.97.1 in the workspace package section, and the README explains that the channel lives in rust-toolchain.toml with the workspace rust-version in Cargo.toml, that CI installs the committed toolchain and derives its cache key from the same file, and that crate editions should be kept unchanged unless an edition migration is planned separately.
The Cargo.toml also contains one of the more honest build comments in any repository. Cover thumbnail generation decodes and resizes JPEGs through specific crates, and unoptimized they run 20 to 50 times slower, turning the background thumbnail warm from seconds into minutes in dev builds, so those packages get opt-level 3 in the dev profile. It is a small thing, and it is the kind of detail that tells you what a dev build actually costs.
Bun runs the frontend, Tauri runs the shell
The desktop app is a Tauri application, which means a web frontend in a native shell with a Rust backend, and the build is driven by Bun rather than npm or pnpm.
Developing on it needs Bun and rustup, with Rust pinned by rust-toolchain.toml, and the README says to run rustup toolchain install once in the repository to install the pinned toolchain. Then the packages are installed with bun install, and the app starts with bun run dev.
bun installbun run dev
# or just bun devThe scripts in package.json show the whole pipeline. dev is an alias for dev:app, which is tauri dev with the development configuration at src-tauri/tauri.dev.conf.json, and build is an alias for build:app, which is tauri build. The web side has its own scripts, build:web running tsgo and vite build, and dev:web running vite dev, plus preview:web for vite preview.
There is a documentation wrinkle here. The README section titled App preview without backend says you can run just the frontend with bun dev:app, and warns that you will see errors because the Rust backend will be missing but is assumed to exist. In the package.json, dev:app is the tauri dev command and dev:web is the pure frontend one, so the README and the manifest disagree about which script launches the frontend alone. Anyone trying to work on the UI without a Rust toolchain should check the scripts rather than trust that section.
The frontend stack is React with TanStack Router, styled with Tailwind through PostCSS, and there is a Storybook setup with a dev script on port 6006, a Chromatic config for visual regression, and a .storybook/ directory. The testing setup runs on vitest, with faker for data, plus a Python script under tools/testing/ that checks lists.
Nightly macOS builds are unsigned, so the quarantine flag has to go
The download story is short. Stable builds are available in Releases, and development builds come from the GitHub Actions build workflow. On macOS the instruction is to download the.dmg from Releases, drag Citadel to Applications and open it.
The caveat is for the development builds, and it is stated in a note. Builds from GitHub Actions are not signed, macOS will report them as damaged, and removing the quarantine attribute resolves this.
xattr -d com.apple.quarantine /Applications/Citadel.app/That command is worth understanding rather than copying blindly. The quarantine attribute is what Gatekeeper uses to decide whether a downloaded application has been assessed, and it is also what triggers the damaged message for an unsigned bundle. Stripping it tells macOS to stop asking.
For a reviewer, the important detail is the release numbering. The three most recent tags are all nightly builds of the same version, v0.6.1-nightly.20261009.231, v0.6.1-nightly.20260924.216 and v0.6.1-nightly.20260923.215, and the package.json version is 0.6.1. So the project is shipping a 0.x line where the newest published tags are date-stamped nightlies. There is no long-term support branch to point a production user at, and the app has an auto-updater instead.
That updater is deliberately conservative. Citadel can check for app updates and install them on request, automatic update checks can be disabled in Settings with an Auto updates On/Off toggle, and installs are always explicit, with an Install and restart action. The maintainer workflow is documented separately in docs/updater-and-releases.md.
Linting is split into a table of eight commands
The README devotes a table to the lint and format commands, and the split is the interesting part, because a Tauri project has two toolchains and two style configurations.
bun lintbun format:checkThe full table lists a Format command and a Format (Check) command and a Lint command for all code, then a Format and a Lint for the backend, then a Format and a Lint for the frontend. The backend commands are cargo fmt --all and cargo clippy --workspace, and the frontend commands are biome format and biome lint over src/ and tools/testing/. The aggregate lint script runs the web lint first and then the backend lint, and there is a quality script that chains format:check and lint.
Behind the table is a specific Rust upgrade procedure, which is the part an upstream contributor needs. Rust upgrades are intentional repository changes: update the channel in rust-toolchain.toml and the workspace rust-version in Cargo.toml together, then run bun format:check, bun lint, and cargo test --workspace before merging. Keeping the two files in sync is not a style preference, because CI installs the committed toolchain and derives its cache key from the same file, so a mismatch between them produces a build that is not the build you tested.
The remaining tooling is standard and visible in the tree: biome.jsonc for the frontend linter, .editorconfig and .prettierignore, an eslint-adjacent lint/ directory, an AGENTS.md and CLAUDE.md, a .claude/ directory, a Dockerfile, a .dockerignore, and a Docker-free local build path.
The Dockerfile still packages a 0.3.0 artifact
The container build is a two-stage Dockerfile that starts from a Fedora-based Tauri image, installs Bun with a curl one-liner, copies the package.json and bun.lockb, installs with a frozen lockfile, copies the source and runs bun run build. The artifacts stage then copies two files out of the build.
The version in those paths is the interesting detail. The Dockerfile copies citadel_0.3.0_amd64.AppImage and citadel_0.3.0_amd64.deb from the release bundle directories, while the package.json version is 0.6.1. So the container recipe references a build output naming that corresponds to a version two or three releases behind the current one.
That is a small inconsistency with a practical consequence: the Dockerfile is not a current, verified way to produce a Citadel artefact, because a fresh build with the current version will emit a different file name and the COPY will fail. Anyone trying to build a container image from this repository should expect to fix the paths, and that is worth knowing before you plan a reproducible build around it.
The rest of the container story is otherwise reasonable. The frozen lockfile install is the right call for a build image, and the multi-stage layout with a scratch artifacts stage is a common pattern for shipping only the finished bundles. The base image, ivangabriele/tauri:fedora-37-18, is a prebuilt Tauri environment, so the Dockerfile does not have to install the Rust and webview dependencies itself.
MIT, an active repository, and a product document that spells out the intent
The licence is MIT, and the LICENSE file is at the repository root. You can use, modify and redistribute Citadel, including in commercial builds, provided the copyright notice and the licence text are kept. That is a licensing fact rather than legal advice, and for an app that ships as a downloadable binary it is the permissive end of the scale.
The activity is current. The repository is not archived, the default branch is main, and the last push was on 2026-10-09, the same day as the newest nightly tag. The workflows in .github/ include a quality workflow and a build workflow, and the Discord badge points at a community server.
Two repository files are worth a reader's time beyond the README. PRODUCT.md is where a project like this normally states what it is and who it is for, and DESIGN.md is the design counterpart, and their presence signals that the team is thinking about the product rather than only the code. There is also a docs/ directory holding the updater and release workflow notes, an ai-docs/ directory, a test-library/ directory holding a sample library for tests, and a crates/ directory with the workspace members.
The version number is the honest summary of where this sits. It is 0.6.1 with nightly builds, the project goals are explicit, the non-goals are explicit, and the compatibility promise with Calibre is the one thing an adopter can verify directly by opening a Citadel-edited library in Calibre.
Editorial conclusion
Citadel fits someone with a large Calibre library who wants a faster, better looking tool for metadata and organisation while keeping Calibre in the loop, and who is comfortable with a young project. It does not fit someone who wants the reading experience, the content editor or the plugin ecosystem, all of which the project lists as non-goals. Before adopting it, open a copy of your library with Calibre after editing it in Citadel, because that backwards compatibility promise is the one thing the project defines itself by, and check the updater settings before you rely on nightly builds, since the macOS artifacts are unsigned.
Frequently asked questions
What is Citadel and is it a replacement for Calibre?
Citadel is a Rust and Tauri desktop app for managing an ebook library, and the project states it is not an ebook reader, not a content editor and not aiming for 100% feature parity with Calibre, particularly around plugins. Its first goal is that Calibre must be able to read any library Citadel has edited.
How do I build Citadel from source?
Install Bun and rustup, run rustup toolchain install once in the repository so the pinned toolchain from rust-toolchain.toml is present, then run bun install and bun run dev. A production build uses bun install and bun run build, which runs tauri build.
Why does macOS say the Citadel app is damaged?
Development builds from GitHub Actions are not signed, and macOS reports unsigned apps that way. The README says removing the quarantine attribute resolves it, with the command xattr -d com.apple.quarantine /Applications/Citadel.app/. Stable .dmg builds from Releases are the normal download path on macOS.
How do I upgrade Rust in the Citadel repository?
Update the channel in rust-toolchain.toml and the workspace rust-version in Cargo.toml together, then run bun format:check, bun lint, and cargo test --workspace before merging. CI installs the committed toolchain and derives its cache key from the same file, and crate editions should stay unchanged unless an edition migration is planned separately.
Does Citadel update itself automatically?
It can check for app updates and install them on request. Automatic update checks can be switched off in Settings with the Auto updates toggle, and installs are always explicit through an Install and restart action. Maintainer setup details are in docs/updater-and-releases.md.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/everydaythingssoftware-citadel)Was this useful?