Implementation Plan: Connectors (First-Party Communication-Channel Plugins)
Feature ID: connectors
Spec: spec.md
Status: Draft v1
Owner: Product (Ruslan)
Date: 2026-07-18
Additive-only. Every phase adds category strings, capability constants, tables (nullable scope columns), packages, and services. Nothing in the existing
notification-channel/ aggregator / MCP surfaces is renamed or removed. Each entity change ships its migration in the same PR (apps/api/src/migrations/<unix>-*.ts).
This is a continuous program. The durable deliverables are the connector category, the IConnectorPlugin contract, and the routing/pairing/session services. Providers land one at a time against that stable contract.
Phase 1 — Category, contract, facade, Slack outbound
Goal: a first-party bidirectional-capable plugin category exists, and slack-connector can send a message end-to-end from the UI.
- P1.1 Add
'connector'toPLUGIN_CATEGORIES(packages/plugin/src/contracts/plugin-manifest.types.ts). - P1.2 Add
CONNECTOR+CONNECTOR_SLACK/_DISCORD/_WHATSAPP/_NOTION/_MICROSOFT_365toPLUGIN_CAPABILITIES(packages/plugin/src/contracts/facade-capabilities.ts). - P1.3 Define
IConnectorPlugin+ DTOs (ConnectorMetadata,ConnectorCapabilityFlags,ConnectorInboundRequest/Event,ConnectorReply,ConnectorRecordInput/Result,ConnectorPollResult,ConnectorCallOptions,isConnectorPlugin) inpackages/plugin/src/contracts/capabilities/connector.interface.ts. Re-useChannelSendInput/ChannelSendResult/ChannelTargetConfig/ChannelVerificationfromnotification-channel.interface.ts. Export from the package barrel. - P1.4
BaseConnectorPlugin(packages/plugin/src/abstract/base-connector.ts): per-connectorIdNUL-keyed idempotency cache, SSRF-guarded fetch, signature-verify toolkit (HMAC + ed25519, constant-time, timestamp-skew clamp), defaultonLoad/onUnload. - P1.5
connectorsentity (packages/agent/src/entities/connector.entity.ts, Tier A) + repository + register inpackages/agent/src/database/database.config.tsENTITIES(noautoLoadEntities— explicit registration is mandatory, elseEntityMetadataNotFoundError).@EncryptedJsonColumnfortargetConfig. - P1.6 Migration
<unix>-AddConnectorsTable.ts. - P1.7
ConnectorFacadeService(packages/agent/src/facades/connector.facade.ts):send/verifyConnection(outbound only this phase), owner-scoped lookup, secret redaction + length cap,PluginUsageEvent(newPluginUsageCapability.CONNECTOR). Trigger.devconnector-deliverydispatcher interface + in-process fallback (mirror the notification facade's optional dispatcher). - P1.8
PluginUsageCapability.CONNECTORenum value (packages/agent/src/entities/plugin-usage-event.entity.ts). - P1.9 REST:
apps/api/src/connectors/connectors.controller.ts+connectors.service.ts+connectors.module.ts—GET/POST /,PATCH/DELETE /:id,POST /:id/verify,POST /:id/test. DTO whitelist +targetConfigbyte cap +ParseUUIDPipe+@Throttle(copy the notification-channels controller posture). - P1.10
slack-connectorpackage (packages/plugins/slack-connector/):package.json(@slack/web-api),tsup.config.ts,tsconfig.json,vitest.config.ts,src/slack-connector-plugin.ts(outboundchat.postMessage,verifyConnection=auth.test),src/index.tssingleton. - P1.11 Web UI:
apps/web/.../settings/integrations/connectors/— connectors list + Add-Connector wizard (schema-driven form,x-secretpassword inputs, verify step, test-send). New "Connectors" group on/settings/plugins. - P1.12 Bridge:
NotificationChannelFacadeServiceoptional resolver can target aconnectorsrow so connectors appear in theevent-subscriptionsmatrix (no data copy). - P1.13 Tests: Vitest for
slack-connector(send happy-path, idempotency,verifyConnection); Jest forConnectorFacadeService(outbound, owner-scope IDOR, redaction); API e2e for CRUD + test-send.
Phase 2 — Discord, inbound, pairing, chat-everything routing
Goal: an external message routes to an Agent and gets a reply, gated by pairing and isolated per conversation.
- P2.1 Entities (Tier C, no
@ManyToOneon scope FKs) + repositories +ENTITIESregistration:connector_identities,connector_pairing_codes,connector_conversations,connector_message_log. - P2.2 Migration
<unix>-AddConnectorInboundTables.ts(all four tables + indexes/uniques). - P2.3 Extend
IConnectorPluginusage: wireverifyInbound/handleChallenge/parseInbound/replythroughConnectorFacadeService.handleInbound. - P2.4 Inbound webhook route
POST /api/connectors/inbound/:connectorId(@Public,rawBodycapture, resolve-by-id → verify → challenge → parse → 202/401). Mirrorcomposio-triggersfail-closed shape. - P2.5
ConnectorRoutingService(packages/agent/src/connectors/connector-routing.service.ts): inbound dedupe, identity/pairing resolution, session resolve/create (compositeuser:channel:conversationkey +conversationslink), dispatch to the chat-everything engine as the paired user under the routed Agent'sAgentPermissions, reply via the connector. - P2.6 Untrusted-content fencing on inbound text before the engine (reuse
apps/mcpfence-untrusted/sanitizeapproach). - P2.7 Pairing:
POST /:id/pairing-codes(mint, hashed at rest, ≤15-min TTL, single-use, throttled), redeem path insideConnectorRoutingService,GET /:id/identities,DELETE /:id/identities/:identityId,GET /:id/conversations. - P2.8
discord-connectorpackage (packages/plugins/discord-connector/):@discordjs/rest+discord-interactions; outboundRoutes.channelMessages, inbound Interactions (verifyKeyed25519, PING→PONG),reply. - P2.9
slack-connectorinbound: Events APIverifyInbound(v0 HMAC + skew clamp),handleChallenge(url_verification),parseInbound(message/app_mention),reply(threadedchat.postMessage). - P2.10 Web UI: per-connector inbound URL (copyable), default Agent/Team + routing-mode selector, pairing panel (mint code + instructions), identities + conversations tabs.
- P2.11 Tests: signature-verify (valid/invalid/replayed/skewed) per plugin; pairing (mint/redeem/expiry/single-use/revoke); routing (unpaired→prompt, paired→agent reply, session isolation, inbound dedupe); e2e Discord Interactions + Slack Events round-trip.
Phase 3 — WhatsApp / Notion / Microsoft 365 + catalog repo
- P3.1
whatsapp-connector— Cloud API;X-Hub-Signature-256verify; template-only outbound + 24h window; meteredgetPricing. - P3.2
notion-connector—transport: 'poll'; Trigger.dev scheduledpoll()per enabled connector;createRecord(pages / DB rows); Notion verification token. - P3.3
microsoft-365-connector— Microsoft Graph; mail / Teams / files; Graph change-notification subscriptions +clientStateverify + subscription-renewal loop. - P3.4 Discord Gateway
sockettransport for free-form (non-slash) messages — a long-lived worker; poll-vs-socket operational decision (§11.5). - P3.5
ever-works/connectorscatalog repo — connector manifests + icons + setup guides + npm package refs; wire the plugin-catalog service to read it (agent-templates/ADR-011 analog) + EW-693 dynamic distribution (registry install-on-use). - P3.6 Docs: per-provider operator setup guides (create the Slack app, Discord bot, WhatsApp number, Notion integration, M365 app registration).
Phase n — Continuous
Additional providers (Telegram bidirectional, Linear, GitHub, CRM connectors) as increments against the stable contract. Team-based routing, OAuth identity linking, and multi-workspace resolution fold in per demand (spec §11).
Dependencies
- Depends on: plugin registry + loader (shipped);
@EncryptedJsonColumn+ secret resolution (shipped); Trigger.dev job runtime (shipped); notification-channel DTOs + SSRF guards (shipped, reused). - Integrates with:
chat-everythingengine (inbound routing target),event-subscriptions(outbound bridge),tenants-and-organizations(scope columns), Agents (AgentPermissions, routing target). - Independent of: the existing
*-channelnotification plugins and the Composio/Make/SIM/Zapier/Activepieces aggregators — both keep shipping unchanged.
Constitution gates
- I Plugin-first — each connector is its own
packages/plugins/*package. - II Capability-driven — selection via
PLUGIN_CAPABILITIES.CONNECTOR*. - III Database — new tables via TypeORM migrations in the same PR; explicit
ENTITIESregistration. - IV Job runtime — outbound delivery + poll via Trigger.dev; no BullMQ.
- V Forward-only migrations — additive tables, nullable scope columns.
- VI Tests — Vitest per plugin + Jest for facade/routing + e2e for inbound round-trips.
- VII Secret hygiene — bot tokens / signing secrets
x-secret+ encryptedtargetConfig+x-envVar. - VIII Additive —
*-channelplugins, aggregators, MCP untouched; connectors are a superset. - IX Behaviour-first — spec covers observable send/pair/route/reply semantics.
- X Security — mandatory fail-closed inbound verification, pairing gate, session isolation, Agent-permission clamp.