feat(enclave): Coyote Mode anti-spam (cross-room burst -> enclave ban + 24h soft-purge) (LC-339) #355
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/lc-339-coyote-mode"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What
Adds an opt-in per-enclave anti-spam feature, Coyote Mode, alongside the LC-217 rate limit. When an enclave manager enables it, a member who posts in 3+ distinct rooms of the enclave within <=3 seconds is treated as a bot:
enclave_banslist so they cannot rejoin (no enclave-level ban existed before; kick alone lets a public enclave be rejoined instantly).deleted_at), logged tomod_actions.Default off. Manager-gated (
require_manage).Scope decisions (with the product owner)
Implementation
0064_coyote_mode.sql:enclaves.coyote_mode,enclave_banstable,messages(user_id, created_at)index.db/enclave.rs:coyote_modeon the model + every SELECT/mapper;set_coyote_mode,get_coyote_mode_for_room,count_distinct_rooms_posted_recently,ban_from_enclave,is_enclave_banned.db/moderation.rs:soft_delete_user_messages_in_enclave(scoped batched soft-delete, returns purged ids).routes::room::post_messageafter a successful insert, gated on the room's enclave having the mode on and the poster not being a manager/admin; a per-insertCOUNT(DISTINCT room_id)over a 3s window (no AppState field -> no test-construction drift). The ban + purge runs in atokio::spawnoff the send path and broadcastsMessageDeletedper purged row +EnclaveMemberRemoved, scrubs the user's stars, and drops theirenclave:{id}topic subscription (LC-176).is_enclave_banned-> 403 on rejoin (post_join_by_code,post_discover_join) and on posting in the enclave's rooms.POST /enclave/{id}/coyote-modehandler; en/es i18n keys.Test
tests/db_coyote_mode.rs: toggle round-trip; ban kicks + blocks + idempotent; distinct-room counter is window- and enclave-scoped (other enclave / old message / other user excluded); soft-purge scoped to enclave + user + 24h, setsdeleted_at, second run is a no-op.just check(clippy standalone + saas + desktop, fmt),just test,just test-saasall pass. en/es i18n parity holds. Migration 0064 lands incommon::chat_pool()automatically; the curatedmigration_enclaves.rsis unaffected.Closes LC-339.