feat: schedule the sync with systemd timers, and let the fetch host publish

mattellite's key is now in the NAS's authorized_keys for agentapi, so
gdl-sync.py publishes straight to the archive. That was the last manual step:
the 2026-08-20 run published locally and rsynced by hand with sshpass, which
no scheduled job could do. Verified end to end -- key auth, then a dry-run
rsync from the fetch host into the archive root.

Three timers, installed and enabled: stories daily, full monthly, sweep
quarterly. A templated gdl-sync@.service takes the mode as its instance name.

Settings that are load-bearing rather than decoration:

  RandomizedDelaySec=45m  a job firing at exactly 09:00 daily is obviously a
                          machine; list-timers now shows 09:36, not 09:00
  Persistent=true         catch up a run the host slept through -- cron just
                          skips, and a skipped stories run is content gone
  TimeoutStartSec=infinity  a sweep runs for hours at this pacing; the 90s
                          default would kill it mid-fetch

Two things caught while installing, both documented: month names are invalid
in OnCalendar's date field (Jan,Apr,Jul,Oct-07 is rejected -- use numerics,
and check with systemd-analyze calendar), and `systemctl --user` over
non-interactive ssh needs XDG_RUNTIME_DIR or it cannot find the bus.

Smoke-tested by starting the stories service: it spent zero Instagram requests
because the 20h floor skipped all six sources, which is exactly what a job
firing twice should do.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UXfdJu7QhSJLr47K7koTDF
This commit is contained in:
2026-08-20 20:03:53 -04:00
co-authored by Claude Opus 5
parent ef99389cf4
commit 652747e0f1
5 changed files with 110 additions and 32 deletions
+13
View File
@@ -0,0 +1,13 @@
[Unit]
Description=Quarterly full Instagram archive sweep (no abort)
# The only run that enumerates every profile to the end, and so the only one
# that notices carousels edited after we archived them (test case 15).
[Timer]
# Month names are not valid in OnCalendar's date field -- numeric only.
OnCalendar=*-01,04,07,10-07 04:00:00
RandomizedDelaySec=45m
Persistent=true
[Install]
WantedBy=timers.target