mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
Phase 1 of hybrid E2E encryption for DMs: 2-party pairwise NIP-44, reusing the engram/observer "store ciphertext the relay can't read" pattern. A new `encryption_activated_at` latch column on channels (migration 0004) marks a DM as E2E from creation. It is relay-owned and write-once at the `create_dm` INSERT -- `ChannelUpdate` has no such field, so the dynamic update path structurally cannot move or clear it, making the encryption-start boundary tamper-evident by construction. Only 2-party DMs latch; group DMs (3-9) stay plaintext until Phase 2 brings group keys, since pairwise NIP-44 has no single peer to encrypt to. Ingest rule 15c enforces the boundary fail-visible: a latched channel rejects any kind:9 that is not NIP-44 v2 ciphertext (strong validator: base64 + decoded-len >= 99 + 0x02 version byte). Enforcement is latch-PRESENCE only -- no `created_at` comparison -- so a backdated timestamp (drift window or the clamp-exempt proxy:submit path) cannot smuggle plaintext below the latch. Dispatch skips search indexing and workflow triggers for private/DM channels (fail-closed) so ciphertext never reaches Typesense. Desktop gains `nip44_encrypt_to_peer`/`nip44_decrypt_from_peer` Tauri commands (private key stays in Rust) plus TS bindings. Encrypt-on-send and decrypt-on-render in the message pipeline are a follow-up (Phase 1b). Co-authored-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com>