Adds the VPS-side orchestrator for Option A (ADR 2026-06-22):
the Coolify KVM8 VPS (76.13.220.15) drives PRAutoBlogger generation
on prod synchronously over a held-open SSH session, escaping
Hostinger's ~10-min background-PHP-process kill.
Files:
infra/vps/orchestrate-generation.sh
- SSH to Hostinger; reaper sweep; wp prautoblogger generate --sync
- Lock-aware: skips if run in-flight (plugin DB mutex)
- Structured log lines (captured by systemd journal)
- Failure alert: email to cto@peptiderepo.com via system mail
- DRY_RUN=1 mode: echoes commands without executing generation
- All config via env vars (SSH_KEY, SSH_HOST, etc.); no hardcoded secrets
- SSH key referenced by PATH (/root/.ssh/hostinger_prod_ed25519)
infra/vps/install-orchestrator.sh
- Installs orchestrator to /opt/prab-orchestrator/
- Installs systemd .service and .timer to /etc/systemd/system/
- Does NOT enable or start timer (explicit post-QA cutover step)
infra/vps/prab-generation.service
- Type=oneshot, User=root, TimeoutStartSec=7200 (covers 32-min LLM calls)
- Restart=no (timer retries next day on failure)
infra/vps/prab-generation.timer
- OnCalendar=*-*-* 06:00:00 UTC, Persistent=true, RandomizedDelaySec=300
DO NOT install or enable on the VPS this round — QA-gated, hold for CTO review.
Agent-Session: prab-reliability-20260622
20 lines
731 B
SYSTEMD
20 lines
731 B
SYSTEMD
[Unit]
|
|
Description=Daily PRAutoBlogger generation at 06:00 UTC
|
|
Documentation=https://github.com/peptide-repo/peptide-e2e/tree/main/infra/vps/
|
|
Requires=prab-generation.service
|
|
|
|
[Timer]
|
|
# Fire daily at 06:00 UTC — before Hostinger's daily wp-cron window.
|
|
OnCalendar=*-*-* 06:00:00 UTC
|
|
# Persistent=true: if the VPS was down at 06:00 UTC, fire immediately on next
|
|
# boot so at most one day's generation is missed (not deferred indefinitely).
|
|
Persistent=true
|
|
# Up to 5 min random jitter to avoid thundering-herd if multiple VPS instances
|
|
# are ever added. Harmless with a single instance.
|
|
RandomizedDelaySec=300
|
|
# Accuracy within 1 minute (default is 1 minute; explicit for clarity).
|
|
AccuracySec=60
|
|
|
|
[Install]
|
|
WantedBy=timers.target
|