Skip to content

Reactions

Keep aggregate reaction reads in ordinary readable-channel scope. Accept only one exact message ID and return strict canonical aggregates containing normal count, burst count, total count, structured emoji identity, and the verified connector's own normal and burst flags. Validate the complete known reaction shape, count relationships, emoji identity, duplicate absence, bounds, message identity, and supported channel type. Project out message content, authors, user identities, profiles, burst colors, unknown fields, and raw payloads before returning a result, and never cache or persist an aggregate snapshot.

Keep reaction-user pages behind their independent disabled-by-default gate and an exact channel or thread allowlist, and enforce both before any Discord request. Accept one strict Unicode grapheme containing an emoji code point or one exact name:snowflake custom emoji. Support normal and burst pages separately, require bounded responses, unique positive user IDs, strict ascending order after the exact cursor, and return only IDs and bot flags. Never expose or persist usernames, display names, avatars, profile fields, burst colors, queries, or raw user objects. An absent aggregate must return an empty page without calling the identity-bearing endpoint.

Keep the connector's own normal reaction changes in exact interaction scope. Read the exact message before changing state, return a journaled no-op without consuming the write limiter when the desired state already holds, write pending content-free activity before mutation, require Discord's exact no-content success status, and read the exact message again to prove the connector-owned flag. Never persist emoji text. Treat malformed or mismatched post-state as uncertain rather than success.

Do not add a reaction-moderation shortcut that bypasses the independent moderation toggle, exact reaction-channel allowlist, and protected-user check before any Discord request, pinned application and bot identities, connector-owned target exclusion before target access, exact message ID, complete role and overwrite evidence, private-thread membership, complete VIEW_CHANNEL, READ_MESSAGE_HISTORY, MANAGE_MESSAGES, and conditional CONNECT authority, strict scope-specific target, complete aggregate snapshot, process-keyed plan, signed interactive confirmation, write-aware host approval, final fresh-plan match, durable exact-message coordination, atomic one-shot operation-key reservation, pending content-free activity, one non-retried DELETE, target-absence proof, or exact aggregate readback. If a host cannot support MCP elicitation, keep reaction moderation unavailable through that host.

Limit moderation to three exact actions: remove one non-protected user's normal reaction to one emoji, clear all normal and burst reactions for one emoji, or clear every reaction from one message. Route connector-owned normal removal through the ordinary own-reaction tool. Treat emoji and all scopes as identity-blind: they can remove reactions from locally protected users, and protected-user IDs guard only exact user scope. Put this limitation in the keyed plan, warning list, and confirmation. Do not add fuzzy emoji or user resolution, names as write targets, batches across messages, reaction copying, automatic restore, retry, compensation, or rollback. An already-absent target must remain a record-free no-op.

Bind the strict request, complete aggregate snapshot, structured target, verified identities, guild ownership, message metadata, complete relevant permission evidence, transient local review reason, privacy projection, warnings, and domain-separated operation-key hash into the plan digest. The reason must never be sent as an audit-log header or persisted because Discord does not document reason support for reaction endpoints. Any relevant drift must invalidate approval before reservation.

Reserve and journal before one DELETE with automatic retries disabled. Prove the target absent afterward and report safe unrelated aggregate divergence as completed with drift. Only a known Discord 4xx rejection received without a success response, other than request timeout or rate limit, may settle as failed. Treat transport ambiguity, request timeout, rate limiting, server errors, malformed evidence, a surviving target, failed readback, or receipt finalization failure as uncertain and potentially completed. Permanently spend every reserved key and retain the exact-message claim after uncertainty until credential-free operator review resolves it.

Reaction-moderation activity may retain only exact guild, channel, message, target-user, and custom-emoji IDs, scope, a keyed emoji fingerprint, plan digest, operation-key hash, timestamps, fixed verification and outcome values, activity ID, and sanitized error category. Operation receipts remain narrower. Never persist Unicode emoji text, custom emoji names, message content, authors, user profiles, burst colors, aggregate counts, permission evidence, local reasons, raw keys, raw Discord responses, or transport causes.

Canonical source: SECURITY.md

Documentation generated for guildcontrol@0.0.0. Canonical source and edit history remain in the public repository. GuildControl is an independent project and is not affiliated with or endorsed by Discord Inc. Discord is used only to identify the platform that GuildControl connects to.