Skip to main content

Channel System Overview

The Channel system is Elftia's multi-platform message ingestion layer, responsible for routing messages from external platforms (Discord, Telegram, Slack, etc.) into the Agent processing pipeline and routing replies back to the original platform.

Architecture Overview

graph TB
subgraph External Platforms
D[Discord]
T[Telegram]
S[Slack]
O[Other Platforms...]
end

subgraph Channel Plugin Layer
PL[ChannelPluginLoader<br/>Plugin Discovery & Loading]
PR[ChannelPluginRegistry<br/>Instance Management & Event Dispatch]
end

subgraph Message Pipeline
MR[ChannelMessageRouter<br/>Security Pipeline + Trigger Matching]
end

subgraph Security Pipeline
RL[RateLimiter]
IS[InputSanitizer]
PG[PromptGuardian]
UP[UserPermissionService]
CG[ChannelPermissionGate]
end

subgraph Agent Layer
MB[ChannelMagiBridge<br/>Message Format Conversion]
MS[MagiService<br/>Agent Processing]
end

D & T & S & O --> PR
PL --> PR
PR -->|message event| MR
MR --> RL --> IS --> PG --> UP --> CG
MR -->|routeToAgent event| MB
MB --> MS
MS -->|response| MB
MB -->|sendResponse| MR
MR -->|sendMessage| PR
PR --> D & T & S & O

Key File Index

FilePathResponsibility
ChannelPluginLoaderdesktop/app/main/services/capabilities/integrations/channel/ChannelPluginLoader.tsDiscover, validate, load plugins (dual directory)
ChannelPluginRegistrydesktop/app/main/services/capabilities/integrations/channel/ChannelPluginRegistry.tsManage plugin factory and Channel instance lifecycle
ChannelMessageRouterdesktop/app/main/services/capabilities/integrations/channel/ChannelMessageRouter.tsSecurity pipeline, trigger matching, message buffering, response splitting
ChannelMagiBridgedesktop/app/main/services/agent-core/magi/ChannelMagiBridge.tsChannel message → MagiIncomingMessage conversion
ChannelMarketplaceServicedesktop/app/main/services/capabilities/integrations/channel/ChannelMarketplaceService.tsCDN manifest retrieval, plugin download and installation
ChannelPluginRouterdesktop/app/main/services/routers/ChannelPluginRouter.tsIPC routing (frontend ↔ backend)
channel-sdkpackages/channel-sdk/src/Plugin development SDK (types, interfaces)
channel-typesdesktop/app/shared/contracts/channel-types.tsUnified Channel type definitions
channel-sdk (re-export)desktop/app/shared/contracts/channel-sdk.tsSDK type re-exports + utility functions inlined
security-typesdesktop/app/shared/contracts/security-types.tsUser role and permission type definitions
RateLimiterdesktop/app/main/services/platform/security/RateLimiter.tsSliding window rate limiting
InputSanitizerdesktop/app/main/services/platform/security/InputSanitizer.tsInvisible character removal
PromptGuardiandesktop/app/main/services/platform/security/PromptGuardian.tsAI-driven prompt injection detection
UserPermissionServicedesktop/app/main/services/platform/security/UserPermissionService.tsRole-based access control
ChannelPermissionGatedesktop/app/main/services/platform/security/ChannelPermissionGate.tsSensitive operation confirmation mechanism

Plugin Lifecycle

stateDiagram-v2
[*] --> Discovered: ChannelPluginLoader.discover()
Discovered --> Registered: ChannelPluginRegistry.registerPlugin()
Registered --> InstanceCreated: createInstance(id, config)
InstanceCreated --> Connecting: connectInstance(id)
Connecting --> Connected: plugin.connect() success
Connecting --> Error: plugin.connect() failure
Connected --> Disconnected: disconnectInstance(id)
Error --> Connecting: Retry connection
Disconnected --> Connecting: Reconnect
InstanceCreated --> Removed: removeInstance(id)
Disconnected --> Removed: removeInstance(id)
Connected --> Removed: removeInstance(id)
Removed --> [*]

Startup Flow

  1. ChannelPluginLoader scans two directories (bundled + marketplace) and discovers all elftia-channel.json manifests
  2. For each discovered plugin, call loader.load() to load the module and get the factory function
  3. Call registry.registerPlugin(manifest, factory, dir) to register with Registry
  4. Load saved Channel instance configurations from database
  5. Call registry.createInstance(id, config) for each instance
  6. Call registry.connectAll() for instances with enabled && autoConnect
  7. ChannelMagiBridge.start() begins listening to routeToAgent events

Connection Management

  • connectInstance(id) — Call plugin.connect(credentials, options), state transition: disconnected → connecting → connected | error
  • disconnectInstance(id) — Call plugin.disconnect(), state transition: connected → disconnected
  • connectAll() — Concurrently connect all enabled instances using Promise.allSettled for fault tolerance
  • disconnectAll() — Concurrently disconnect all connected instances

IPC Channels

All frontend operations are implemented through ChannelPluginRouter's IPC channels:

IPC ChannelDirectionDescription
channelPlugin:listInstancesrenderer → mainGet list of all Channel instances
channelPlugin:createInstancerenderer → mainCreate new instance (with encrypted credential storage)
channelPlugin:updateInstancerenderer → mainUpdate instance configuration
channelPlugin:deleteInstancerenderer → mainDelete instance and related storage
channelPlugin:connectrenderer → mainConnect specified instance
channelPlugin:disconnectrenderer → mainDisconnect specified instance
channelPlugin:testConnectionrenderer → mainVerify credential validity
channelPlugin:getMarketplacerenderer → mainGet Marketplace plugin list
channelPlugin:downloadPluginrenderer → mainDownload and install plugin from CDN
channelPlugin:removePluginrenderer → mainUninstall a plugin
channelPlugin:updateTriggerrenderer → mainUpdate trigger rules
channelPlugin:statusChangemain → rendererInstance status change notification
channelPlugin:downloadProgressmain → rendererPlugin download progress notification

Data Persistence

Channel Instances

Instance configurations are stored in the magi_channels table, managed through ChannelPluginRouter's channelsUpsert / channelsUpdate / channelsDelete operations.

Plugin K-V Storage

Each plugin instance owns independent key-value storage (ChannelStorageDb interface) for platform-specific state data. The underlying implementation is SQLite tables with in-memory caching layer.

Message Activity Logs

Inbound and outbound messages are logged to the file system in JSONL format:

{channelDataDir}/{channelId}/conversations/{chatDir}/{YYYY-MM-DD}.jsonl

Writing uses a buffering mechanism (flushed every 2 seconds) to reduce disk I/O.

Next Steps