This commit was merged in pull request #716.
This commit is contained in:
@@ -232,6 +232,10 @@ The following sections document how each supported channel maps its native messa
|
||||
|
||||
**Outbound:** Adapter calls Discord REST `POST /channels/{id}/messages`. Markdown content is sent as-is (Discord renders it). For `contentType = "code"` the adapter wraps in triple-backtick fences with the `metadata.language` tag.
|
||||
|
||||
### Discord service ingress security
|
||||
|
||||
The Discord adapter is an authenticated gateway service, not an anonymous Socket.IO client. It presents `DISCORD_SERVICE_TOKEN` during its `/chat` connection and signs each inbound envelope using HMAC-SHA-256. The envelope contains the Discord native message ID and a generated correlation ID. Gateway verifies the service credential, signature, and configured guild/channel/user allowlists before agent dispatch, then rejects duplicate native message IDs inside its bounded replay window. All three allowlists are default-deny and required when the Discord plugin is enabled. The service credential is injected at runtime and is never logged or included in protocol payloads.
|
||||
|
||||
---
|
||||
|
||||
### Telegram
|
||||
|
||||
@@ -293,13 +293,24 @@ Each OIDC provider requires its client ID, client secret, and issuer URL togethe
|
||||
|
||||
### Plugins
|
||||
|
||||
| Variable | Description |
|
||||
| ---------------------- | -------------------------------------------------------------------------- |
|
||||
| `DISCORD_BOT_TOKEN` | Discord bot token (enables Discord plugin) |
|
||||
| `DISCORD_GUILD_ID` | Discord guild/server ID |
|
||||
| `DISCORD_GATEWAY_URL` | Gateway URL for Discord plugin to call (default: `http://localhost:14242`) |
|
||||
| `TELEGRAM_BOT_TOKEN` | Telegram bot token (enables Telegram plugin) |
|
||||
| `TELEGRAM_GATEWAY_URL` | Gateway URL for Telegram plugin to call |
|
||||
| Variable | Description |
|
||||
| ----------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `DISCORD_BOT_TOKEN` | Discord bot token (enables Discord plugin) |
|
||||
| `DISCORD_SERVICE_TOKEN` | Required high-entropy service credential used to authenticate and sign Discord ingress; inject through the approved secret mechanism only |
|
||||
| `DISCORD_SERVICE_USER_ID` | Required Mosaic service-principal user ID that owns persisted Discord conversations; the original Discord user ID remains audit metadata |
|
||||
| `DISCORD_GUILD_ID` | Discord guild/server ID |
|
||||
| `DISCORD_GATEWAY_URL` | Gateway URL for Discord plugin to call (default: `http://localhost:14242`) |
|
||||
| `DISCORD_ALLOWED_GUILD_IDS` | Required comma-separated Discord guild snowflake allowlist; default-deny |
|
||||
| `DISCORD_ALLOWED_CHANNEL_IDS` | Required comma-separated Discord channel snowflake allowlist; default-deny |
|
||||
| `DISCORD_ALLOWED_USER_IDS` | Required comma-separated Discord user snowflake allowlist; default-deny |
|
||||
| `TELEGRAM_BOT_TOKEN` | Telegram bot token (enables Telegram plugin) |
|
||||
| `TELEGRAM_GATEWAY_URL` | Gateway URL for Telegram plugin to call |
|
||||
|
||||
### Discord ingress security
|
||||
|
||||
When `DISCORD_BOT_TOKEN` is configured, `DISCORD_SERVICE_TOKEN`, `DISCORD_SERVICE_USER_ID`, and all three Discord allowlists are required. Gateway startup fails rather than enabling a broad or unauthenticated remote-control surface. The service user ID identifies a provisioned Mosaic service principal for persistence; the original Discord user ID is retained in ingress audit metadata. The service token is a secret supplied by the approved runtime secret mechanism and is never committed or logged.
|
||||
|
||||
Inbound Discord messages must originate from an allowed guild, channel, and user, mention the bot, and carry a signed envelope containing the native Discord message ID and a generated correlation ID. The gateway validates the service identity, envelope signature, and allowlists again before dispatching. Replayed Discord message IDs are rejected during the bounded ingress replay window. Durable inbox/idempotency retention is introduced with Tess durable state.
|
||||
|
||||
### Observability
|
||||
|
||||
|
||||
35
docs/scratchpads/tess-m1-sec-004-discord-ingress.md
Normal file
35
docs/scratchpads/tess-m1-sec-004-discord-ingress.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# Scratchpad — TESS-M1-SEC-004 Discord ingress
|
||||
|
||||
- **Task / issue:** TESS-M1-SEC-004 / #707
|
||||
- **Branch:** `fix/tess-discord-ingress` from `origin/main` at `59e49cfd`
|
||||
- **Objective:** Authenticate the Discord plugin service at gateway ingress; enforce explicit guild/channel/user allowlists; attach Discord message and generated correlation IDs; reject replayed native message IDs.
|
||||
- **Scope:** `plugins/discord`, `apps/gateway`, and existing Discord admin/developer protocol docs.
|
||||
- **Budget:** Task estimate 28K; no explicit hard cap supplied.
|
||||
- **Assumptions:** The Discord plugin and gateway share an injected high-entropy `DISCORD_SERVICE_TOKEN`; a configured Discord plugin fails closed without it. Allowlist configuration is comma-separated Discord snowflakes. Discord native message ID is the replay key, with bounded in-memory retention pending the M2 durable inbox/idempotency work.
|
||||
|
||||
## Plan
|
||||
|
||||
1. Add failing tests covering ingress service authentication/signing, unlisted guild/channel/user rejection, correlation propagation, and replay rejection.
|
||||
2. Implement the signed Discord ingress envelope and allowlist validation in the plugin.
|
||||
3. Authenticate and validate the envelope at the gateway boundary, then enforce bounded replay protection before agent dispatch.
|
||||
4. Document the service-token and allowlist operations; run focused and baseline gates; obtain independent review.
|
||||
|
||||
## Progress
|
||||
|
||||
- 2026-07-12: Intake complete; PRD TESS-SEC-005, architecture, and threat model reviewed.
|
||||
- Added service-token Socket.IO authentication, HMAC-signed Discord envelopes, default-deny guild/channel/user allowlists, correlated message metadata, bounded replay rejection, and fail-fast configuration checks.
|
||||
- Code and security reviews completed. Code review findings on service persistence ownership, package-boundary tests, disconnected ingress observability, and chat payload validation were remediated; final independent code review approved.
|
||||
|
||||
## Risks / blockers
|
||||
|
||||
- Existing `main` has known unrelated Prettier debt; only changed files will be held format-clean. Durable replay persistence is intentionally out of scope for this M1 prerequisite and belongs to TESS-M2 durable inbox/idempotency work.
|
||||
|
||||
## Verification evidence
|
||||
|
||||
- Focused ingress suite: `pnpm --filter @mosaicstack/gateway test -- discord-ingress.security.spec.ts` — 7 passed.
|
||||
- Gateway suite: `pnpm --filter @mosaicstack/gateway test` — 513 passed, 11 skipped.
|
||||
- Plugin suite: `pnpm --filter @mosaicstack/discord-plugin test` — no tests, passed by configured `--passWithNoTests`.
|
||||
- `pnpm typecheck` — passed.
|
||||
- `pnpm lint` — passed.
|
||||
- `pnpm format:check` — fails only on the known pre-existing Tess documentation debt listed in the task dispatch; changed files pass targeted Prettier verification.
|
||||
- Codex security review — no findings; final Codex code review — approved.
|
||||
Reference in New Issue
Block a user