2.6 KiB
Security Audit: Leave & Stop-Forward (m9-leave-revocation)
Surface
This feature adds three public API methods (leave_community_layer, rejoin_community_layer, community_layer_status) and one storage tag (Tag::CommunityLeave = 0x11). It modifies two internal signal fan-out paths (try_cohort_attribution, try_community_forwarding) to check a DashMap gate.
Authorization
The TidalDb API is a single-process embedded database. Authorization (who may call leave_community_layer on behalf of a user) is enforced at the application layer above TidalDb, not inside TidalDb itself. This is consistent with the rest of the API (signal_with_context, write_user, etc.). No regression.
Input Validation
user_id: u64— no validation required; allu64values are valid user identifiers.- No string or byte inputs are accepted by the public API methods.
persist_leave_recordwrites a fixed 9-byte value produced byserialize_membership— no user-controlled bytes reach the storage key beyond the 8-byte entity_id encoding (same as all other entity keys).
Storage Key Isolation
Key: encode_key(EntityId::new(user_id), Tag::CommunityLeave, b"") — no suffix. The key is entirely determined by user_id and the tag byte 0x11. No injection surface.
Tag 0x11 is unique (verified by all_tags_have_unique_bytes proptest). No collision with any existing tag.
Denial of Service
The DashMap::get gate on the signal write path is O(1) and lock-free per shard. It does not allocate. A user cannot increase gate check cost by any input.
leave_community_layer and rejoin_community_layer each do exactly one storage put and one DashMap insert. These are not on the hot path and cannot be triggered by untrusted external input without application-layer authorization.
Information Disclosure
community_layer_status reads from the in-memory DashMap only — no storage I/O. The returned CommunityMembership struct contains user_id, status, and left_at_ns. None of this is sensitive beyond what the caller already knows (they supplied the user_id).
Startup Rebuild
rebuild_community_leave_status performs a full scan_prefix(&[]) of the users_engine on startup. It reads all keys and filters on Tag::CommunityLeave. Malformed values are skipped with a debug log — no panic, no error propagation. This is consistent with the pattern established by rebuild_community_memberships and all other rebuild functions.
Verdict
No security concerns. The attack surface is identical to existing TidalDb API methods. No new injection surfaces, no new authorization bypasses, no new DoS vectors.
Approved.