Thread state and governance
Keep exact thread-state audit behind its own toggle plus non-empty exact guild and thread allowlists. Do not derive authority from ordinary thread listing, a readable parent, thread creation, forum-post creation, pin management, permission-overwrite management, or Gateway events. Require a separate exact user allowlist for membership reads and actions. Verify pinned identities, exact guild ownership, strict thread and supported parent identity, exact connector membership, complete bounded roles and parent overwrites, effective inherited permissions, and known lifecycle metadata before returning a state or plan.
Return only bounded untrusted names, exact IDs, supported lifecycle fields, membership booleans and nullable join times, complete permission evidence, discarded unknown-field counts, and fixed privacy claims. Never enumerate members or return messages, member counts, message counts, last-message IDs, applied tags, flags, thread timestamps, raw permission summaries, embedded guild members, unknown-field values, or raw Discord payloads. Request exact thread membership without embedded guild-member hydration, and fetch an optional target profile only through the exact guild-member endpoint. Treat an exact thread-member 404 as absence only after the exact thread, parent, guild, connector identity, and inherited access evidence have succeeded; every other member-lookup failure remains an error, and a thread GET 404 never proves a no-op. Do not cache, persist, journal, or export a thread-state or membership read.
Keep rename, archive, unarchive, lock, unlock, auto-archive, slowmode, invitation-policy, member-add, and member-remove changes behind an independent toggle and a strict action-specific schema. Each metadata PATCH must contain exactly one supported field. Permit invitation-policy changes only on private threads. Reject bulk operations, member listing, self join or leave, deletion, applied-tag changes, pin changes, message edits, multi-field PATCH requests, retry, rollback, compensation, and reconciliation.
Require complete VIEW_CHANNEL evidence for every real change. Require MANAGE_THREADS for rename, archive, auto-archive, lock, unlock, slowmode, and invitation-policy changes; and require exact connector membership plus SEND_MESSAGES_IN_THREADS for unarchive, with MANAGE_THREADS when the thread is locked. Member addition also requires SEND_MESSAGES_IN_THREADS, connector membership or MANAGE_THREADS, and complete target parent visibility, with MANAGE_THREADS required when a private thread is non-invitable. Member removal requires MANAGE_THREADS or exact connector ownership of a private thread. A real removal must reject protected users, the guild owner, and administrators. Fail closed on archived or locked state where the requested action is unsafe, unknown metadata fields, unknown permission bits, incomplete roles or overwrites, malformed memberships, unsupported relationships, mismatched IDs, or absent action-specific evidence.
Do not add an immediate-call path. Preserve every gate: exact policy, pinned identity, strict minimized evidence, action-specific authority, protected-target checks, 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 write, strict response validation where Discord returns state, and exact state or membership readback. A verified already-current request must reserve nothing, journal nothing, request no confirmation, and issue no write. A client without MCP elicitation must not execute thread changes.
Bind the normalized action-specific request, verified application and bot, exact guild, parent and thread, connector and optional target membership, complete roles and overwrites, supported current lifecycle state, complete permission and authority evidence, privacy projection, audit reason, and domain-separated operation-key hash into the plan. Any relevant identity, relationship, membership, role, overwrite, lifecycle, permission, desired value, reason, or key change must invalidate review. Keep the raw operation key out of plan material and signed request state.
Reserve and journal before dispatching one PATCH, PUT, or DELETE. Never retry a write. Validate a returned PATCH response and then perform exact fresh state or membership readback. Report a controlled success with safe unrelated lifecycle divergence as completed with drift. Only a known pre-write Discord 4xx refusal may settle as failed. Treat rate limits, transport or server errors, malformed or mismatched success, readback failure, local finalization failure, or any other indeterminate post-reservation result as uncertain and potentially completed. Spend every reserved key permanently and serialize exact guild and thread targets inside one process as defense in depth. The production facade acquires a durable exact thread claim plus an exact member claim for membership actions, so connector processes sharing the activity-state root exclude overlapping changes and retain the claims after uncertainty.
Thread-governance activity and operation records may contain only the action, exact guild and thread IDs, optional target user ID, plan digest, operation-key hash, timestamps, fixed verification and outcome values, activity ID, and sanitized error category. Never persist parent IDs, thread or member names, lifecycle values, membership timestamps, role or permission evidence, audit reasons, raw operation keys, Discord responses, or transport causes. Discord does not document an audit-log reason header for thread-member endpoints, so never claim that a member PUT or DELETE wrote its transient reviewed reason to Discord's audit log.
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.