mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-10-05 19:28:25 +00:00
A review of the full branch diff turned up five, all verified against the code before changing anything. **A departure gate nothing ever cleared.** `leaveGroup` raises a durable `LEAVING` gate and the only `clearGate` calls were inside `resolveDisbandRequest`, so a member who left and was invited back held a gate against a membership that no longer existed. Harmless until this branch, where gates began blocking outbound work AND surviving restarts — which turned it into a group that reads fine and can never be written to again, permanently. An authenticated re-join now clears it, which is the rule `REMOVED` already stated. **An SSRF hole in IPv6 avatar hosts.** `isNonRoutableIpv6` compared TEXT, so `::1` was caught and `0:0:0:0:0:0:0:1` — the same address, expanded — was not, and `::ffff:127.0.0.1` shares no prefix with anything it looked for. A group avatar URL could make every member fetch from their own machine. Addresses are now parsed to their 16 bytes and judged numerically, with IPv4-mapped and -compatible forms delegated to the existing IPv4 rules and an unparseable literal refused rather than waved through. **A message on a branch the group then adopted was never rendered.** An app payload that decrypted only on a candidate branch had its id recorded as processed, so a later redelivery hit the `Duplicate` early-return — even though that result is itself a witness FOR the branch, which convergence may go on to select. It is now retryable like `UndecryptableOuterLayer`. Safe to re-process: witnesses are a set keyed by sender, so a resent payload adds nothing to a branch's standing. **One dropped socket wedged a group until app restart.** An unconfirmed publish pins `PendingPublish`, and the only caller of `retryPendingPublishObligations` was `restoreAll`. A blocked commit now retries that group's obligations on its way through, so the next attempt is the recovery. `MarmotPublishBeforeApplyTest` measured "no replacement commit" by counting sends, which the retry breaks without violating anything: the re-send carries the SAME event id, and a fork means a second DIFFERENT commit for the held epoch. It now counts distinct ids, which is the property it always meant. **`forget()` left two of the gate's three copies behind.** It dropped the map but not the snapshot non-suspending readers see, nor the record on disk, so `restore()` resurrected a gate for a forgotten group. Latent — no production caller yet. 11,001 tests green. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016kCuA6tc4JQzHPCDd39GHq