Files
cayce-ops/deploy/systemd
cayce b87a51afc0
ci / lint-test-leaks (pull_request) Canceled after 0s
backup: encrypted daily backups — VPS generates, Mac pulls (Tanda B, point 1)
deploy/backup/make-backup.sh (installed as /usr/local/bin/cayce-make-backup):
- pg_dump custom-format of the cayce-ops DB (URL from env or .env,
  SQLAlchemy driver qualifier stripped, credentials never logged)
- gitea dump (repos + sqlite + attachments), live; consistency notes in RESTORE.md
- config tar: Caddyfile(+d), gitea app.ini + internal_token, cayce-* units, .env
- EACH artifact encrypted with age BEFORE leaving the tmpdir; recipient
  read from /etc/cayce-backup/age-recipient.pub (public key only). The
  private age key never touches this server.
- plaintext exists ONLY in a mktemp dir removed by an EXIT trap; a partial
  day directory is removed on failure; any step failure => exit != 0 =>
  journald entry
- manifest.sha256 over the encrypted artifacts (pull side verifies
  integrity WITHOUT the age private key)
- destination /var/backups/cayce/YYYY-MM-DD/ (750 root:backup-pull, 640)
- retention: 7 daily dirs, pruned by mtime

systemd: cayce-backup.service (oneshot, hardened: ProtectSystem=strict,
NoNewPrivileges, ProtectHome=read-only) + cayce-backup.timer (daily
03:15 UTC, Persistent). Timer ENABLED on the VPS per operator instruction;
runs fail loudly (exit 1, clear journald message) until the age recipient
is provided — that is the designed not-configured state.
2026-10-28 20:59:28 +00:00
..

systemd deployment (VPS)

This is the primary deployment path (docker-compose is for local dev).

sudo useradd -r -m -d /srv/cayce-ops cayce
rsync -a --exclude .venv ./ cayce@vm:/srv/cayce-ops/
sudo -u cayce uv sync --frozen
sudo -u cayce cayceops db upgrade          # alembic to head
sudo cp deploy/systemd/cayce-ops@.service deploy/systemd/cayce-ops@.timer /etc/systemd/system/

# Every daemon has a `run` subcommand now:
#   scanner run · executor run · maild run (poll + send-approved)
#   publisher run (publish-due) · monitor run (build)
# cadence per daemon (edit OnCalendar in each timer copy):
#   scanner  hourly      executor  */10 min      maild  */5 min
#   publisher */5 min    monitor   */1 min
# NOTE: `publisher plan` (fills the calendar) is a SEPARATE timer:
#   OnCalendar=daily  ->  ExecStart=/srv/cayce-ops/.venv/bin/cayceops publisher plan
for d in scanner executor maild publisher monitor; do
  sudo cp deploy/systemd/cayce-ops@.timer /etc/systemd/system/cayce-ops@$d.timer
done
# adjust OnCalendar per timer, then:
sudo systemctl daemon-reload
sudo systemctl enable --now cayce-ops@{scanner,executor,maild,publisher,monitor}.timer

The unit hardens the daemon (NoNewPrivileges, ProtectSystem=strict) and reads secrets only from /srv/cayce-ops/.env (never in the repo).