mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-08-11 00:37:41 +00:00
An audit (adversarial-verified) found the deletion side-channel over-deletes and over-propagates. Root cause: deletionSideChannelFilter fell open to authors=null for any non-author-scoped content sync, so `amy sync --kind 1` reconciled the RELAY'S ENTIRE kind-5/62 history and applied it to the personal FsEventStore — every kind-5 deleting its targets + installing an id-tombstone for every target id, every ALL_RELAYS kind-62 wiping all of a pubkey's events (all kinds), and pushing our whole local deletion history up. Data loss plus a full-history reconcile on every scoped sync. Fixes: - Bound the side-channel to the authors we actually hold content for (filter authors ∪ local matched-set authors), never the relay's population. Skip when that scope is empty; Phase 3's reject-reaction covers the author-less case. - Kind-5 (precise, owner-scoped) propagates by default; kind-62 vanish is opt-in via --sync-vanish (its blast radius always exceeds a content sync's scope). - excludesDeletionKinds() now checks each deletion kind independently (`--kind 1,5` no longer silently drops kind-62); the side-channel reconciles only the missing kinds. - amy Phase 1 is best-effort: a deletion-reconcile failure records deletions_error and falls through to content, never aborting the primary sync (matches geode). - Mirror up-catch-up converges on whether a PUBLISHABLE event was pushed, not raw haveCount — a vanish targeting another relay no longer burns all 8 rounds every startup. Mirror keeps its (correct) global scope for relay-to-relay replication. Helper API: negentropyPropagateDeletions gains scopeAuthors + deletionKinds; deletionSideChannelFilter takes authors + deletionKinds and returns only the missing kinds. Tests updated for the new semantics. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JgL1WTV4Hkp2uuXcUHCHGt