An ebook reader and library manager for Urbit. Upload books directly to your ship or to S3, read them in-browser, track your progress, and share collections with other ships.
- Read in browser — PDF (pdf.js), EPUB (epub.js), MOBI, TXT, Markdown, HTML
- Ship or S3 storage — books can be stored directly in the
%booxagent state, with optional S3 storage via Landscape's%storageagent - Reading positions — synced progress across devices
- Collections — organize books, share with other ships or publish publicly
- Annotations — highlight text and add notes in both EPUB and PDF
- EPUB: CFI-based text anchoring via epub.js
- PDF: text layer overlay for selection and persistent highlights
- Notations visible on public collection pages
- Social — browse friends' shared collections, send books to pals, and retrieve ship-backed books directly over Gall/Mesa
- Public pages — unauthenticated collection pages with inline reader, swipe navigation, font/zoom controls, and deep linking
- Readable toggle — control whether public collections allow reading or are showcase-only
- OPDS catalog — expose your library to any OPDS-compatible reader (with Basic Auth)
- PDF zoom — scale in/out with localStorage persistence
- EPUB typography — adjustable font size, font family, and line height
- Mobile responsive — touch swipe navigation, compact layouts, icon-only nav on narrow screens
- URL deep linking — hash-based position preservation for private and public readers
- Library pagination — configurable 10/20/50 per page
- Bulk upload — add multiple books at once
- PWA — installable as a standalone app on mobile/desktop
desk/ Urbit desk (deployed to ship)
app/boox.hoon Main agent — API, state, OPDS, inline public page HTML
sur/boox.hoon Types: book, position, collection, notation, state
sur/boox-peer.hoon Transient Gall/Mesa file-transfer protocol
mar/boox-action.hoon JSON<->noun action mark
mar/boox-peer.hoon Ship-to-ship transfer mark
mar/boox-update.hoon Update mark
lib/ Standard libraries (server, dbug, etc.)
ui/ Frontend source (Vite)
index.html SPA entry point
js/api.js API client (BooxAPI)
js/app.js UI rendering, state, collections, social
js/reader.js Multi-format reader with annotations and zoom
js/s3.js S3 upload/signing and direct ship upload helpers
css/app.css Styles
vite.config.js Vite config — uses vite-plugin-singlefile
build.zig Reproducible desk and frontend build
Agent state is at state-11: books, positions, book-order, collections, pending, OPDS settings, readable collections, notations, scrobble settings, hashes, Anna's Archive metadata, and locally stored file blobs.
The Zig build installs the frontend dependencies, builds the bundled web app,
assembles the distributable desk in zig-out/, and imports pinned desk
dependencies:
zig buildTo replace an existing mounted desk after building:
zig build -Ddesk=/path/to/pier/booxUse zig build clean to remove zig-out/, or zig build clear to also remove
the cached desk dependencies.
cd ui
npm install
npm run devVite proxies /apps/boox/api to localhost:8080 during development. Edit files
in ui/; production builds write the static application into desk/web/ for
Clay serving.
The build uses vite-plugin-singlefile to inline the app JS and CSS into
index.html; %boox-fileserver serves the resulting files from Clay at
/apps/boox.
npm run build # outputs to desk/web/All endpoints under /apps/boox/api. Authenticated endpoints require an Eyre session cookie.
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| GET | /books |
Eyre | List all books with notation counts |
| GET | /book/<id> |
Eyre | Single book with full notations |
| GET | /s3-config |
Eyre | S3 config from system %storage |
| GET | /collections |
Eyre | List collections |
| GET | /settings |
Eyre | OPDS settings |
| GET | /pals |
Eyre | Combined pals mutuals + contacts |
| GET | /pending |
Eyre | Pending book imports |
| GET | /file/<kind>/<id> |
Eyre | Retrieve a locally stored file (book or cover) |
| GET | /remote/<ship>/book/<id>/file |
Eyre | Retrieve a remote ship-backed book over Gall/Mesa, with Fine fallback |
| GET | /opds |
Basic | OPDS catalog feed |
| GET | /public/collections |
None | Public collection list |
| GET | /public/<token> |
None | Public collection JSON with notations |
| GET | /public/<token>/file/<kind>/<id> |
None | Retrieve a local file exposed through a readable public collection |
| GET | /public/<token>/page |
None | Public collection HTML page with inline reader |
| POST | / |
Eyre | Poke with action JSON |
| POST | /upload/<kind>/<id> |
Eyre | Store raw uploaded bytes in %boox state |
{"action": "add-book", "book-id": "0v...", "title": "...", ...}
{"action": "remove-book", "book-id": "0v..."}
{"action": "set-position", "book-id": "0v...", "value": "...", "progress": 42}
{"action": "add-notation", "book-id": "0v...", "nid": "0v...", "anchor": "...", "selected": "...", "note": "..."}
{"action": "remove-notation", "book-id": "0v...", "nid": "0v..."}
{"action": "create-collection", "name": "...", "description": "..."}
{"action": "share-collection", "name": "..."}
{"action": "publish-collection", "name": "..."}
{"action": "toggle-readable", "name": "..."}
{"action": "toggle-opds"}
{"action": "send-book", "book-id": "0v...", "to": "~ship"}When enabled, an OPDS catalog is served at /apps/boox/api/opds with Basic Auth (username: ship name, password: configurable or +code).
- Direct ship upload does not require TLS or S3. S3 upload remains available when configured, but still requires HTTPS or localhost for browser-side Web Crypto signing.
- Remote ship-backed books are sent as a single authenticated Gall noun when both peers have a directed Mesa route. Older installs and non-Mesa peers use the existing Fine scry path.
- State upgrades (new
state-N) require a nuke+revive cycle on the agent. - Frontend is served from Clay by
%boox-fileserver; the docket points atsite+/apps/boox.