Invites
Keep guild invite inventory behind its own audit toggle and exact guild allowlist. The allowlist must remain a subset of any configured read-guild scope. Require verified application and bot identities, an exact guild and owner, exact connector membership, complete bounded roles and channels, a complete bounded invite inventory, and effective guild-level MANAGE_GUILD before returning an inventory or plan. Do not add a reduced VIEW_AUDIT_LOG mode because Discord withholds complete invite metadata without MANAGE_GUILD.
Keep creation behind a separate capability gate, exact direct-channel allowlist, and private capability-root allowlist. Require finite positive lifetime and use limits, forced uniqueness, explicit temporary-membership intent, an audit reason, one-shot operation key, bearer-capability acknowledgment, exact VIEW_CHANNEL and CREATE_INSTANT_INVITE evidence, plus guild-level MANAGE_GUILD for exact-user acceptance, a fresh canonical absent output target, process-keyed planning, signed approval, write-aware host approval, durable exact-channel and guild invite-collection coordination, pending content-free activity, one non-retried POST, strict response evidence, exact readback, and exclusive process-owned 0600 file delivery. Never return the created code or URL through MCP.
Keep invite role assignment behind an additional capability, a nonempty exact role allowlist, and enabled nonprivileged Gateway layout evidence. Require a tagged exact role set, explicit persistent-grant acknowledgment, MANAGE_ROLES, one unambiguous connector highest role, strict hierarchy, standard unmanaged roles other than @everyone, no ADMINISTRATOR, no unknown role or selected-overwrite permission bit, and proof that every guild and channel permission conferred by the selected roles is already held by the connector. Reject temporary membership, incomplete or obfuscated channel metadata, topology or continuity drift, unresolved overwrites, and a permission impact too large for human review. Project the minimum new ordinary member before and after the role set across every direct channel, disclose every guild and changed-channel permission decision plus high-risk gain, and state that existing members can accept, granted roles persist after invite expiry or deletion, removal is a separate action, and later role or channel-overwrite edits can change authority before or after acceptance.
Require every creation request to select one exact acceptance mode. bearer permits anyone holding the finite capability to attempt acceptance. exact-users additionally requires one bounded nonempty set of unique positive user IDs. Canonicalize that set numerically and bind every exact ID into the transient plan, digest, and signed confirmation, but never place it in activity, operation, diagnostic, telemetry, or capability-file output. The private file may contain only the acceptance kind and target count so sharing it with one intended recipient cannot disclose the others.
Generate the exact-user CSV inside the connector and accept no caller-provided bytes, path, filename, form field, role payload, or raw multipart payload. Keep the file reservation empty while polling Discord's target-user job under a fixed bound. Require strict create-response and independent readback agreement on the exact assigned-role set before capability delivery, then require a strict completed target-user status with exact totals and an authenticated bounded CSV readback whose canonical user set exactly matches the review when applicable. Coordinate every selected role in addition to the exact channel and guild invite collection. A failed, incomplete, timed-out, malformed, or mismatched role or target-user verification is uncertain after dispatch, spends the operation key, keeps the channel quarantined, and never writes the capability file. Do not retry, update, revoke, replace, remove roles, or compensate automatically.
Discord exposes no conditional target-user snapshot that can bind verification atomically to local capability delivery. Coordinate connector-owned writes durably, require an exclusive external invite-administration window, and disclose the remaining race in every exact-user plan. A remote invite can remain after an uncertain target-user job even though its code was never disclosed. Require manual inventory review by an authorized operator rather than claiming cleanup.
For guild inventory and opaque-reference lookup, keep invite codes transient inside the REST and invite service layers. Project raw Discord responses before forming an MCP result. Return only a process-keyed opaque reference, bounded channel and invite metadata, exact inviter and target IDs without profiles, usage and lifetime properties, known and unknown flags, and granted-role IDs with permission evidence. Never return a code, URL, guild object, user profile, role name or visual, application metadata, scheduled-event or stage object, Discord-derived target-user acceptance set, approximate count, raw response, or unknown field. Do not add invite acceptance, arbitrary code lookup, URL input, a code-bearing MCP schema, or an immediate creation shortcut outside the separately gated private-file workflow.
Guild vanity URL audit is the narrow exception for a custom public invite code. Reuse only the invite-audit capability and exact guild allowlist, require complete owner or MANAGE_GUILD evidence, validate the VANITY_URL feature, and cross-check the guild object against Discord's documented vanity read. Omit the code by default and from every resource; disclose it transiently only when the strict tool input sets includeCode: true. Never return a full URL or persist, cache, summarize, log, trace, or export the code. Do not implement an undocumented vanity mutation route.
Authenticate every local page cursor and bind it to the exact guild, full inventory digest, and next offset. Fetch and validate another complete fresh inventory for every continuation page and exact-reference lookup. Reject cursor tampering, foreign process state, changed metadata or use counts, missing channels or roles, contradictory target semantics, duplicate capabilities, unsupported invite types, and any inventory above the local safety ceiling. A reference and cursor must expire on connector restart and must never be reversible to an invite code.
Keep revocation behind a second capability gate and require the same exact guild audit scope. Do not add an immediate-call path. Preserve every gate: verified identities, exact scope, complete evidence, process-keyed full-inventory plan binding, signed interactive confirmation, write-aware client approval, final fresh-plan match, atomic one-shot operation-key reservation, pending content-free activity, one non-retried DELETE, exact returned-target validation, and a fresh complete inventory proving absence. Reject the target code and invite URLs in the audit reason before mutation.
Discord deletes an invite by code and supplies no conditional operation that atomically binds the preceding inventory. Treat the final inventory-to-delete interval as an unavoidable external race. Surface it in every deletion plan and require exclusive invite administration for high-risk revocation. Deletion prevents later use but does not remove members or roles granted by earlier uses, so surface prior use as a risk and never describe revocation as retroactive access removal. Freshness, a digest covering the complete inventory, least-privilege MANAGE_GUILD, returned-target validation, and full absence readback reduce but cannot eliminate the race.
Replace the code-bearing REST path with a fixed diagnostic route and suppress the underlying transport cause. Mark invite list and deletion operations content-sensitive so response text and external error details cannot enter diagnostics. Observability may receive only fixed operation names, risk classes, outcomes, numeric status, retry data, and durations. Never include the code, URL, raw route, response, audit reason, opaque cursor payload, or permission evidence in errors, logs, telemetry, activity, or operation receipts.
A known pre-write Discord 4xx may be classified as failed. Treat transport failure, Discord 5xx, malformed success, returned-target mismatch, failed absence readback, or any otherwise indeterminate post-reservation state as uncertain and potentially completed. Spend every reserved key after every outcome, retain a permanent same-reference uncertainty barrier inside the service instance, and never retry or compensate automatically. The production facade also acquires a durable exact guild invite-collection claim, so connector processes sharing the activity-state root exclude overlapping revocations and retain the claim after uncertainty.
Invite creation and deletion activity and operation records may contain only exact guild, channel, and selected role IDs, the opaque invite reference when available, plan digest, operation-key hash, timestamps, fixed acceptance, role-assignment, verification and outcome values, activity ID, and sanitized error category. Never persist invite codes, URLs, target-user IDs or CSV, profiles, names, role permissions or impact, local paths, audit reasons, raw operation 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.