Skip to main content

Architecture overview

ELVIRA is a distributed system with a small, boring core. That’s the whole design idea: keep the catalog simple and dependable, and push everything expensive — OCR, captioning, embeddings, encryption — out to independent workers on a job queue. A new capability becomes a new service, not a change to the thing holding the entire library.

It has a practical upside too: each worker can be a student team’s project, built and defended on its own, without risking the catalog everyone depends on.

The pieces

Core

ComponentRepoWhat it does
CatalogEvilFlowersCatalogDjango app and the source of truth — catalogs, entries, acquisitions, users, ACLs. Serves OPDS 1.2, OPDS 2.0 and a REST management API. Multi-tenant: one install hosts many libraries.
Portalelvira-portalReact + Vite app for students and staff. One codebase, themed per deployment (fiit, ku, …). University SSO, shelves, search.
ViewerEvilFlowersViewerEmbeddable PDF reader on pdf.js. In-document search, annotations, page-range sharing, citation export. Also published on npm.
Importerevilflowers-importerPulls publications from Kramerius and WebDAV and uses an LLM (OpenAI or Ollama) to fill missing metadata. Emits JSON the catalog ingests.

Workers

Every worker is a Celery consumer on the same broker. The catalog emits an event when a publication arrives; workers pick up what concerns them and write their results back the same way. None of them is on the request path — the catalog stays responsive whether or not the fleet is busy.

WorkerRepoJob
Analyzerevilflowers-analyzer-serviceOrchestrates the rest — routes a new publication to the right services and aggregates results.
Textevilflowers-text-serviceExtracts text (with full Slovak diacritics) from born-digital PDFs.
OCRevilflowers-ocr-workerAdds a searchable text layer to scans, via ocrmypdf.
Imageevilflowers-image-serviceExtracts figures, captions them (PaliGemma) and classifies them (ViT / ResNet50).
Equationevilflowers-equation-serviceReconstructs mathematical formulas as LaTeX, via Nougat.
Videoevilflowers-video-serviceTranscribes audio from video publications (Whisper large-v3), aligned to timestamps.
Searchevilflowers-search-serviceEmbeds extracted text into a vector index and serves semantic search behind the catalog.
LCP encryptevilflowers-lcpencrypt-workerEncrypts titles for lending (AES-256-CBC) via Readium lcpencrypt.

Contract

How a document flows through

  1. Acquisition. A PDF (or EPUB, DiViNa, W3C Audiobook) arrives — REST upload, the importer pipeline, or an admin command.
  2. Extraction. The catalog publishes an event. The analyzer decides whether the file is born-digital or scanned and routes it to the text service or the OCR worker; image, equation and video services take what they need. Everyone writes results back to the catalog.
  3. Indexing. The search service picks up the extracted text, embeds it, and makes it searchable — semantic and classic full-text, side by side.
  4. Protection (optional). If readium_enabled, the LCP worker encrypts the acquisition. The catalog issues a license and advertises the borrow link over OPDS 2.0; reading apps follow it, fetch the license and decrypt locally.
  5. Delivery. The portal and the embedded viewer read everything back through OPDS and the REST API. To the student it’s one thing: a library that opens in a tab.

Design principles

  • Standards first. Everything a reader needs is mounted on a well-known OPDS / Readium prefix, so off-the-shelf apps work without bespoke glue. See Standards.
  • Multi-tenant by default. One deployment hosts many catalogs, each with its own entries, ACLs and theme — that’s how the same install serves a whole university and a second one re-themed.
  • The core does less on purpose. If a feature can live in a worker, it does.

See it move

For a step-by-step, clickable version of the flow above — with links straight to each repository — see the ecosystem map.

In production at
Slovak University of TechnologyCatholic University in Ružomberok