# Design: Leave & Stop-Forward (m9-leave-revocation) ## Architecture Backend-only. No UI changes. Touches 6 files. ## Storage **Tag::CommunityLeave = 0x11** added to `storage/keys.rs` (after PurgeManifest = 0x10). Key format: `encode_key(EntityId::new(user_id), Tag::CommunityLeave, b"")` — no suffix. Value format: `[status: 1 byte (0=Active, 1=Left)][left_at_ns: 8 bytes LE]` — fixed 9 bytes. Stored in the **users** storage partition via `storage.users_engine()`. ## Data Types (entities/community.rs) Appended to existing `CommunityMembershipIndex`: ```rust pub enum MembershipStatus { Active, Left } pub struct CommunityMembership { user_id, status, left_at_ns: Option } pub fn serialize_membership(m: &CommunityMembership) -> [u8; 9] pub fn deserialize_membership(user_id: u64, bytes: &[u8]) -> Option ``` ## TidalDb Field (db/mod.rs) ```rust community_leave_status: dashmap::DashMap, ``` Added after `community_membership`. Initialized to `DashMap::new()` in both `from_config` and `from_parts`. ## API (db/community.rs) Three new public methods appended to the existing `TidalDb` impl: - `leave_community_layer(user_id)` — creates Left membership, persists, inserts into DashMap - `rejoin_community_layer(user_id)` — creates Active membership (preserving left_at_ns), persists, inserts - `community_layer_status(user_id)` — reads from DashMap, returns default_active if absent - `persist_leave_record(membership)` — private helper: writes to users_engine via Tag::CommunityLeave ## Gate (db/signals.rs) Added to BOTH `try_cohort_attribution` and `try_community_forwarding`: ```rust if self.community_leave_status.get(&user_id) .map_or(false, |m| m.status == MembershipStatus::Left) { return; } ``` Placed at top of each function before any other work. ## Startup Restore (db/state_rebuild.rs) `rebuild_community_leave_status(storage, map)` scans users_engine for Tag::CommunityLeave keys, deserializes each, inserts into DashMap. Called from `from_parts` after `rebuild_community_memberships`.