mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-10-05 19:28:25 +00:00
Kind 451 had no callers, and the reason turned out to be everything around it: the 447/448/449 events in this tree were the exploratory shape the spec now names as not interoperable — tokens in `token` tags with empty content, the sender's leaf implicit, no removals at all, and no owner authentication. The token encryption derived its key from the old `mip05-v1` salt, and the 446 trigger still carried the `encoding` tag the adopted rumor dropped. Wiring the proof into that would have produced records no peer can read. So the gossip is now content-JSON under `marmot-push-v1`, and the version string is the gate: the old value is refused rather than translated, because the two versions are not predecessor and successor. The design the rewrite is really about is owner authentication. A record's authority comes from its own `owner_sig` and current membership, never from who carried it — which is what lets one member relay another's records so a group converges without every owner being online, while stopping the relayer from repointing, re-signing or restamping what it carries. `PushSignedRecord` is the canonical byte string that makes both halves computable; it uses the spec's fixed-width fields rather than this codebase's usual QUIC varints, which look identical locally and are wrong on the wire. The part that costs real machinery is revocation. A removal does not merely delete: it leaves a tombstone at its own `(owner_ts, digest)` stamp, and that stamp has to be durable. Any current member can re-emit a revoked but still validly-signed record in a fresh kind 448 at any later epoch, so its carrying epoch is unbounded and no retained-message window can bound it. The stored stamp is the only thing that recognises such a record as stale, which is why `MarmotPushStateStore` exists and why Amethyst backs it with a file. Everything here is advisory end to end. A bad entry, an unverifiable signature, a stale list — each drops on its own and none of it may reach the validity of the kind:445 that carried it. The decoders return what they could read instead of throwing, and the coordinator catches at its boundary, so a surprise cannot escape into ingest. Not wired: announcing a token of our own. That needs Amethyst's own notification-server public key, which is a deployment decision rather than something the protocol discovers — a server can only wake the app whose push credentials it holds. Until it exists this client participates correctly in other members' routing and announces nothing. MDK's `wn` exposes no push commands, so the harness cannot drive this against the reference. Coverage is the spec's published removal fixture, byte-layout assertions written independently of the encoder, and the ordering and tombstone rules. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016kCuA6tc4JQzHPCDd39GHq