diff --git a/nix/templates/harness-base.nix b/nix/templates/harness-base.nix index 0d93c698..37596756 100644 --- a/nix/templates/harness-base.nix +++ b/nix/templates/harness-base.nix @@ -1246,6 +1246,20 @@ in echo "matrix-avatar-sync: no matrix-token at $TOKEN_FILE; skipping" exit 0 fi + # Hash-based idempotency: skip the upload if the icon hasn't + # changed since the last successful sync. Every upload mints a + # new mxc:// URI which triggers a profile state event in every + # joined room — uploading the same bytes again produces timeline + # spam without changing the visible avatar. The hash file lives + # in $HYPERHIVE_STATE_DIR (survives restart, wiped on purge so + # purge + re-provision gets a fresh upload). Delete to force + # re-upload. + HASH_FILE="$HYPERHIVE_STATE_DIR/matrix-avatar-icon-hash" + CURRENT_HASH=$(sha256sum "$ICON" | cut -d' ' -f1) + if [ -f "$HASH_FILE" ] && [ "$(cat "$HASH_FILE" 2>/dev/null)" = "$CURRENT_HASH" ]; then + echo "matrix-avatar-sync: icon unchanged (hash matches); skipping" + exit 0 + fi TOKEN=$(cat "$TOKEN_FILE") # Local tuwunel reachable on shared host netns at the # default matrix-spec port. Override via @@ -1293,6 +1307,9 @@ in -o /dev/null -w "%{http_code}" 2>/dev/null || true) if [ "$CODE" = "200" ]; then echo "matrix-avatar-sync: avatar set on $USER_ID" + # Persist hash so subsequent runs skip the upload when the + # icon hasn't changed. + echo "$CURRENT_HASH" > "$HASH_FILE" else echo "matrix-avatar-sync: avatar PUT returned HTTP $CODE — skipping (non-fatal)" fi