librarian

Librarian for Mac

Handbrake for documents. Drop files into one window, pick a destination folder and format, and cleaned files appear there automatically — converted, cleaned, and classified by the engine bundled inside the app. Download, drag to Applications, double-click, done.

How it’s organized

Install (for users)

  1. Download Librarian-AppleSilicon.dmg (Apple Silicon — M-series Macs) from the assets of the latest release.

    (Apple menu → About This Mac shows which chip you have. DMGs are attached to releases from v1.1.0 onward.)

    Intel Macs: the native app is Apple Silicon only as of v1.8.3. A bundled dependency (cryptography, via markitdown[pdf]) dropped Intel macOS wheels at the same release that fixed its security advisories, so a self-contained Intel app can no longer be built without shipping a vulnerable version. Intel users should install via pip install "nampara-librarian[all]" or run the Docker image — both stay patched.

  2. Open the DMG and drag Librarian into Applications.
  3. Launch Librarian and drop a file anywhere in the window.

macOS 14 (Sonoma) or newer is required.

First launch: release DMGs (v1.8.4 and later) are signed with a Developer ID and notarized by Apple, so they pass normal macOS Gatekeeper checks.

Only if you built the app yourself without a signing certificate (a local make or a fork without the signing secrets below): such a build is ad-hoc signed, and macOS Gatekeeper rejects an ad-hoc-signed app that carries the browser’s quarantine flag with “Librarian” is damaged and can’t be opened — the app is not actually damaged. Because it reports “damaged” rather than “unidentified developer”, neither right-click → Open nor Privacy & Security → Open Anyway is offered. Clear the quarantine flag once, then launch normally:

xattr -cr /Applications/Librarian.app

How it works

The app bundles a relocatable Python runtime plus the nampara-librarian backend (installed with the [all] extras) in Librarian.app/Contents/Resources/backend. On launch it starts python -m librarian api on a local port (127.0.0.1 only), waits for /health, and talks to it over the same public HTTP API any other client would use. Quitting the app stops the backend.

Because the backend ships the [all] extras, the high-fidelity liteparse PDF/image engine — Markdown tables, headings, figure placeholders, and selective OCR — is the default inside the app, with its own bundled PDFium + Tesseract. No engine selection or extra install is needed.

Scanned and image-based PDFs are OCR’d with a self-contained copy of Tesseract and Poppler (plus English language data) bundled under Librarian.app/Contents/Resources/ocr, with dependent libraries relocated into the bundle. The app puts these on the engine’s PATH and sets TESSDATA_PREFIX at launch — and points the liteparse engine at the same traineddata via LIBRARIAN_LITEPARSE_TESSDATA_PATH — so OCR (built-in and liteparse) works fully offline with no Homebrew install, no PATH setup, and no first-use language-data download. (macOS GUI apps inherit only a bare system PATH, which is why a system-installed OCR tool would otherwise be invisible to the engine.) Each DMG is built on a native-architecture runner so the bundled OCR binaries match the target Mac.

Each launch generates a random API key and passes it to the backend through LIBRARIAN_API_KEY. API requests to the loopback server require that key.

Your data lives in ~/Library/Application Support/Librarian:

Path Contents
librarian.sqlite document database and search index
uploads/ original files you dropped in
converted/ converted Markdown/text outputs
backend.log backend log (first stop for troubleshooting)
page-cache/ resumable PDF text and image extraction, keyed by source and settings
.env (optional) backend configuration, see below

Connecting an LLM provider

Open Settings (⌘,) — or click Set up AI cleaning… in the empty main window — pick a provider (Anthropic, OpenAI, DeepSeek, Ollama, LM Studio, or Custom), paste your API key (local servers take an address instead, pre-filled with the standard port), and press Connect. The app queries the provider’s live model list and shows a model dropdown with what your account or server actually offers — picking a model applies it immediately and pins it to every cleaning call (“Cleaning with claude-sonnet-4-6”). A bad key fails with a clear message within a few seconds. Until a provider is connected, files are still converted and organized, just without AI cleaning.

API keys are stored in the macOS Keychain and passed to the engine through its process environment — not written to the app’s .env file. Keychain items persist across app reinstalls and live in macOS’s protected credential store. Non-secret settings (provider, model, base URL) live in ~/Library/Application Support/Librarian/.env, which you can also edit by hand; a key found in a legacy .env is migrated into the Keychain automatically.

Selecting a model or changing engine options restarts the embedded backend as needed. If you edit .env by hand, quit and reopen the app to apply the change. Any LIBRARIAN_* setting from docs/DEPLOYMENT.md works here.

OCR for scanned documents

The release DMG already includes Tesseract, Poppler, and English OCR language data. No Homebrew install is needed for scanned PDFs or images. If you run an unbundled developer build against a separately installed backend, install the system OCR tools for that backend:

brew install tesseract poppler

Resetting the app for a fresh test

Quit Librarian and move /Applications/Librarian.app, ~/Library/Application Support/Librarian, ~/Library/Caches/ai.nampara.librarian, and ~/Library/Preferences/ai.nampara.librarian.plist to Trash before installing a newly downloaded release. This resets the app’s database, uploads, extraction cache, backend logs, and settings. Exports already saved to ~/Documents/Librarian are separate files and are not removed by this reset. The macOS Keychain can still hold previously saved provider API keys under the service ai.nampara.librarian; remove those entries in Keychain Access only if you also want a fresh credential setup.

Using a remote backend instead

Settings (⌘,) → turn off “Run the built-in backend automatically” and enter the URL and API key of any Librarian API server. Developer builds without a bundled backend fall back to this mode automatically (default http://127.0.0.1:8080).

Develop (for contributors)

Requirements: Xcode 15+ or a recent Swift toolchain.

# From the repository root, in a separate terminal:
pip install -e ".[all]" && librarian api

# App:
cd apps/macos
make run          # debug build via swift run

Or open Package.swift in Xcode and press Run.

Build the distributable yourself

# 1. Build the backend wheel (repo root)
python -m pip install build && python -m build --wheel

# 2. Build the self-contained app + DMG (Apple Silicon example)
cd apps/macos
make bundled-app        # app + downloads relocatable Python + installs wheel
make dmg                # dist/Librarian.dmg

make bundled-app accepts WHEEL=... (wheel path or PyPI requirement), BUNDLE_ARCH=arm64|x86_64, and IDENTITY="Developer ID Application: ..." for real signing. The bundled Python version is pinned in scripts/bundle_backend.sh. (BUNDLE_ARCH=x86_64 still works for local Intel builds, but note that some pinned dependencies no longer publish Intel macOS wheels, so such a build may not resolve against the release constraints.)

Release pipeline (CI)

.github/workflows/macapp.yml builds the Apple Silicon DMG on a macOS runner for every v* tag (and on demand via workflow dispatch) and attaches it to the GitHub release as Librarian-AppleSilicon.dmg — which is what the stable releases/latest/download/... link points at. (The Intel DMG was dropped in v1.8.3; see the Install section above.)

Builds are ad-hoc signed by default. To ship notarized builds that open with zero Gatekeeper friction, add these repository secrets (requires an Apple Developer account):

Secret Value
MACOS_SIGNING_CERT_P12_BASE64 base64 of your Developer ID Application certificate (.p12)
MACOS_SIGNING_CERT_PASSWORD password for the .p12
MACOS_SIGNING_IDENTITY e.g. Developer ID Application: Your Name (TEAMID)
MACOS_NOTARIZATION_APPLE_ID Apple ID email
MACOS_NOTARIZATION_TEAM_ID 10-character team ID
MACOS_NOTARIZATION_APP_PASSWORD app-specific password for notarytool

The workflow detects the secrets and switches to Developer ID signing plus notarytool submission and stapling automatically — no workflow changes needed.

A download landing page lives in site/ and deploys via GitHub Pages (.github/workflows/pages.yml; enable Pages with source “GitHub Actions” in the repository settings).