Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 2 additions & 43 deletions docs-web/architecture/chat-connectors/discord.md
Original file line number Diff line number Diff line change
@@ -1,46 +1,5 @@
# Discord Connector Profile

Discord has two independently selected transports. The existing `webhook` mode preserves custom bot/gateway URLs and stored connection compatibility. The provider-native `official_api` mode owns Discord HTTP interaction authentication, Gateway v10 message delivery, REST replies, and read-only credential verification.
Discord is registered only with the existing `webhook` bot/gateway transport. Its module owns the unchanged setup schema, message normalizer, authentication headers and signature bases, outbound gateway mapping, response parsing, verification metadata, and session requirement.

## Official configuration and trust boundary

The official setup requires an application ID, the application's hexadecimal Ed25519 public key, a Gateway intents bitfield, and a write-only bot token. The default bitfield is `37377`: `GUILDS`, `GUILD_MESSAGES`, `DIRECT_MESSAGES`, and privileged `MESSAGE_CONTENT`. Operators must enable `MESSAGE_CONTENT` in the Discord Developer Portal to receive ordinary message bodies.

Official REST requests are pinned to `https://discord.com/api/v10`. Gateway and resume URLs must use secure Discord-owned `discord.gg` hosts. Values retained for legacy webhook connections cannot redirect official traffic. Bot tokens, interaction tokens, and authorization headers are excluded from persisted Gateway state and delivery metadata.

## Interaction ingress

The profile validates `X-Signature-Ed25519` against `X-Signature-Timestamp` plus the exact raw body before JSON parsing. Missing or malformed headers, malformed keys, stale timestamps, invalid signatures, malformed JSON, and unsupported interaction shapes produce deterministic classified failures. Authenticated type-1 validation requests receive the required JSON PONG response.

`ChatProviderIngressSecurity` invokes the optional provider-native hook before generic bearer/HMAC handling. The production ingress route returns an immediate authenticated handshake response when present and otherwise continues into the existing ingress service.

Application command, component, and modal payloads normalize into stable external channel, sender, interaction-message, and Discord thread identities. Gateway `MESSAGE_CREATE` payloads use the same normalized contract.

## Gateway state machine

`DiscordGatewaySession` is transport- and persistence-neutral. It receives injected WebSocket, timer, delay, and session-store boundaries so unit tests are completely offline.

The state machine implements:

- `Identify` and `Resume` payloads for Gateway v10;
- latest dispatch sequence tracking;
- first-heartbeat jitter, recurring heartbeats, ACK tracking, and immediate reconnect after a missed ACK;
- persistence of only `sessionId`, Discord resume URL, sequence, and bot user ID;
- resume after recoverable closes and re-identify after invalid sequence, timed-out session, or non-resumable invalid-session responses;
- bounded exponential reconnect backoff;
- terminal classifications for invalid auth, invalid intents, missing privileged intent access, shard errors, and unsupported Gateway versions;
- cancellation and clean shutdown that stop timers, close the socket, and prevent reconnects.

The bot user ID from `READY` suppresses the connector's own `MESSAGE_CREATE` events without suppressing messages from unrelated bot accounts.

## REST replies and verification

Message creation disables all automatic mentions with `allowed_mentions.parse: []`, supplies a stable delivery nonce with `enforce_nonce`, preserves reply references, and accepts only snowflake message IDs from successful responses. Route/global reset headers and `Retry-After` are honored with one bounded immediate 429 retry; further rate limits are returned to the outer delivery scheduler.

The configured outbound adapter caches the profile's official executor, preserving Discord rate-limit state across production deliveries. Profiles without an executor, including Discord `webhook`, retain the generic HTTP or command path.

Credential verification performs only `GET /users/@me`. Typed results distinguish invalid authentication, missing permissions, rate limiting, timeout, cancellation, ambiguous network outcomes, provider unavailability, and invalid responses without retaining token-bearing messages.

The profile remains side-effect free when the registry is constructed. Network and Gateway work begins only when the corresponding runtime client or session is explicitly started.

References: [Gateway](https://docs.discord.com/developers/events/gateway), [Gateway events](https://docs.discord.com/developers/events/gateway-events), [Interactions](https://docs.discord.com/developers/interactions/overview), [Messages](https://docs.discord.com/developers/resources/message), and [Rate limits](https://docs.discord.com/developers/topics/rate-limits).
The baseline profile has no live test and does not advertise managed, native, or `official_api` modes. Unsupported combinations fail before network or process execution.
2 changes: 1 addition & 1 deletion docs-web/architecture/chat-connectors/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Code UX registers one typed, independently editable profile for each external ch

The registry is static and side-effect free. Network requests and native command execution remain in service-layer facades. Lookup fails closed when a provider or provider/mode combination is not registered.

The additive `official_api` mode is implemented by WhatsApp, Telegram, Slack, Microsoft Teams, and Discord without changing the persisted meaning of `managed_bridge`, `webhook`, or `native_bridge`. Profiles advertise only implemented modes.
The additive `official_api` mode is available to future profiles without changing the persisted meaning of `managed_bridge`, `webhook`, or `native_bridge`. Baseline profiles advertise only implemented modes.

## Provider Profiles

Expand Down
46 changes: 2 additions & 44 deletions docs-web/architecture/chat-connectors/microsoft-teams.md
Original file line number Diff line number Diff line change
@@ -1,47 +1,5 @@
# Microsoft Teams Connector Profile

The Microsoft Teams connector retains the `managed_bridge` and custom `webhook` transports and adds a direct `official_api` profile based on Microsoft Bot Connector Activities.
Microsoft Teams is registered with `managed_bridge` and `webhook` transports. Its module owns the unchanged setup schemas, Bot Framework activity normalizer, ingress authentication metadata, outbound request mapping, response parsing, configuration verification, and official reference metadata.

## Configuration contract

`official_api` stores the non-secret Microsoft app ID, `MultiTenant` or `SingleTenant` application type, and optional tenant ID in connection setup. The client secret is a required write-only credential. Single-tenant configurations require a tenant ID; a tenant ID on a multi-tenant configuration acts as an inbound tenant restriction.

No setup field accepts an official Connector service URL. The service URL is learned only from an authenticated Activity and stored in its conversation reference after claim and host validation.

## Authentication boundary

`MicrosoftBotAuthService` owns provider-specific trust and transport behavior:

1. Read a Bearer JWT from the request header and require `RS256`.
2. Load Microsoft's fixed Bot Connector OpenID metadata and JWKS documents, validate their fixed issuer/JWKS/algorithm contract, and cache signing keys for at most 24 hours.
3. Refresh once within a bounded interval when a previously unseen key ID indicates rotation.
4. Verify the RSA signature, issuer, app-ID audience, `nbf`/`iat`/`exp` window with five-minute skew, exact Activity/JWT service URL match, channel endorsement, and configured tenant.
5. Accept only HTTPS service URLs on the documented public, GCC, GCC High, and DoD Teams Connector host allowlist; arbitrary `*.botframework.com` subdomains are rejected.

There is no insecure mode for disabling signature, claim, endorsement, tenant, or service URL validation. Authentication output contains the Activity, normalized message, and a durable conversation reference, but never the Bearer JWT or signing key.

## Activity mapping

Only `message` Activities enter chat ingestion. Unsupported types fail before delivery or conversation-message creation. The normalizer removes the bot recipient's mention entity from visible text while leaving other mentions intact and preserves:

- Activity, conversation, and reply IDs;
- locale and Bot Framework channel ID;
- tenant, team, and Teams channel IDs;
- original sender, bot recipient, and conversation account; and
- an authenticated `serviceUrlValidated: true` conversation reference.

The conversation reference is safe to persist with delivery metadata because it contains routing identities and the validated URL, not access tokens, client secrets, JWTs, or signing keys.

## Outbound transport

The service acquires app-only OAuth tokens from Microsoft's documented v2 client-credential endpoint. Multi-tenant apps use the `botframework.com` authority and single-tenant apps use their configured tenant. Tokens request `https://api.botframework.com/.default`, remain memory-only, and expire from the cache before their provider expiry.

Replies are posted with the token to the persisted reference's validated service URL at `/v3/conversations/{conversationId}/activities/{activityId}`. The reply Activity swaps the original sender/recipient, retains conversation and locale, and sets `replyToId` to the triggering Activity ID. IDs are path-encoded, request timeouts are bounded, and arbitrary setup URLs never participate in official transport.

## Diagnostics and local verification

Diagnostics have stable categories for app identity, token acquisition, OpenID metadata, JWKS retrieval, tenant mismatch, expired signing keys, unusable signing-key sets, throttling, and unavailable or timed-out Microsoft services. Signing metadata is healthy only when at least one active RSA/RS256 verification key is importable and endorses `msteams`. Retryability is explicit and upstream bodies are not exposed as credential-bearing diagnostics.

Microsoft offers Bot Framework Emulator and Microsoft 365 Agents/Teams development tooling for local bot testing, not a public unauthenticated sandbox. The automated suite therefore uses local RSA keys plus mocked OpenID, JWKS, OAuth, and Connector responses, including Emulator-shaped Activity fixtures, without contacting Microsoft tenant services.

References: [Bot Connector authentication](https://learn.microsoft.com/en-us/azure/bot-service/rest-api/bot-framework-rest-connector-authentication?view=azure-bot-service-4.0), [send and receive messages](https://learn.microsoft.com/en-us/azure/bot-service/rest-api/bot-framework-rest-connector-send-and-receive-messages?view=azure-bot-service-4.0), [Activity protocol](https://learn.microsoft.com/en-us/microsoft-365/agents-sdk/activity-protocol), and [local bot testing](https://learn.microsoft.com/en-us/microsoftteams/platform/bots/how-to/debug/locally-with-an-ide).
The baseline profile has no live test and does not implement `official_api`. Registry construction never contacts Microsoft services.
23 changes: 14 additions & 9 deletions docs-web/architecture/external-chat-providers.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ The bridge-mode type includes `managed_bridge`, `webhook`, `native_bridge`, and
- Microsoft Teams: managed bridge or bot webhook.
- Discord: bot/webhook gateway.

Public records expose redacted credential metadata only. Runtime code that needs secrets must call the explicit internal repository read path.
Public records expose redacted credential metadata only. Runtime code that needs secrets resolves an ephemeral connection profile through `ChatProviderSecretService`; repository reads never decrypt connector credentials.

Profiles declare setup, authentication and handshake behavior, normalization, external identity, outbound mapping and parsing, verification, session requirements, official references, live-test availability, and lifecycle metadata. The registry itself is side-effect free; network and process execution stay in shared runtime services. See the [Chat Connector Registry](./chat-connectors/index.md) and its provider pages.

Expand Down Expand Up @@ -52,9 +52,12 @@ SQLite tables are created for fresh databases and during startup migrations for

| Table | Purpose |
| --- | --- |
| `chat_provider_connections` | Provider kind, bridge mode, status, enabled flag, setup JSON, and secret JSON. |
| `chat_provider_connections` | Provider kind, bridge mode, status, enabled flag, setup JSON, sanitized verification results, and connector secret version. The nullable `secret_json` column is retained only as a legacy migration source. |
| `chat_provider_connection_secrets` | AES-256-GCM envelope fields, root-key id/version, and non-secret configured-field metadata. |
| `chat_provider_channel_bindings` | Links external channels to projects with routing hints, optional project-manager agent preset, inbound/outbound flags, and `suppress_rich_widgets` defaulting to true. |
| `chat_provider_message_deliveries` | Inbound idempotency keys and outbound delivery status, attempts, errors, linked conversation message IDs, and payload snapshots. |
| `chat_provider_message_deliveries` | Inbound idempotency keys plus outbound status, attempts, explicit retry schedule, compare-and-set lease ownership, linked conversation IDs, and payload snapshots. |
| `chat_provider_ingress_replay_receipts` | Expiring authenticated-ingress replay receipts, unique per connection and replay key. |
| `chat_provider_sessions` | Resumable provider-native session state with connection/binding ownership and compare-and-set versions. |

Bindings allow many projects to point at the same external channel and one project to use multiple channels. Provider deletion cascades bindings and delivery rows. Existing MCP connection and conversation tables remain unchanged.

Expand All @@ -64,11 +67,13 @@ Bindings allow many projects to point at the same external channel and one proje

- Connection create/update/list/get/delete.
- Redacted public reads and unredacted internal reads.
- Secret-preserving updates when an update omits the `secrets` field.
- Atomic encrypted-envelope create, rotation, and clearing in the same secret-version CAS transaction as connection metadata, plus resumable post-key-readiness sealing of legacy plaintext.
- Verification reset after authentication, transport, enabled/status, or setup changes while display-name-only edits preserve the last result.
- Channel binding create/update/list/get/delete.
- Inbound duplicate lookup by `(providerConnectionId, externalMessageId)`.
- Atomic inbound duplicate insertion by `(providerConnectionId, externalMessageId)` and atomic expiring replay-receipt insertion.
- Compare-and-set provider session updates and expiry cleanup.
- Outbound delivery upsert and state transitions.
- Outbound delivery listing scoped to a provider connection or channel binding for dashboard status views, plus pending/retryable outbound delivery scans for retry workers.
- Outbound delivery listing plus lease claim/complete/release operations with due-time filtering and stale-lease recovery.

Indexes cover provider kind, enabled status, project lookup, provider/channel lookup, inbound dedupe, and pending/retryable outbound delivery scans.

Expand Down Expand Up @@ -98,10 +103,10 @@ Outbound delivery lifecycle:
- `pending`: reply has been persisted and queued for bridge delivery.
- `sending`: an adapter attempt is in progress.
- `delivered`: the bridge accepted the reply; `externalMessageId` is stored when the bridge returns one.
- `retryable_failure`: a retryable bridge failure occurred and the payload contains `delivery.nextAttemptAt`.
- `retryable_failure`: a retryable bridge failure occurred and `next_attempt_at` records the durable schedule (the redacted payload mirrors it for display).
- `failed`: delivery is terminal, such as disabled outbound routing, missing bridge configuration, non-retryable HTTP response, or exhausted attempts.

Retryable HTTP/network/native bridge failures use exponential backoff. The dashboard lifecycle starts the outbound retry loop, and status APIs/MCP reads expose delivery status, attempt count, last error, linked conversation message id, and redacted payload state. Secrets are redacted from logs, payloads, stored errors, dashboard responses, and MCP responses.
Retryable HTTP/network/native bridge failures use exponential backoff. Retry workers acquire bounded delivery leases before network or native command execution; competing workers cannot claim the same row, and expired leases are recoverable after a crash. The dashboard lifecycle starts the outbound retry loop, and status APIs/MCP reads expose delivery status, attempt count, last error, linked conversation message id, and redacted payload state. Secrets are redacted from logs, payloads, stored errors, dashboard responses, and MCP responses.

## Dashboard API

Expand Down Expand Up @@ -134,7 +139,7 @@ Channel binding controls support multiple projects on the same external channel

Provider cards and connection detail views surface enabled state, bridge mode, ingress URL, authentication status, configured channels, bound projects, outbound reply state, pending outbound delivery count, and failed outbound delivery count. Recent failed outbound messages are shown with retryable labels and redacted error text.

The ingress endpoint supports Managed, webhook, and native bridge payloads for WhatsApp, iMessage, Telegram, Slack, Microsoft Teams, and Discord. Managed and native bridges authenticate with bearer tokens from the configured bridge secret. Webhook bridges require a configured signing secret and a valid HMAC signature; they do not accept bearer-only fallback. All ingress requests require a fresh timestamp, and signed requests or requests with explicit nonces are replay-checked before processing.
The ingress endpoint supports Managed, webhook, and native bridge payloads for WhatsApp, iMessage, Telegram, Slack, Microsoft Teams, and Discord. Managed and native bridges authenticate with bearer tokens resolved ephemerally from the encrypted envelope. Webhook bridges require a configured signing secret and a valid HMAC signature; they do not accept bearer-only fallback. All ingress requests require a fresh timestamp, and signed requests or requests with explicit nonces are atomically replay-checked through expiring SQLite receipts before processing.

Inbound messages normalize to provider connection id, provider kind, external channel id/name, external sender id/name, text, external message id, timestamp, and redacted raw metadata. The repository idempotency lookup runs before chat posting; duplicate external messages return the existing delivery record without creating another conversation message.

Expand Down
Loading
Loading