For leaked-V_me scenarios. The author re-seals a single slot under a
fresh V_me, invalidating the leaked key's access to this specific
post on the wire. Comments signed under the old pub_x at that slot
are NOT auto-deleted; pair with revoke_fof_commenter if comment
cleanup is desired.
Wire format (BlobHeaderDiffOp::FoFKeyBurn):
post_id, slot_index, new_pub_x, new_wrap_slot, burned_at_ms,
author_sig (64B ed25519 over canonical tuple).
fof.rs:
- sign_fof_key_burn / verify_fof_key_burn: canonical signing tuple
includes post_id, slot_index_le, new_pub_x, prefilter+read+sign
bytes from WrapSlot, burned_at_ms_le. Identical shape to access-
grant but with slot_index instead of append.
- apply_fof_key_burn_locally: delegates to storage.replace_fof_slot.
storage.rs:
- replace_fof_slot(post_id, slot_index, new_pub_x, new_wrap_slot):
mutates the stored post's fof_gating_json. Bounds-checks slot_index.
Local-only; PostId unaffected.
connection.rs: receive arm. Verifies author_sig + applies.
node.rs:
- Node::key_burn_post_slot(post_id, slot_index, new_v_x): recovers
CEK via find_unlock_for_post, generates fresh per-V_x keypair,
seals new slot under new_v_x with the existing CEK +
slot_binder_nonce. Signs + applies locally + propagates.
CEK is NOT rotated by this op — body remains encrypted under the
same CEK as before. Locally-cached plaintext on devices that
already-decrypted is unrecoverable by any wire mechanism (out of
scope per spec).
Test brings the total to 147:
- fof_key_burn_replaces_slot: Alice burns her slot from V_me_old to
V_me_new; V_me_old no longer unlocks; V_me_new unlocks and yields
the same CEK; pub_post_set updates to the new pub_x.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>