Aleph
Concepts

Protocol Adapters

The boundary between provider protocols and messaging-channel adapters, including registration and runtime capabilities.

Aleph separates model-provider HTTP protocols from messaging-channel I/O. Protocol adapters build model requests and parse streaming deltas; channel adapters handle inbound events, access prefilters, session routing, and outbound capabilities. Telegram, Discord, and iMessage are not model protocols and do not share the provider protocol registry.

Provider Protocol Layer

Provider configuration names a protocol, which the registry resolves from dynamic YAML or compiled-in implementations. OpenAI-compatible vendors can reuse the openai protocol instead of duplicating an adapter; native Messages, Gemini, OpenAI Responses, Codex/ChatGPT, and Ollama retain their own request and stream-parsing differences.

Dynamic protocols live under ~/.aleph/protocols/. File changes are debounced, parsed, and atomically registered before taking effect. Dynamic names take priority over built-ins; an unknown name returns an error with the available protocols. This layer does not own Telegram/Discord/iMessage pairing, allowlists, conversation IDs, or message limits.

Channel Adapter Layer

Every channel implements the Channel trait in src/gateway/channel.rs and exposes ChannelCapabilities through ChannelInfo. Capability flags are promises: an adapter that declares a feature must implement the corresponding method; otherwise the default returns UnsupportedFeature rather than claiming success.

Configurable channels are registered through ChannelFactory in src/gateway/interfaces/plugin.rs. register_channel_plugins currently registers Telegram, Discord, and other configurable channels. iMessage is the exception: the channel initialization path constructs it directly, so it does not depend on the plugin table.

Telegram Boundary

The current Telegram adapter uses teloxide long polling. It handles messages, attachments, replies, edit-based streaming, status reactions, stickers, and inline callbacks. At the interface edge it only classifies DM/group policy and static allowlists:

  • open, allowlist, pairing, and disabled are DM/group policy outcomes.
  • A non-allowlisted DM under pairing returns NeedsPairing and is forwarded to the inbound router.
  • Pairing state, approval, and the final access decision are single-sourced by the router's check_permission and pairing_store.
  • The channel no longer owns a local pairing-code store, runtime paired-user set, webhook server, session manager, or draft API.

Telegram declares attachments, images, audio, video, reactions, replies, editing, and Markdown/HTML support. Its message limit is 4096 and its stream protocol is edit-based. It is not a webhook adapter.

Discord Boundary

Discord enters through the registered DiscordChannelFactory and handles guild/DM messages, slash commands, component buttons, embeds, attachments, edit-based streaming, replies, and reactions. Regular messages, slash commands, and approval buttons enforce the same DM, guild, and channel allowlists:

  • dm_allowed controls direct messages.
  • allowed_guilds restricts servers.
  • allowed_channels restricts channels inside servers.
  • A component click is converted into an inbound message with a cb_ prefix and forwarded to the router's ApprovalCallbackSink; the Discord adapter stores no approval state.

Discord declares attachments, media, reactions, replies, editing, deletion, typing indicators, and Markdown. Its message limit is 2000 and the normal attachment limit is 25 MB. Bot permission auditing is a separate bitfield check and is not the same as Gateway channel allowlisting.

iMessage Boundary

iMessage is one channel type with two transports:

TransportInboundOutboundPlatform boundary
LocalmacOS chat.db pollingAppleScriptmacOS only; requires Full Disk Access and Automation; cannot send tapbacks, so reactions is false
BlueBubblesREST + webhook, with optional catch-up pollingBlueBubbles RESTAny OS; attachments, replies, and reactions; reactions require private-api; editing and deletion are not declared

Local iMessage polling turns add-tapbacks into inbound context carrying MessageMeta::Reaction and filters remove-tapbacks. Therefore, surfacing a user's new reaction to the model does not mean the local transport can send reactions. BlueBubbles also validates the tapback name and private-api availability.

Addressing and Routing

ChannelId identifies a transport, ConversationId identifies a platform conversation, and ChannelCapabilities describes supported operations. Gateway's channel_directory reads routing metadata while channel_message performs sends; DM/group policy, pairing, and allowlists remain inbound-router constraints that channel adapters cannot bypass.

Code Location

  • src/providers/protocols/ — provider protocols and registry
  • src/gateway/channel.rs — channel trait, addressing, and capabilities
  • src/gateway/interfaces/plugin.rs — channel factory table
  • src/gateway/interfaces/telegram/ — Telegram polling, policy prefilter, and router handoff
  • src/gateway/interfaces/discord/ — Discord messages, components, and access prefilter
  • src/gateway/interfaces/imessage/ — Local and BlueBubbles transports
  • src/gateway/inbound_router/ — unified inbound authorization, pairing, and session routing

See Also

On this page