How the tinychannels Crate Enables OpenHuman's 17 Messaging Channel Integrations
The tinychannels crate provides a unified transport, authentication, and data-model layer that allows OpenHuman to support 17 messaging platforms through thin re-exports rather than custom implementations.
OpenHuman's messaging architecture delegates all platform-specific heavy lifting to the tinychannels crate, a portable Rust library that abstracts Telegram, Discord, Slack, WhatsApp, iMessage, and 12 other services behind a common interface. By treating tinychannels as the single source of truth for channel logic, the tinyhumansai/openhuman repository maintains a lean codebase while offering extensive messenger compatibility. This integration pattern reduces adding new providers to a single line of code.
Architectural Overview of the tinychannels Integration
OpenHuman’s messaging layer lives under src/openhuman/channels/. Rather than reinvent transport logic for each service, OpenHuman delegates WebSocket handling, message serialization, OAuth flows, and platform-specific networking to tinychannels, exposing these capabilities through its own domain APIs and RPC endpoints.
The Channel Trait and Core Abstractions
At the foundation lies the Channel trait, which defines the minimal API every messenger must implement: send, listen, and auth. OpenHuman re-exports these types directly from tinychannels in src/openhuman/channels/traits.rs:
pub use tinychannels::{Channel, ChannelMessage, ChannelSendExt, SendMessage};
This re-export strategy ensures that OpenHuman’s interactive loop always uses the upstream trait definitions, guaranteeing consistency across all 17 integrated services.
Provider Re-exports and Feature Gating
Each of the 17 messaging providers exists as a thin module under src/openhuman/channels/providers/. These modules contain no implementation logic; they simply re-export the corresponding tinychannels provider. For example, src/openhuman/channels/providers/telegram/mod.rs contains:
pub use tinychannels::providers::telegram::{session_store, TelegramChannel};
Providers are conditionally compiled behind the channels feature flag using #[cfg(feature = "channels")] blocks in src/openhuman/channels/mod.rs. Building without a specific provider omits the corresponding re-export, and the channel runtime gracefully skips absent tools.
Runtime Host and Relay Infrastructure
The channel host supplies the runtime context—storage paths, credential stores, and policy enforcement—that providers require. OpenHuman constructs this host using tinychannels::host::ChannelHostBuilder in src/openhuman/channels/host/mod.rs, assembling it once per process and handing it to every provider.
For real-time communication, OpenHuman connects its relay runtime to tinychannels’ transport layer. The file src/openhuman/channels/relay_runtime.rs utilizes tinychannels::relay::RelayTransport and tinychannels::relay::WebSocketRelayConfig to manage inbound and outbound message envelopes.
How OpenHuman Implements the 17 Channel Stack
The integration spans seven architectural layers, each delegating to tinychannels:
- Controllers & RPC: JSON-RPC endpoints under
/rpc/channels/*are thin wrappers forwarding calls to tinychannels’ChannelManager(seesrc/openhuman/channels/controllers/ops/connect.rs). - Configuration Schema: Persistence of tokens and webhook URLs uses tinychannels config types re-exported in
src/openhuman/config/schema/channels.rs. - Security Pairing: OpenHuman’s security model bridges with tinychannels’ session-key handling through
ChannelInboundEnvelopeutilities insrc/openhuman/channels/host/adapters.rs.
All heavy lifting—OAuth flows, platform-specific networking, and message serialization—remains encapsulated within the tinychannels crate.
Adding a New Messaging Provider
Integrating an additional messenger requires no new logic in OpenHuman. The process follows four steps:
- Upstream Implementation: The tinychannels crate adds a new provider (e.g.,
providers::newservice). - Re-export: OpenHuman adds a thin module under
src/openhuman/channels/providers/containingpub use tinychannels::providers::newservice::NewServiceChannel;. - Feature Flagging: The provider is gated behind the
channelsfeature insrc/openhuman/channels/mod.rs. - Automatic Exposure: The
ChannelManagerregistered at startup automatically picks up the new provider through the existing RPC controller code.
Thus, the integration effort is reduced to a single line of re-export while tinychannels guarantees consistent behavior across all supported services.
Working with Channels in Practice
Sending Messages via the Unified API
OpenHuman exposes a uniform interface for sending messages regardless of the underlying platform. The following example demonstrates sending an email using the shared ChannelSendExt trait:
use openhuman::channels::{Channel, EmailChannel, SendMessage};
// Build the OpenHuman channel host (normally done at startup)
let host = openhuman::channels::host::build_channel_host()?;
// Obtain a concrete channel instance
let email: EmailChannel = host
.provider_registry()
.get::<EmailChannel>()
.expect("Email provider not configured");
// Use the unified ChannelSendExt trait to send a message
let result = email
.send_message(
SendMessage {
to: "user@example.com".into(),
body: "Hello from OpenHuman!".into(),
..Default::default()
},
)
.await?;
println!("Message sent, id: {}", result.message_id);
Listening for Inbound Events
Inbound message handling utilizes the host's unified stream, filtering envelopes by channel type:
use openhuman::channels::{Channel, ChannelHost};
use futures::StreamExt;
// Assume a host has been built as above
let host: ChannelHost = openhuman::channels::host::build_channel_host()?;
// Subscribe to inbound events from all configured channels
let mut inbound = host
.inbound_stream()
.await?
.filter_map(|envelope| async move { envelope.ok() });
while let Some(envelope) = inbound.next().await {
println!(
"Incoming {} message from {}",
envelope.channel.kind(),
envelope.sender.id()
);
}
Cargo Feature Configuration
Providers are toggled via Cargo features. The channels feature enables the core runtime, while individual messengers can be selectively included:
[features]
default = ["channels"]
channels = [] # core flag
telegram = ["channels"] # enables TelegramChannel
discord = ["channels"] # enables DiscordChannel
Source Code Reference
Key files illustrating the tinychannels integration include:
src/openhuman/channels/mod.rs– Central module re-exportingChannel,ChannelSendExt, and all provider types.src/openhuman/channels/traits.rs– Re-exports tinychannels traits for the interactive loop.src/openhuman/channels/providers/*– Thin wrappers for each tinychannels provider (Telegram, Discord, Email, etc.).src/openhuman/channels/host/mod.rs– Builds theChannelHostusingChannelHostBuilder.src/openhuman/channels/controllers/*– JSON-RPC adapters forwarding toChannelManager.src/openhuman/channels/relay_runtime.rs– Connects OpenHuman’s relay to tinychannels’ WebSocket transport.src/openhuman/config/schema/channels.rs– Re-exports tinychannels configuration types for persistence.
Summary
- The tinychannels crate abstracts all transport, authentication, and messaging logic for 17 platforms, allowing OpenHuman to avoid platform-specific implementations.
- Architecture: OpenHuman re-exports traits from
src/openhuman/channels/traits.rsand providers fromsrc/openhuman/channels/providers/, delegating runtime management toChannelHostBuilderandChannelManager. - Integration Cost: Adding a new messenger requires only a single-line re-export in
src/openhuman/channels/providers/and a feature flag, as all logic resides in the upstream crate. - Unified API: All channels implement the
Channeltrait andChannelSendExt, enabling generic message sending and inbound stream processing regardless of the underlying service.
Frequently Asked Questions
What is the tinychannels crate and why does OpenHuman use it?
The tinychannels crate is a self-contained Rust library that implements the full messaging stack—including WebSocket handling, OAuth flows, and platform-specific networking—for 17 different messaging services. OpenHuman uses it to avoid maintaining 17 separate integrations, instead relying on tinychannels as the single source of truth for all messaging functionality.
How does OpenHuman add support for a new messaging platform?
Adding support requires only a re-export in src/openhuman/channels/providers/newservice/mod.rs that exposes the tinychannels provider type, plus a feature flag in the crate configuration. The existing ChannelManager and RPC controllers automatically pick up the new provider without additional logic, as all implementation details live in the tinychannels crate.
What role does the ChannelHost play in the messaging architecture?
The ChannelHost, constructed via tinychannels::host::ChannelHostBuilder in src/openhuman/channels/host/mod.rs, supplies the runtime context required by all providers—including storage paths, credential stores, and policy enforcement. It serves as the central registry and execution environment for the 17 integrated messaging channels.
How are messaging channels configured and enabled in OpenHuman?
Channels are configured through re-exported tinychannels types in src/openhuman/config/schema/channels.rs and enabled via Cargo features. The base channels feature activates the runtime, while individual providers (e.g., telegram, discord) are toggled as separate features that simply re-export the corresponding tinychannels modules.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →