[READ-ONLY] Mirror of https://github.com/olamaelcu/bibliograph. An AppView for interfacing with bilbographic information on ATProto
TypeScript 97%
JavaScript 3%
Procfile <1%

README.md

Bibliograph #

ATProto AppView for community.lexicon.book — a shared book lexicon that indexes book records, reviews, reading statuses, and author/publisher claims from the Atmosphere network.

A public reference instance is available at biblio.livtet.olamaelcu.net.

Quick start #

npm install
npm run dev

Server starts on http://localhost:3000. Visit / for the splash page, /health for a JSON status check.

What it does #

Bibliograph consumes book-related ATProto records via Tap, indexes them into SQLite, and serves XRPC query/procedure endpoints following the community.lexicon.book lexicon.

Lexicons #

NSID Type Purpose
community.lexicon.book.book record Core book definition (title, author, ISBN, metadata)
community.lexicon.book.claim record Author/publisher claim with deduplication key
community.lexicon.book.review record User review with optional rating
community.lexicon.book.status record Reading status (reading, read, to-read, abandoned)

XRPC Endpoints #

Queries (GET /xrpc/nsid):

Endpoint Description
getBook Fetch a single book by AT-URI
getBooks Batch fetch books by URIs
getReviews Paginated reviews for a book
getReview Fetch a single review by AT-URI or user+book
getUserStatus Reading statuses for a user
searchBooks Full-text search on title, author, ISBN
getClaims Claims attached to a book
getFeed Home feed: recent status updates, newest books, trending, following, cross-user

Procedures (POST /xrpc/nsid):

Endpoint Description
createBook Create a book definition (requires ISBN for dedup)
createReview Post a review
createStatus Record reading status
createClaim Claim a book as author/curator

Other endpoints #

Path Method Description
/tap/event POST Tap webhook — receives firehose events
/api/lookup/book GET Open Library lookup by ISBN or title

Architecture #

┌──────────┐     ┌──────────────┐     ┌──────────────┐
│   Tap    │────▶│  /tap/event  │────▶│   Indexer    │
│ (firehose)│    │   (webhook)  │     │   (SQLite)   │
└──────────┘     └──────────────┘     └──────┬───────┘
                                             │
                                      ┌──────▼───────┐
                                      │  Hono server  │
                                      │  (XRPC API)   │
                                      └──────────────┘
                                             │
                                    ┌────────▼────────┐
                                    │  Open Library    │
                                    │  Google Books    │
                                    │  (providers)     │
                                    └─────────────────┘
  • Hono — HTTP server with XRPC-style routing
  • Drizzle ORM + better-sqlite3 — local index database with FTS5 search
  • Tap — firehose consumer for ATProto record events
  • Providers — Open Library and Google Books data enrichment

Authorization model #

Books are created in a pending status. An author claim record (community.lexicon.book.claim) ties the book to the creator's DID.

  • Claim owner — the DID that claimed the book can edit it
  • Librarian — users with verified claims on multiple books gain edit privileges
  • ISBN required — book creation requires an ISBN/EAN for deduplication

Book data lookup #

The /api/lookup/book endpoint queries Open Library:

curl "http://localhost:3000/api/lookup/book?isbn=9780140328721"
curl "http://localhost:3000/api/lookup/book?title=Dune&author=Frank+Herbert"

Google Books support is available via GoogleBooksProvider but requires an API key passed to the constructor.

Connecting Tap #

Run Tap with the book lexicon signal collection:

tap run \
  --signal-collection=community.lexicon.book.book \
  --collection-filters=community.lexicon.book.* \
  --webhook-url=http://localhost:3000/tap/event \
  --admin-password=secret

Deploying to Dokku #

The Procfile declares the AppView processes:

release: tsx src/release.ts   # runs migrations before deploy
web:     tsx src/index.ts     # AppView XRPC API + labeler subscription stream

The com.atproto.label.subscribeLabels endpoint is served in-process by the same web process (Dokku proxies the web process by default, so no custom proxy config is needed). Clients connect to the same host as the AppView:

wss://biblio.livtet.olamaelcu.net/xrpc/com.atproto.label.subscribeLabels

Labels written by the AppView appear in the stream via the shared label_events log (same process, same SQLite database). The labeler DID document must declare an atproto_labeler service entry pointing at this endpoint for clients to discover it.

Project structure #

src/
  app.ts           Hono app wiring and route mounting
  index.ts         Entrypoint — runs migrations, starts server
  types.ts         TypeScript interfaces for all lexicons
  auth.ts          Authorization guard (claim ownership, librarian)
  indexer.ts       Tap event handler — indexes record creates/updates/deletes
  api/
    get-book.ts    Query handlers (GET /xrpc/*)
    create-book.ts Procedure handlers (POST /xrpc/*)
  db/
    schema.ts      Drizzle ORM table definitions
    connection.ts  better-sqlite3 database singleton
    init.ts        Table creation, FTS5 setup, search helper
  providers/
    interface.ts   BookProvider interface
    openlibrary.ts Open Library API provider
    googlebooks.ts Google Books API provider
lexicons/
  community/lexicon/book/
    *.json         14 ATProto lexicon schema definitions

License #

MPL-2.0