Sync reading position from Moon Reader app to Bookhive atproto records
atproto
bookhive
ereader
moonreader
1<img src="icon.png" alt="spacebee" width="160" align="right">
2
3# spacebee
4
5Sync [Moon+ Reader](https://www.moondownload.com) reading position to your Atmosphere account and share your progress on [bookhive.buzz](bookhive.buzz).
6
7It also serves a simple web page showing your reading status:
8<img src="sshot_spacebee.png">
9
10> 🚨 *This is vibecoded, I don't know much Python but it works 👍️*
11
12## How to use
13Spacebee 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:
14
15<img src="sshot_moonreader.webp">
16
17### Running locally
18
19```sh
20cp .env.example .env
21uv sync --extra dev
22uv run uvicorn spacebee.main:app --reload --port 8080
23```
24
25Point a test device at `http://<wherever>:8080/` as the WebDAV target.
26
27### Docker
28
29Copy compose file, fill in a `.env`, run:
30
31```sh
32curl -O https://raw.githubusercontent.com/oakbrad/spacebee/main/docker-compose.yml
33curl -O https://raw.githubusercontent.com/oakbrad/spacebee/main/.env.example
34mv .env.example .env # then edit with your creds
35docker compose up -d
36```
37
38By default it listens on `127.0.0.1:8080`.
39
40### Configuration
41
42Copy `.env.example` to `.env` and fill in:
43
44| Var | Purpose |
45| --- | --- |
46| `BSKY_HANDLE` | The handle spacebee writes records as |
47| `BSKY_APP_PASSWORD` | An app password for that handle |
48| `DAV_USER` / `DAV_PASSWORD` | Basic-auth credentials Moon+ Reader will send |
49| `PASSTHROUGH_ROOT` | Local-disk scratch dir for non-`.po` paths |
50| `PDS` | *Optional.* If unset, resolved from the handle. |
51
52## How it works
53
54```mermaid
55graph LR
56 MR[Moon+ Reader] -->|"WebDAV: PROPFIND / GET / PUT"| SB[spacebee]
57 SB -->|"buzz.bookhive.book"| PDS[ATProto PDS]
58```
59
60- `PROPFIND /Books/.Moon+/Cache/` synthesizes a directory listing from your
61 `buzz.bookhive.book` records that have a `bookProgress.moonReader.file`
62 field.
63- `GET /Books/.Moon+/Cache/{file}.po` returns the stored position string
64 verbatim (preserves Moon+ Reader's internal chapter/offset encoding).
65- `PUT /Books/.Moon+/Cache/{file}.po` parses the `.po` body, finds the
66 matching bookhive record (or catalog-searches and creates one), and
67 updates `bookProgress.{percent,currentChapter,moonReader}` on your PDS.
68
69Non-position WebDAV paths (`Books/.Moon+/Settings/`, backups, etc.) fall through to a local-disk scratch area rooted at `$PASSTHROUGH_ROOT`.
70
71spacebee also serves a small read-only HTML dashboard at `/` that renders
72your bookhive records (currently-reading, finished, etc.). The dashboard and
73cover-image blob proxy at `/blob/{cid}` are public; all WebDAV endpoints are
74gated by HTTP Basic.
75
76### Record
77
78```json
79{
80 "$type": "buzz.bookhive.book",
81 "bookProgress": {
82 "percent": 11,
83 "updatedAt": "2026-04-17T10:01:24.000Z",
84 "moonReader": {
85 "file": "The Necromancers House - Buehlman Christopher.epub.po",
86 "position": "1703297605115*21@0#4826:11.1%",
87 "syncedAt": "2026-04-17T10:01:24.000Z"
88 },
89 "currentChapter": 22
90 }
91}
92```
93
94
95## Related
96
97- [bookhive.buzz](https://bookhive.buzz) — the AT Protocol book tracker whose records spacebee
98 reads and writes.
99- [Moon+ Reader](https://www.moondownload.com/) - Android eReader app