claude-ceo-assistant db21c92acd
meta-ci-advisory / meta (advisory) (push) Successful in 5s
ci / quality (push) Successful in 22s
ci / sdk-contract (push) Successful in 24s
ci / security (push) Successful in 26s
ci / e2e (3.13) (push) Successful in 26s
ci / unit-tests (3.13) (push) Successful in 28s
ci / e2e (3.11) (push) Successful in 28s
ci / unit-tests (3.11) (push) Successful in 34s
Merge pull request 'fix(release): source identity from Infisical and bind exact tag provenance' (#4) from ci/release-bot-token-to-infisical into main
2026-10-12 01:02:53 +00:00

Discord channel plugin for Molecule

discord-channel-molecule is a supervised kind: channel plugin. It connects one Discord bot to one host workspace through Discord Gateway v10 and Molecule's public channel API v1 Unix-socket contract. It does not import Molecule Core or private runtime modules.

Status: the implementation and hermetic end-to-end suite are complete, but the plugin is not yet canary-proven, tagged, or released. Install only an exact reviewed commit in a nonproduction workspace until the canary and manual release gate are complete.

Supported behavior

Surface Behavior
Direct messages Accepted only when DMs are explicitly enabled and the user and DM channel IDs are allowlisted.
Guild messages Accepted only when the user, guild, and channel IDs are allowlisted and Discord's structured mentions array contains the exact configured bot ID. Mention-looking text alone does not activate the bot.
Replies Reply to the originating message, suppress all generated mentions, emit typing indicators, and split text losslessly at Discord's 2,000-character limit.
Delivery Persist the authorized payload as queued before the in-memory queue. A restart safely restores queued work. Immediately before A2A, atomically transition to dispatching and discard the payload; a recovered dispatch or ambiguous host/provider result is never replayed. Only definitively delivered/failed tombstones are prunable.
Gateway Persist session_id, sequence, and resume_gateway_url; heartbeat, RESUME, bound the event queue, and use capped reconnect backoff.

Text messages are the v0.1 scope. Attachments, slash-command Interactions, unsolicited outbound notifications, threads as a distinct conversation model, and webhook-only operation are not implemented.

Architecture and trust boundary

Discord Gateway MESSAGE_CREATE
  -> exact bot/user/guild/channel policy
  -> owner-only atomic message journal
  -> SDK 0.5.4 channel API v1 over authenticated private UDS
  -> host workspace agent
  -> Discord REST reply (single attempt per chunk)

The runtime injects MOLECULE_CHANNEL_API_VERSION, MOLECULE_CHANNEL_A2A_SOCKET, MOLECULE_CHANNEL_A2A_TOKEN, and MOLECULE_CHANNEL_PLUGIN_ID. The bot token and allowlists are workspace configuration owned by this plugin. Neither set belongs in Core configuration, an org template, plugin.yaml, a prompt, or a git repository.

Discord application setup

  1. Create an application and bot in the Discord Developer Portal.
  2. Enable the privileged MESSAGE_CONTENT Gateway intent. The plugin requests only guild messages, direct messages, and message content. Discord closes a connection that requests this intent without portal authorization.
  3. Generate an OAuth2 bot invite for the nonproduction server. Grant only the channel permissions needed for the canary: View Channel, Send Messages, and Read Message History.
  4. Enable Developer Mode in Discord and copy the bot user ID, allowed human user IDs, server/guild ID, and exact channel IDs.
  5. Copy the bot token once into the workspace secret surface. Never paste it into chat, commit it, put it in a URL, or include it in a support log.

Discord's current Gateway, privileged-intent, resume, and message constraints are linked in PROVENANCE.md.

Install an immutable plugin revision

Use the platform's authenticated plugin-management UI or management client to install a reviewed commit, not a moving branch. The immutable source value is:

gitea://molecule-ai/discord-channel-molecule#<reviewed-commit>

Do not place a workspace token in a URL, command argument, shell history, or support conversation. Follow the current platform plugin-install documentation for the credential-safe invocation supported by that client.

Plugin installation restarts the workspace. Configure all values below through the workspace Secrets UI or the authenticated workspace secret-management surface, preferably in one update so the daemon sees a consistent set on its next start.

Configuration

Name Required Meaning
DISCORD_BOT_TOKEN yes Discord bot token; secret and redacted from configuration/log representations.
DISCORD_BOT_USER_ID yes Exact numeric bot user ID; READY must report the same identity.
DISCORD_ALLOWED_USER_IDS yes Comma-separated numeric user IDs. Empty or malformed values fail startup.
DISCORD_ALLOWED_CHANNEL_IDS yes Comma-separated numeric guild or DM channel IDs.
DISCORD_ALLOWED_GUILD_IDS guild use Comma-separated guild IDs. Empty means every guild message is denied.
DISCORD_ALLOW_DMS no Exact true enables allowlisted DMs; default false.
DISCORD_STATE_DIR no Dedicated owner-only journal directory. Defaults to discord-channel-molecule below the runtime's plugin state directory. Existing directories/files must already be owned by the daemon and mode 0700/0600; symlinks, nonregular files, and unsafe parents fail startup.
DISCORD_QUEUE_SIZE no Bounded inbound queue, 1–1024; default 64.
DISCORD_MAX_STATE_MESSAGES no Dedup/journal retention, 10–10,000; default 10,000. Queued, dispatching, and ambiguous entries are never pruned.
DISCORD_HTTP_TIMEOUT_SECONDS no Bounded Discord REST timeout; default 15.
DISCORD_HOST_TIMEOUT_SECONDS no Bounded workspace turn timeout; default 600.
DISCORD_RECONNECT_MAX_SECONDS no Reconnect backoff ceiling; default 60.

DISCORD_API_BASE_URL and DISCORD_ALLOW_INSECURE_TEST_ENDPOINTS exist only for the hermetic loopback suite. Production accepts only https://discord.com/api/v10 for REST and wss://gateway.discord.gg for the Gateway, so a configuration override cannot redirect the bot token.

Canary checklist

Do not release or replace the native path from unit/CI evidence alone.

  1. Install an exact reviewed commit into one disposable nonproduction workspace.
  2. Confirm the daemon reaches READY with no secret values in its logs.
  3. From an allowed user and channel, verify one DM (if enabled) and one exact guild mention each produce one agent reply.
  4. Verify an unlisted user, unlisted channel, unlisted guild, bot-authored message, webhook-authored message, and mention-looking text without a real Discord mention produce no workspace turn.
  5. Restart before a controlled queued message crosses A2A and verify it is recovered once. Restart after a controlled dispatch begins and verify the retained ambiguous tombstone prevents a duplicate host turn.
  6. The human release approver, signed in as Gitea user hongming, records the exact full commit SHA in one evidence comment on internal#1071 under the same repository owner as this plugin (currently molecule-ai) using exactly these two lines once each: discord-channel-molecule canary commit: <full-sha> and discord-channel-molecule non-production canary: PASS.
  7. Only then manually dispatch .gitea/workflows/release.yml on exact main with that commit, evidence URL, and confirmation phrase.

The release workflow never runs on a push or tag. It re-runs all tests, validates the SDK-vendored client, builds the source/wheel artifacts, verifies that the exact evidence comment names the exact release SHA, rechecks live main, and creates v<plugin.yaml version> idempotently through the release-bot token. The preexisting-tag path, the HTTP 201 body, and the authoritative GET after every tag POST outcome must all agree on the exact tag name, target SHA, and canonical message binding that evidence URL; a same-SHA tag without that provenance fails closed. Infisical and release credentials reach authenticated HTTP clients only through stdin/config file descriptors, never process arguments; every authenticated curl invocation disables ambient curl configuration before reading that input. The non-mutating preflight requires authenticated repository push permission; the single tag attempt plus authoritative reconciliation remains the exact tag-write proof. Repository administration must protect v* tags from direct pushes.

Migration from the retired native Discord channel

The old Core subsystem used webhook_url and chat_id for outbound Incoming Webhook delivery plus a Discord Application public_key and platform Interactions endpoint for inbound slash commands. This plugin instead uses one bot token and a persistent Gateway connection for conversational DMs and exact mentions.

There is no automatic credential or behavior conversion:

  • create a bot token and explicit allowlists; an Incoming Webhook URL cannot be converted into a bot token;
  • do not restore or dual-run the retired Core subsystem for rollback; disable this plugin if the canary fails and leave Discord disconnected while fixing or rolling back to an earlier reviewed plugin commit;
  • migrate slash-command users to DM or exact-mention conversations; slash commands require a separately reviewed plugin feature;
  • validate inbound and reply continuity at the pinned plugin commit; and
  • after the cutover evidence is accepted, revoke the old webhook token and remove the retired Interactions endpoint/public key and legacy configuration.

This is deliberately not described as parity before the real canary.

Development and verification

The repository started from the published molecule-ai-sdk==0.5.4 kind: channel scaffold. channel_sdk.py remains byte-identical to that release.

python3.11 -m venv .venv
.venv/bin/pip install --require-hashes -r requirements-dev.lock
.venv/bin/ruff check .
.venv/bin/pytest -q -m "not e2e"
.venv/bin/pytest -q -m e2e
.venv/bin/bandit -q -r . -x ./.venv,./tests,./channel_sdk.py
.venv/bin/pip-audit --require-hashes --disable-pip -r requirements.lock

Validate the plugin and SDK vendor boundary with SDK 0.5.4:

molecule-plugin validate plugin .
molecule-plugin channel-sdk check .

CI runs quality, unit, hermetic E2E, SDK drift, dependency audit, and static security jobs on every pull request and push. The advisory meta-CI consumer also derives the canonical skill-lint bundle from repo-meta.yaml. No test requires Discord or Molecule credentials.

S
Description
Molecule Discord channel plugin using the public Plugin SDK API-v1 contract
Readme MIT
327 KiB
Languages
Python 91.3%
Shell 8.7%