mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-10-06 03:38:23 +00:00
`marmot.group.message-retention.v1` decoded into `MarmotGroupState.retention` and then nothing read it. In a group with disappearing messages enabled, every member's copy vanished on schedule except Amethyst's, which kept the plaintext indefinitely — not a wire incompatibility, the group still worked, but a privacy divergence from what that group was told it had. Expiry is pinned per message when it enters the log, never recomputed. That is the component's rule and it is the easy one to get wrong: a message keeps the retention of its OWN source epoch, so changing the setting later must not shorten, extend, or restore the expiry of a message that already exists. Recomputing from the current setting would let one member retroactively shorten everyone's history, or resurrect what should already be gone. First write wins for the same reason — the ratchet rewinds on restart and relays replay recent kind:445s, so the same message really is persisted twice, and a second write that re-timed it would let a message postpone its own expiry every time it was replayed. The retention itself is read from the `0x8005` component with a fallback to a legacy group's `0xF2EE` field, because the two profiles express the same setting in different places and reading only one would silently treat half the groups as having no expiry. Expiring deletes rather than hides. This store is the only copy — the ratchet moved past the ciphertext it came from long ago — so a message that is merely filtered out of a read is still on disk, and a disappearing message that is gone from disk but still on screen has not disappeared either. Both stores rewrite their logs, reads prune first so a restart cannot show something that fell due while the app was closed, and the front end is told what went so it can drop those rows from a conversation already open. Traffic is the clock: a group being read is a group whose expired messages should already be gone. There is no timer, so a group nobody opens keeps its messages until someone does — worth knowing, and better than a wakeup that exists only to delete. Expiry stays advisory by design, as the component says: the duration is authenticated but the base is the sender's own `created_at`, so it inherits the trust already placed in an MLS-authenticated sender and is not a guarantee against a hostile one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016kCuA6tc4JQzHPCDd39GHq