Sync reading position from Moon Reader app to Bookhive atproto records
atproto bookhive ereader moonreader
3
fork

Configure Feed

Select the types of activity you want to include in your feed.

Python 86.0%
HTML 13.2%
Dockerfile 0.7%
19 1 0

Clone this repository

https://tangled.org/bradwenner.photo/spacebee https://tangled.org/did:plc:fyxup3moaom35xzxnomsryyx/spacebee
git@tangled.org:bradwenner.photo/spacebee git@tangled.org:did:plc:fyxup3moaom35xzxnomsryyx/spacebee

For self-hosted knots, clone URLs may differ based on your setup.

Download tar.gz
README.md
spacebee

spacebee#

Sync Moon+ Reader reading position to your Atmosphere account and share your progress on bookhive.buzz.

It also serves a simple web page showing your reading status:

🚨 This is vibecoded, I don't know much Python but it works 👍️

How to use#

Spacebee pretends to be WebDAV but reads and writes from your Atmosphere account. In the Moon Reader app, go to Options->Sync with WebDAV and point it to spacebee:

Running locally#

cp .env.example .env
uv sync --extra dev
uv run uvicorn spacebee.main:app --reload --port 8080

Point a test device at http://<wherever>:8080/ as the WebDAV target.

Docker#

Copy compose file, fill in a .env, run:

curl -O https://raw.githubusercontent.com/oakbrad/spacebee/main/docker-compose.yml
curl -O https://raw.githubusercontent.com/oakbrad/spacebee/main/.env.example
mv .env.example .env      # then edit with your creds
docker compose up -d

By default it listens on 127.0.0.1:8080.

Configuration#

Copy .env.example to .env and fill in:

Var Purpose
BSKY_HANDLE The handle spacebee writes records as
BSKY_APP_PASSWORD An app password for that handle
DAV_USER / DAV_PASSWORD Basic-auth credentials Moon+ Reader will send
PASSTHROUGH_ROOT Local-disk scratch dir for non-.po paths
PDS Optional. If unset, resolved from the handle.

How it works#

graph LR
    MR[Moon+ Reader] -->|"WebDAV: PROPFIND / GET / PUT"| SB[spacebee]
    SB -->|"buzz.bookhive.book"| PDS[ATProto PDS]
  • PROPFIND /Books/.Moon+/Cache/ synthesizes a directory listing from your buzz.bookhive.book records that have a bookProgress.moonReader.file field.
  • GET /Books/.Moon+/Cache/{file}.po returns the stored position string verbatim (preserves Moon+ Reader's internal chapter/offset encoding).
  • PUT /Books/.Moon+/Cache/{file}.po parses the .po body, finds the matching bookhive record (or catalog-searches and creates one), and updates bookProgress.{percent,currentChapter,moonReader} on your PDS.

Non-position WebDAV paths (Books/.Moon+/Settings/, backups, etc.) fall through to a local-disk scratch area rooted at $PASSTHROUGH_ROOT.

spacebee also serves a small read-only HTML dashboard at / that renders your bookhive records (currently-reading, finished, etc.). The dashboard and cover-image blob proxy at /blob/{cid} are public; all WebDAV endpoints are gated by HTTP Basic.

Record#

{
  "$type": "buzz.bookhive.book",
  "bookProgress": {
    "percent": 11,
    "updatedAt": "2026-04-17T10:01:24.000Z",
    "moonReader": {
      "file": "The Necromancers House - Buehlman Christopher.epub.po",
      "position": "1703297605115*21@0#4826:11.1%",
      "syncedAt": "2026-04-17T10:01:24.000Z"
    },
    "currentChapter": 22
  }
}