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:
@@ -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
|
||||
@@ -0,0 +1,22 @@
|
||||
# Fires daily-epub-generate.service every morning at 05:30 Eastern (spec §3.15).
|
||||
#
|
||||
# Install:
|
||||
# sudo install -m0644 systemd/daily-epub-generate.timer /etc/systemd/system/
|
||||
# sudo systemctl daemon-reload && sudo systemctl enable --now daily-epub-generate.timer
|
||||
# Check: systemctl list-timers daily-epub-generate.timer
|
||||
|
||||
[Unit]
|
||||
Description=Build The Daily EPUB every morning
|
||||
Documentation=https://github.com/thallada/the-daily-epub
|
||||
|
||||
[Timer]
|
||||
Unit=daily-epub-generate.service
|
||||
OnCalendar=*-*-* 05:30:00 America/New_York
|
||||
# Catch up after downtime — a missed morning still gets its issue.
|
||||
Persistent=true
|
||||
# Spread the load on the Miniflux/DeepSeek side.
|
||||
RandomizedDelaySec=300
|
||||
AccuracySec=1min
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
@@ -0,0 +1,70 @@
|
||||
# The Daily EPUB — rating endpoints, XTC OPDS feed and static files (spec §3.12, §3.15).
|
||||
#
|
||||
# Install:
|
||||
# cargo build --release && sudo install -m0755 target/release/daily-epub /usr/local/bin/
|
||||
# sudo install -d -m0750 -o daily-epub -g daily-epub /etc/daily-epub
|
||||
# sudo install -m0640 -o daily-epub -g daily-epub config.example.toml /etc/daily-epub/config.toml
|
||||
# printf 'DAILY_EPUB_SERVER__HMAC_SECRET=%s\n' "$(openssl rand -hex 32)" \
|
||||
# | sudo tee /etc/daily-epub/env >/dev/null # plus DAILY_EPUB_MINIFLUX__API_KEY etc.
|
||||
# sudo chmod 0600 /etc/daily-epub/env
|
||||
# sudo useradd --system --home /var/lib/daily-epub --shell /usr/sbin/nologin daily-epub
|
||||
# sudo install -m0644 systemd/daily-epub*.{service,timer} /etc/systemd/system/
|
||||
# sudo systemctl daemon-reload && sudo systemctl enable --now daily-epub.service
|
||||
#
|
||||
# Then reverse-proxy daily.hallada.net → 127.0.0.1:3499 (spec §3.15).
|
||||
|
||||
[Unit]
|
||||
Description=The Daily EPUB server (ratings, XTC OPDS)
|
||||
Documentation=https://github.com/thallada/the-daily-epub
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=exec
|
||||
User=daily-epub
|
||||
Group=daily-epub
|
||||
ExecStart=/usr/local/bin/daily-epub --config /etc/daily-epub/config.toml serve
|
||||
EnvironmentFile=-/etc/daily-epub/env
|
||||
Environment=RUST_LOG=info,sqlx=warn,hyper=warn
|
||||
Restart=on-failure
|
||||
RestartSec=5s
|
||||
# SIGTERM triggers the graceful shutdown in server::serve.
|
||||
KillSignal=SIGTERM
|
||||
TimeoutStopSec=20s
|
||||
|
||||
# --- 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
|
||||
MemoryDenyWriteExecute=yes
|
||||
SystemCallArchitectures=native
|
||||
SystemCallFilter=@system-service
|
||||
SystemCallErrorNumber=EPERM
|
||||
UMask=0027
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
Reference in New Issue
Block a user