Initial commit: The Daily EPUB full implementation

Full implementation of a personalized daily newspaper delivered as an
EPUB.

Articles are pulled from a local self-hosted Miniflux instance, enriched
with comments, summarized and filtered by DeepSeek AI, and then
assembled into two EPUB editions: standard and optimized for the Xteink
X4 e-ink reader. Both are served by the local self-hosted BookOrbit OPDS
server in a separate library. Then the X4 edition is futher converted to
XTC format and served over a separate OPDS server hosted by the Rust
binary. Runs are tracked in a local SQLite database so runs are
idempotent per date.

Full documentation of the plan is in docs/plans and setup and install
instructions are in the README.md file.
This commit is contained in:
2026-08-15 17:46:19 +00:00
commit 9e30c1dcdf
80 changed files with 27578 additions and 0 deletions
+62
View File
@@ -0,0 +1,62 @@
# The Daily EPUB — build and publish one issue (spec §3.15). Driven by
# daily-epub-generate.timer; run by hand with `sudo systemctl start daily-epub-generate`.
#
# Install: see the header of daily-epub.service (same binary, config and env file).
# Logs: journalctl -u daily-epub-generate -f
[Unit]
Description=The Daily EPUB — generate today's issue
Documentation=https://github.com/thallada/the-daily-epub
After=network-online.target miniflux.service
Wants=network-online.target
[Service]
Type=oneshot
User=daily-epub
Group=daily-epub
ExecStart=/usr/local/bin/daily-epub --config /etc/daily-epub/config.toml generate
EnvironmentFile=-/etc/daily-epub/env
Environment=RUST_LOG=info,sqlx=warn,hyper=warn
# Extraction, images and the LLM stage are network-bound; give the run room but
# never let a hung fetch hold the timer's next firing.
TimeoutStartSec=45min
Nice=10
IOSchedulingClass=idle
# --- state + writable paths ---------------------------------------------
StateDirectory=daily-epub
StateDirectoryMode=0750
WorkingDirectory=/var/lib/daily-epub
# The publish dirs from [publish] in config.toml — keep these in sync.
# Every path listed here must exist at start, or the unit fails with 226/NAMESPACE.
ReadWritePaths=/home/thallada/bookorbit/books/daily-epub /var/lib/daily-epub/xtc
# --- hardening (spec §3.15) ---------------------------------------------
ProtectSystem=strict
# read-only (not yes): the BookOrbit publish dir lives under /home, and
# ProtectHome=yes would mask it even with the ReadWritePaths entry above.
ProtectHome=read-only
PrivateTmp=yes
PrivateDevices=yes
NoNewPrivileges=yes
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectKernelLogs=yes
ProtectControlGroups=yes
ProtectClock=yes
ProtectHostname=yes
ProtectProc=invisible
RestrictNamespaces=yes
RestrictRealtime=yes
RestrictSUIDSGID=yes
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
LockPersonality=yes
# No MemoryDenyWriteExecute here: this unit spawns Node (epub-to-xtc-converter),
# whose JIT needs W+X pages (§3.11).
SystemCallArchitectures=native
SystemCallFilter=@system-service
SystemCallErrorNumber=EPERM
UMask=0027
[Install]
WantedBy=multi-user.target