Reviewed local-file attachment messages
Attachment messages have no immediate-call path. Set capabilities.attachments: true, list every eligible channel or thread by its own exact ID in scopes.attachmentChannelIds, and set storage.attachmentRoots to one absolute canonical owned directory or a JSON array of such directories. The channel allowlist must be a subset of readScope.channelIds when the read allowlist exists. A parent channel never grants attachment scope to a child thread. With a channel read allowlist, a thread workflow requires both the parent and thread in read scope, plus the thread's own attachment-scope entry. Grant View Channel, Read Message History, Attach Files, and either Send Messages or Send Messages in Threads as applicable. Attachment-root configuration is rejected on runtimes without numeric process-ownership evidence; leave attachment roots unset to use the other capabilities there.
The narrow surface accepts one exact absolute local path, optional plain-text message content, an optional safe attachment filename, an optional accessibility description, an optional exact reply, explicit notification settings, and a unique one-shot operation key. It never accepts remote URLs, data URLs, base64 payloads, directories, multiple files, or streams. The configured byte ceiling defaults to and cannot exceed 10 MiB, matching Discord's default per-file limit rather than assuming boosted-guild limits.
- Place the intended file inside a dedicated configured attachment root and ensure it has one hard link and is owned by the connector user.
- Call
plan_attachment_messagewith the exact channel, path, message fields, optional reply and notification settings, and one-shot operation key. - Review the exact guild and channel IDs, canonical path, stable file properties and byte size, filename and description, content, reply, notification users, required and effective permissions, warnings, operation-key hash, and keyed digest.
- Call
execute_attachment_messagewith identical inputs plus the digest. - Approve the signed MCP confirmation only if every exact ID, byte-bound file property, message field, permission, warning, hash, and digest remains intended.
- Review the returned message ID, jump URL, attachment filename and size, activity ID, and verified outcome. The result never exposes Discord's attachment URL.
Each plan opens the file without following the final symlink, validates its identity and metadata before and after an exact bounded read, then checks the path again. Planning rejects path escapes, symlinks in the resolved path, hardlinks, foreign ownership, non-regular or empty files, oversized files, and files or paths that change while being read. A process-keyed HMAC of the bytes and stable device, inode, ownership, mode, size, and nanosecond timestamps are bound into the plan without exposing the byte digest. The MCP adapter plans again before elicitation, and the service reads and plans a third time immediately before mutation; any mismatch blocks execution.
Notification behavior matches plain-text sends: nobody is notified by default, every notified user must be locally allowlisted and visibly mentioned in the content, role and mass mentions stay disabled, and reply-author notification is a separate reviewed boolean whose exact author is checked. The planner also requires complete role and overwrite evidence for every permission. Threads use their parent's permission overwrites but still require their own exact attachment-channel allowlist entry.
After the final matching plan, execution consumes the shared process-local interaction budget, atomically reserves the operation-key hash in a durable private receipt, and appends a pending content-free activity record. It uploads the in-memory byte snapshot through one native multipart request with nonce enforcement and no automatic retry. The create response must match the nonce and complete reviewed message. An exact message GET must then match the verified bot, channel, guild, reply, content, and single attachment's filename, description, and size; an omitted optional nonce is accepted, but a conflicting nonce is not.
The raw key, local path, filename, description, file size and digest, message content, notification user IDs, and attachment URL never enter the activity log or operation receipt. A reserved key remains spent after known failure, uncertainty, or local recording failure. Transport errors, Discord 5xx responses, and any outcome after a message ID becomes known are uncertain; do not retry them. Inspect the exact channel and returned message ID when available before considering a fresh reviewed request with a new key. The connector never retries the multipart POST or deletes a message as rollback. See Discord's message resource reference for multipart files, attachment metadata, nonce enforcement, replies, and allowed mentions.
Canonical source: docs/reference.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.