Once an owner consumes more than OWNER_BUDGET_SEC seconds, the script
stops starting new repos for that owner and moves on to the next.
The in-flight repo finishes naturally — we never kill mid-push.
ONLY_REPO mode bypasses the budget (single-repo runs are intentional).
Surfaced via a new OWNERS_DEFERRED counter in the summary line + an
OWNER_BUDGET_EXCEEDED audit event per owner.
Originally added after signalapp was starved by openclaw repeatedly
consuming the whole window. With 12 owners and a 16h sync timeout,
2h/owner gives ample headroom while preventing any single owner from
monopolising the run.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Wiki: clones {repo}.wiki.git, pushes to Gitea (renders natively)
- Releases: downloads latest N releases per repo (RELEASE_KEEP=3)
including all binary assets + source archives (tar.gz, zip)
- Release metadata (name, prerelease flag) preserved
- Assets uploaded to matching Gitea releases via API
- Both features toggleable via SYNC_WIKIS and SYNC_RELEASES env vars
- Skips repos with no wiki/releases silently
seedsigner/buildroot LFS push was taking hours, hitting the systemd
timeout, causing restart loop that never reached seedhammer.
- LFS fetch/push now wrapped in timeout (LFS_TIMEOUT=600s default)
- Systemd timeout raised to 8h for safety
- Git refs always push first (the critical data), LFS is best-effort
- Sync timer: daily at 02:00 AEST + on boot
- Healthcheck timer: daily at 08:00 AEST
- Service timeout raised to 4h for large repos
- Restart on failure with 5 min backoff
- ntfy.sh push notifications with priority levels:
- urgent: wipe detection (red alert)
- high: sync errors
- low: successful sync summary
- healthcheck results with stethoscope tag
- All notifications work via free ntfy.sh (no SMTP needed)
- gitea_api GET returning 404 for new orgs/repos caused set -e to
kill the script before error handling could run. Added || true.
- Avatar upload was using wrong endpoint (PATCH /orgs/{org} with
avatar_base64). Corrected to POST /orgs/{org}/avatar with image
field containing raw base64, per Gitea 1.22 API.
(( SYNCED++ )) returns exit code 1 when SYNCED=0, because the
post-increment evaluates to 0 which is falsy in arithmetic context.
With set -euo pipefail this kills the script after the first repo.
Also disable LFS lock verification (one-way mirror doesn't need it)
to suppress noisy stderr messages.