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
| File | Path | Responsibility |
|---|---|---|
| ChannelPluginLoader | desktop/app/main/services/capabilities/integrations/channel/ChannelPluginLoader.ts | Discover, validate, load plugins (dual directory) |
| ChannelPluginRegistry | desktop/app/main/services/capabilities/integrations/channel/ChannelPluginRegistry.ts | Manage plugin factory and Channel instance lifecycle |
| ChannelMessageRouter | desktop/app/main/services/capabilities/integrations/channel/ChannelMessageRouter.ts | Security pipeline, trigger matching, message buffering, response splitting |
| ChannelMagiBridge | desktop/app/main/services/agent-core/magi/ChannelMagiBridge.ts | Channel message → MagiIncomingMessage conversion |
| ChannelMarketplaceService | desktop/app/main/services/capabilities/integrations/channel/ChannelMarketplaceService.ts | CDN manifest retrieval, plugin download and installation |
| ChannelPluginRouter | desktop/app/main/services/routers/ChannelPluginRouter.ts | IPC routing (frontend ↔ backend) |
| channel-sdk | packages/channel-sdk/src/ | Plugin development SDK (types, interfaces) |
| channel-types | desktop/app/shared/contracts/channel-types.ts | Unified Channel type definitions |
| channel-sdk (re-export) | desktop/app/shared/contracts/channel-sdk.ts | SDK type re-exports + utility functions inlined |
| security-types | desktop/app/shared/contracts/security-types.ts | User role and permission type definitions |
| RateLimiter | desktop/app/main/services/platform/security/RateLimiter.ts | Sliding window rate limiting |
| InputSanitizer | desktop/app/main/services/platform/security/InputSanitizer.ts | Invisible character removal |
| PromptGuardian | desktop/app/main/services/platform/security/PromptGuardian.ts | AI-driven prompt injection detection |
| UserPermissionService | desktop/app/main/services/platform/security/UserPermissionService.ts | Role-based access control |
| ChannelPermissionGate | desktop/app/main/services/platform/security/ChannelPermissionGate.ts | Sensitive 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
ChannelPluginLoaderscans two directories (bundled + marketplace) and discovers allelftia-channel.jsonmanifests- For each discovered plugin, call
loader.load()to load the module and get the factory function - Call
registry.registerPlugin(manifest, factory, dir)to register with Registry - Load saved Channel instance configurations from database
- Call
registry.createInstance(id, config)for each instance - Call
registry.connectAll()for instances withenabled && autoConnect ChannelMagiBridge.start()begins listening torouteToAgentevents
Connection Management
connectInstance(id)— Callplugin.connect(credentials, options), state transition:disconnected → connecting → connected | errordisconnectInstance(id)— Callplugin.disconnect(), state transition:connected → disconnectedconnectAll()— Concurrently connect allenabledinstances usingPromise.allSettledfor fault tolerancedisconnectAll()— Concurrently disconnect all connected instances
IPC Channels
All frontend operations are implemented through ChannelPluginRouter's IPC channels:
| IPC Channel | Direction | Description |
|---|---|---|
channelPlugin:listInstances | renderer → main | Get list of all Channel instances |
channelPlugin:createInstance | renderer → main | Create new instance (with encrypted credential storage) |
channelPlugin:updateInstance | renderer → main | Update instance configuration |
channelPlugin:deleteInstance | renderer → main | Delete instance and related storage |
channelPlugin:connect | renderer → main | Connect specified instance |
channelPlugin:disconnect | renderer → main | Disconnect specified instance |
channelPlugin:testConnection | renderer → main | Verify credential validity |
channelPlugin:getMarketplace | renderer → main | Get Marketplace plugin list |
channelPlugin:downloadPlugin | renderer → main | Download and install plugin from CDN |
channelPlugin:removePlugin | renderer → main | Uninstall a plugin |
channelPlugin:updateTrigger | renderer → main | Update trigger rules |
channelPlugin:statusChange | main → renderer | Instance status change notification |
channelPlugin:downloadProgress | main → renderer | Plugin 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
- Channel Plugin SDK — Plugin interface definitions and manifest format
- Message Routing and Security Pipeline — Message processing pipeline details
- ChannelMagiBridge — Message bridging from Channel to Agent
- Writing a Channel Plugin — Write a custom plugin from scratch