-
Notifications
You must be signed in to change notification settings - Fork 0
Security
biblichor pulls binary content from third-party sources, so the pipeline applies a strict hygiene gate on every download.
Some sources (notably kindlebangla.com for ~30% of its catalog)
deliver the ebook inside a RAR or ZIP wrapper. biblichor unpacks
these, but only after every member passes:
| Check | Blocks |
|---|---|
| Magic byte detection | Files claiming to be archives but aren't (RAR or ZIP only) |
| Compressed size cap (default 200 MB) | Bandwidth / disk hoses |
| Extension whitelist |
.epub/.azw3/.mobi/.pdf/.jpg/.opf and a few siblings; everything else (.exe, .sh, .lnk, .so, …) is fatal |
| Path traversal | Members containing .. or starting with / are fatal |
| Nested archives | Refused outright (no RAR-in-RAR, ZIP-in-ZIP) |
| Zip-bomb protection | Total uncompressed size capped (default 500 MB) |
| Single ebook contract | Must contain exactly one .epub/.azw3/.mobi/.pdf
|
These rules are always on; nothing in config.yaml disables
them. Pipeline marks the book failed with the reason recorded in
events.
After hygiene passes, the unpacked ebook is handed to clamscan if
it's installed. By default ClamAV is treated as optional — its
absence is a loud warning, not a failure.
# Install
sudo apt install clamav clamav-daemon
sudo freshclam # signature DB
sudo systemctl enable --now clamav-freshclam # daily auto-update
# Then in config/config.yaml:
security:
require_clamav: true| Pipeline behavior | require_clamav=false |
require_clamav=true |
|---|---|---|
| ClamAV not installed | ⚠ Warn, allow after hygiene pass | ✗ Fail every archive |
| ClamAV reports clean | ✓ Allow | ✓ Allow |
| ClamAV reports infected | ✗ Fail | ✗ Fail |
| ClamAV errors / times out | ✗ Fail | ✗ Fail |
Bare .epub / .pdf downloads (the common case) are also scanned
by ClamAV when installed; hygiene only applies to archives.
security:
require_clamav: false # flip to true after installing clamav
max_archive_size_mb: 200 # compressed wrapper cap
max_extracted_size_mb: 500 # total uncompressed cap (zip-bomb guard)
max_members: 50 # max entries in an archiveBookOrbit admin credentials (when stored via the SPA or
biblichor bookorbit-setup) live in library.db under AES-256-GCM
encryption.
- 32-byte key from HKDF-SHA256, derived from:
- the existing age recovery key at
data/secrets/restore.keyifbiblichor backup-keyhas been run, OR - a dedicated
data/secrets/secrets.keybiblichor auto-generates on first use (mode 0600)
- the existing age recovery key at
- Each secret is bound to its name as AAD, so swapping ciphertexts between rows fails authentication
- Rotation:
biblichor backup-key --rotatere-encrypts all secrets under a new key atomically (decrypt-old + encrypt-new in one transaction, rolled back on any failure)
See Backups → Encrypted secrets store for the recovery model.
-
Dashboard binding defaults to
0.0.0.0. SetTAILSCALE_IP=100.x.y.zin the systemd Environment line (native) or as an env var (compose) to bind to a single Tailscale IP and keep the dashboard private. - BookOrbit ports default to
3000for the dashboard. If you bound biblichor to Tailscale, also restrict BookOrbit accordingly incompose.yml. - The
/healthzendpoint is intentionally unauthenticated for Docker healthcheck use.
The .gitignore excludes:
-
data/(includinglibrary.dband downloaded files) *.db-
config/config.yaml(operational, generated) -
config/.env(app secrets) -
.env(compose secrets) -
data/secrets/(recovery keys, encrypted secret key file)
If you fork this repo or commit changes, double-check git status
before push.
biblichor is AGPL-3.0. If you run a modified version as a network service, you must make the source available to its users. Linking to your fork in the dashboard's footer satisfies that obligation for personal-host deployments.
Setup
BookOrbit
Operations
Reference