Shelfmark is a self-hosted book and audiobook search aggregator. It searches multiple sources simultaneously and returns results in a unified interface. All outbound traffic exits via the seedbox in the Netherlands through a persistent SOCKS5 tunnel.
| Setting | Value |
|---|---|
| URL | http://192.168.8.100:8084 |
| Host | CT 100 (192.168.8.100) |
| Image | ghcr.io/calibrain/shelfmark:latest |
| Version | v1.2.0 (build 2026-03-07) |
| Port | 8084 |
| Compose file | /opt/shelfmark/docker-compose.yml (on CT 100) |
| Proxy mode | SOCKS5 via 172.25.0.1:1080 (Docker bridge to CT 100 host) |
| Proxy exit | ismene.usbx.me (Netherlands, UltraSeedbox) |
| Remote access | Pangolin private resource only โ not publicly accessible |
| Metadata provider | Hardcover |
Docker volumes:
| Container path | Host path (CT 100) | ZFS dataset | Purpose |
|---|---|---|---|
/books |
/mnt/seedbox/Books |
nvmepool/ingest |
Downloaded books โ shared with Readarr (Bookshelf) |
/config |
/opt/shelfmark/config |
CT 100 rootfs | Settings, users.db, cover cache |
Previously /books pointed to /opt/shelfmark/books (isolated from Readarr). Changed 2026-03-31 to /mnt/seedbox/Books so Shelfmark downloads land directly in the seedbox Books folder where Readarr can see them.
Docker environment:
PROXY_MODE=socks5
SOCKS5_PROXY=socks5://172.25.0.1:1080
NO_PROXY=localhost,127.0.0.1,192.168.8.*,172.25.0.*
TZ=America/New_York
PUID=0
PGID=0
PUID/PGID set to 0 (root): The seedbox Books folder receives files via rsync with UID 1040, which Shelfmark’s default appuser (UID 1000) cannot write to. Running as root prevents “destination not writable” errors during post-processing. Changed 2026-04-02.
Shelfmark routes all search and download traffic through the seedbox to avoid region-based restrictions. The chain is:
Shelfmark (CT 100 Docker)
โ SOCKS5 to 172.25.0.1:1080 (Docker bridge โ CT 100 host)
seedbox-socks.service (autossh, CT 100 systemd)
โ SSH tunnel to delgross@46.232.210.50
ismene.usbx.me (Netherlands exit, UltraSeedbox)
โ
Book search sources (Anna's Archive, Libgen, etc.)
Check proxy tunnel status:
# SSH into CT 100 first
ssh root@192.168.8.221
pct exec 100 -- bash
# Then:
systemctl status seedbox-socks.service
Restart proxy tunnel:
systemctl restart seedbox-socks.service
If Shelfmark searches fail or time out: The SOCKS5 tunnel is almost always the culprit. Check the service status above. The tunnel reconnects automatically via autossh, but occasionally needs a manual restart after seedbox connectivity issues.
Prowlarr runs on CT100 as an indexer aggregator, providing Usenet-based book search as an additional release source alongside the direct download sources (Anna’s Archive, Libgen, etc.). Prowlarr routes all traffic through the same SOCKS5 seedbox tunnel as Shelfmark.
| Setting | Value |
|---|---|
| URL | http://192.168.8.100:9696 |
| Image | lscr.io/linuxserver/prowlarr:latest |
| Compose file | /opt/prowlarr/docker-compose.yml (on CT 100) |
| API Key | 2adb6f9d248840bcadc0ab93222b78fd |
| Auth | Forms (user: bee), disabled for local addresses |
| SOCKS5 proxy | 172.26.0.1:1080 (Prowlarr Docker bridge gateway โ CT 100 host tunnel) |
| Bypass | 192.168.8.*,localhost,127.0.0.1,172.26.0.* |
Indexers configured:
| Indexer | Type | API Key | Book categories |
|---|---|---|---|
| altHUB | Newznab (Usenet) | f0d9327bc1db3011025b40176ec6955a |
7000 (Books), 107020 (Ebook), 107030 (Comics), 107010 (Mags) |
Shelfmark connection: Enabled in Shelfmark Settings โ Prowlarr with auto-expand search on. When Shelfmark searches for a book, it queries both direct_download and prowlarr sources simultaneously. Prowlarr found 115 additional books that direct download sources couldn’t locate.
Note: Prowlarr’s Docker network (prowlarr_default) uses gateway 172.26.0.1, which is different from Shelfmark’s network (shelfmark_default, gateway 172.25.0.1). Both reach the same SOCKS tunnel on 0.0.0.0:1080 on the CT100 host, just via different Docker bridge IPs.
A Python script at ~/Sync/ED/homelab/book_library/kindle_to_shelfmark.py automates bulk importing from the Kindle library into Shelfmark. It reads the Kindle NZB results JSON (1,773 books with titles, authors, and ASINs) and for each book: searches Shelfmark’s Hardcover metadata provider, finds downloadable releases, and queues the best epub for download.
First full run (2026-03-31):
| Metric | Count |
|---|---|
| Total processed | 1,773 |
| Metadata found | 1,732 (97.7%) |
| Metadata not found | 41 |
| Releases found | 1,273 (73.5% of metadata matches) |
| Releases not found | 459 |
| Queued for download | 1,349 (1,234 first run + 115 Prowlarr retry) |
| Queue failures | 39 (mostly duplicates already in queue) |
Usage:
cd ~/Sync/ED/homelab/book_library
# Dry run โ search only, don't download
python3 kindle_to_shelfmark.py --dry-run --skip-existing
# Full run โ queue all downloads
python3 kindle_to_shelfmark.py --skip-existing --delay 5
# Resume after interruption
python3 kindle_to_shelfmark.py --skip-existing --resume
# Process in batches
python3 kindle_to_shelfmark.py --skip-existing --limit 50
Files:
| File | Purpose |
|---|---|
kindle_to_shelfmark.py |
Import script |
kindle_nzb_results.json |
Source data โ 1,773 Kindle books with ASIN, title, author |
shelfmark_state.json |
Resume state โ tracks which ASINs have been processed |
shelfmark_import_*.log |
Timestamped log files for each run |
How the script works:
- For each Kindle book, search Shelfmark metadata API (
/api/metadata/search) using title + author - Find best match by title word overlap (โฅ40% threshold)
- Search for downloadable releases (
/api/releases) using the matched provider/book_id - Score releases โ prefer epub format, reasonable file size (0.5โ100 MB)
- Queue the best release via
/api/releases/download - Save state after every 20 books for resume capability
Shelfmark has no authentication enabled (auth_mode: none). All API endpoints are accessible without credentials.
| Endpoint | Method | Purpose |
|---|---|---|
/api/health |
GET | Health check |
/api/metadata/search?query=...&limit=N |
GET | Search book metadata (Hardcover) |
/api/releases?provider=...&book_id=... |
GET | Search downloadable releases for a book |
/api/releases/download |
POST | Queue a release for download |
/api/localdownload |
GET | List locally downloaded books |
/api/downloads/active |
GET | Active download queue |
/api/config |
GET | Current configuration |
Shelfmark searches multiple sources in priority order until a download succeeds.
Fast sources (tried first):
| Source | Status | Notes |
|---|---|---|
| AA Fast Downloads | โ Active | Requires donator key. Dedicated fast servers, typically 2-4 MB/s. |
| Library Genesis | โ Active | Default mirrors: libgen.gl, .li, .bz, .la, .vg |
Slow sources (fallback):
| Source | Status | Notes |
|---|---|---|
| AA Slow (No Waitlist) | โ Active | Partner servers, no countdown |
| AA Slow (Waitlist) | โ Active | Partner servers with countdown timer |
| Welib | โ Active | Alternative mirror, requires Cloudflare bypass |
| Zlib | โ Active | Z-Library mirror, requires Cloudflare bypass |
Additional sources:
| Source | Status | Notes |
|---|---|---|
| Prowlarr (altHUB) | โ Active | Usenet indexer, finds books not in direct download sources |
Anna’s Archive donator key is configured in Settings โ Download Sources. This unlocks the AA Fast Downloads tier with dedicated servers instead of the free mirrors that crawl at 3-10 KB/s.
DNS-over-HTTPS (DoH) is disabled in Shelfmark’s network config (/opt/shelfmark/config/plugins/network.json, USE_DOH: false). DoH via Quad9 was returning 400 errors for several domains when routed through the SOCKS tunnel, causing all post-processing to fail. System DNS resolution works correctly as a fallback.
Shelfmark is configured as a Pangolin private resource โ it’s reachable remotely without opening any public ports. Connect via the Pangolin VPN client and access it at http://192.168.8.100:8084 as if you’re on the home network.
It does not have a public subdomain โ intentionally kept private since it’s a search aggregation tool.