Member voice state and moderation
Keep exact member voice-state audit behind its own toggle plus non-empty exact guild and voice-channel allowlists. Do not derive scope from the member directory, ordinary guild reads, a category, thread inheritance, Stage lifecycle, or member administration. Verify the pinned identities, exact guild and target membership, strict voice-state response identity, source-channel ownership and supported type, complete roles and overwrites, and effective connector VIEW_CHANNEL plus CONNECT before returning one state. Treat only Discord error code 10065 as a disconnected target; a generic 404 or malformed result is not absence evidence.
Return only verified application, bot, guild, owner, and target IDs, bounded untrusted guild, member, and channel names, connection state, exact allowlisted source channel, server mute and deafen booleans, complete read-permission evidence, discarded unknown-field count, and fixed privacy claims. Never enumerate occupants or return session IDs, embedded members, self mute or deafen state, stream or camera state, Stage suppression or request-to-speak state, unknown-field values, or raw Discord payloads. Do not cache, persist, journal, or export a voice-state read.
Keep move, disconnect, server-mute, server-unmute, server-deafen, and server-undeafen behind an independent change toggle and a strict action-specific schema. Mutate only ordinary voice channels; Stage participants are always read-only. Reject the connector bot, guild owner, pending members, administrators, protected user IDs, and targets not strictly below the connector's unique highest role. Require complete VIEW_CHANNEL, CONNECT, and MOVE_MEMBERS evidence on source and destination for move, source for disconnect, VIEW_CHANNEL, CONNECT, and MUTE_MEMBERS on source for server mute state, or VIEW_CHANNEL, CONNECT, and DEAFEN_MEMBERS on source for server deafen state. A move must separately prove target VIEW_CHANNEL and CONNECT on the exact destination. Fail closed on unknown permission bits, incomplete or ambiguous roles, duplicate or unresolved overwrites, unsupported channel types, mismatched IDs, or state outside exact local scope.
Do not add an immediate-call path, occupant list, bulk action, fuzzy member or channel lookup, generic member update, Stage participant control, retry, rollback, compensation, or reconciliation. Preserve every gate: exact scope, pinned identity, protected-target checks, complete permission and strict local hierarchy evidence, exact current state, process-keyed planning, signed interactive confirmation, write-aware host approval, final fresh-plan match, atomic one-shot operation-key reservation, pending content-free activity, one non-retried one-field PATCH, strict response validation, and exact voice-state readback. A client without MCP elicitation must not execute member voice changes.
Bind the action-specific request, verified identities, guild owner, exact bot and target memberships, complete roles, source and destination channels and overwrites, current state, action-specific connector permissions, target destination access, strict hierarchy, privacy projection, audit reason, and domain-separated operation-key hash into the plan. Any relevant identity, membership, role, channel, overwrite, state, permission, destination, enabled value, reason, or key change must invalidate review. A verified already-current request must reserve nothing, journal nothing, request no confirmation, and issue no write.
Reserve and journal before dispatching one PATCH whose body contains exactly one of channel_id, mute, or deaf. Validate the returned user identity, every response field that can prove controlled state, and unchanged uncontrolled state, then perform an exact fresh voice-state readback and recheck channel scope. Report controlled success with unrelated state divergence as completed with drift. Only a known Discord 4xx refusal other than 429 returned instead of a successful mutation response may settle as failed. Treat a rate limit, transport or server error, malformed success, response mismatch, readback failure, local finalization failure, or other indeterminate post-reservation result as uncertain and potentially completed. Spend every reserved key permanently, serialize exact guild and user targets inside one process as defense in depth, never retry, and never roll back automatically. The production facade acquires a durable exact member claim, so connector processes sharing the activity-state root exclude overlapping voice changes and retain the claim after uncertainty without persisting voice-channel IDs.
Member-voice activity and operation records may contain only the exact guild and user IDs, action, plan digest, operation-key hash, timestamps, fixed verification and outcome values, activity ID, and sanitized error category. Never persist source or destination channel IDs, voice-state booleans, names, role or permission evidence, hierarchy, audit reasons, raw operation keys, 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.