ClientBuilder
ClientBuilder is the fluent, type-safe constructor for a Client connection.
Obtain one via Client::builder().
Just getting started?
Client::quick_connectconnects and authenticates in one call with no options to think about. Come back here when you need proxies, custom transports, low memory mode, or anything else the defaults don’t cover.
use ferogram::Client;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let (client, _shutdown) = Client::builder()
.api_id(12345)
.api_hash("abc123")
.session("my.session")
.catch_up(true)
.connect()
.await?;
Ok(())
}
connect() returns Result<(Client, ShutdownToken), BuilderError>. The
BuilderError can be MissingApiId, MissingApiHash, or a network-level
Connect(InvocationError).
Common patterns
Most people only need a handful of options. Here are the three most common setups:
#![allow(unused)]
fn main() {
use ferogram::Client;
// 99% of users - just fill in credentials and go
let (client, _) = Client::builder()
.api_id(API_ID)
.api_hash(API_HASH)
.session("bot.session")
.connect().await?;
// Termux / tiny VPS - running on constrained hardware
let (client, _) = Client::builder()
.api_id(API_ID)
.api_hash(API_HASH)
.session("bot.session")
.low_memory_mode(true)
.connect().await?;
// Power user - custom update queue to handle high-traffic bots
use ferogram::update_config::OverflowStrategy;
let (client, _) = Client::builder()
.api_id(API_ID)
.api_hash(API_HASH)
.session("bot.session")
.update_queue_capacity(512)
.update_overflow_strategy(OverflowStrategy::DropNewest)
.connect().await?;
}
The full option reference follows below.
Credentials
connect() returns BuilderError::MissingApiId if not set.
connect() returns BuilderError::MissingApiHash if not set.
Session
Three session backends are available. They are mutually exclusive - the last call wins.
path. This is the default backend
("ferogram.session" in the working directory if no session method is called).
.session("mybot.session")
client.export_session_string() can be injected here directly
(e.g. via an environment variable). No file is written to disk.
.session_string(std::env::var("SESSION").unwrap_or_default())
SessionBackend implementation. Use this for
LibSqlBackend (bundled SQLite, no system dependency) or any
custom persistence layer.
#[cfg(feature = "libsql-session")]
use ferogram::LibSqlBackend;
use std::sync::Arc;
.session_backend(Arc::new(LibSqlBackend::new(“my.db”)))
Requires the libsql-session feature for LibSqlBackend.
See Feature Flags.
Updates
sync
.catch_up(enabled: bool) → ClientBuilder
When true, replay missed updates via updates.getDifference immediately after
connecting. Useful for bots or userbots that must not miss messages during downtime.
Default: false.
Network
sync
.dc_addr(addr: impl Into<String>) → ClientBuilder
Override the first DC address. Useful when connecting to a test server.
.dc_addr("149.154.167.40:443") // production DC 1
.dc_addr("149.154.167.40:80") // test DC
sync
.allow_ipv6(allow: bool) → ClientBuilder
Allow IPv6 DC addresses when resolving the DC table. Default: false.
sync
.transport(kind: TransportKind) → ClientBuilder
Choose the MTProto transport framing layer.
Variant Description
TransportKind::AbridgedSmallest overhead (default)
TransportKind::IntermediateCompatible with more proxies
TransportKind::ObfuscatedDeep-packet-inspection resistant
TransportKind::HttpPlain HTTP wrapping
Note: when using .mtproxy() or .proxy_link(), the transport is set automatically from the secret prefix - do not also call .transport().
sync
.probe_transport(enabled: bool) → ClientBuilder
Race Obfuscated, Abridged, and HTTP transports in parallel and connect using
whichever completes the DH handshake first. The losing attempts are cancelled
immediately. Ideal when you don't know which transport your network or firewall
permits. Incompatible with MTProxy. Default: false.
#![allow(unused)]
fn main() {
.probe_transport(true)
}
See Transport Probing & Resilient Connect for the race schedule, timing details, and interaction with MTProxy.
sync
.resilient_connect(enabled: bool) → ClientBuilder
If direct TCP fails, retry via DNS-over-HTTPS (Mozilla + Google DoH), then
fall back to Firebase / Google special-config endpoints. Useful in restricted
networks where Telegram DCs are ISP-blocked. Default: false.
#![allow(unused)]
fn main() {
.resilient_connect(true)
}
See Transport Probing & Resilient Connect for the full fallback chain and when to combine with probe_transport.
sync
.pfs(enabled: bool) → ClientBuilder
Enable Perfect Forward Secrecy. When set, a temporary DH key bind is performed immediately after the permanent auth key is established. Traffic runs under a short-lived session key derived from that bind; the permanent key is never used to encrypt traffic directly. If the bind RPC fails for any reason, the pool falls back to the standard session without interrupting the connection.
Adds one extra DH round-trip per connection. Off by default. Enable only if your threat model requires it.
#![allow(unused)]
fn main() {
let (client, _shutdown) = Client::builder()
.api_id(12345)
.api_hash("your_hash")
.session("bot.session")
.pfs(true)
.connect()
.await?;
}
Proxy
sync
.socks5(addr: impl Into<String>) → ClientBuilder
Route all connections through a SOCKS5 proxy. Pass a "host:port" string:
.socks5("127.0.0.1:1080")
sync
.mtproxy(proxy: MtProxyConfig) → ClientBuilder
Route all connections through an MTProxy. The transport is automatically selected
from the proxy secret prefix; do not also call .transport().
Build with ferogram::parse_proxy_link(url) or construct manually.
sync
.proxy_link(url: &str) → ClientBuilder
Set an MTProxy from a https://t.me/proxy?... or tg://proxy?... link.
An empty string is a no-op. Transport is selected from the secret prefix automatically.
.proxy_link("https://t.me/proxy?server=1.2.3.4&port=443&secret=abc123")
See Proxies & Transports for full details.
Identity (InitConnection)
These strings are sent to Telegram in the InitConnection call and appear in
the active sessions list on my.telegram.org.
sync
.device_model(model: impl Into<String>) → ClientBuilder
Device model shown in sessions. Default: "Linux".
Example: .device_model("Pixel 9 Pro")
sync
.system_version(version: impl Into<String>) → ClientBuilder
OS / system version shown in sessions. Default: "1.0".
Example: .system_version("Android 15")
sync
.app_version(version: impl Into<String>) → ClientBuilder
App version shown in sessions. Default: the crate version from CARGO_PKG_VERSION.
sync
.lang_code(code: impl Into<String>) → ClientBuilder
BCP-47 language code sent in InitConnection. Default: "en".
sync
.system_lang_code(code: impl Into<String>) → ClientBuilder
System language code. Default: "en".
sync
.lang_pack(pack: impl Into<String>) → ClientBuilder
Language pack name. Default: "" (empty). Leave unset unless building a client that mirrors an official Telegram app.
Retry & Restart
sync
.retry_policy(policy: Arc<dyn RetryPolicy>) → ClientBuilder
Override the flood-wait / rate-limit retry strategy.
The default is AutoSleep: automatically sleep for FLOOD_WAIT durations.
See Retry & Flood Wait.
sync
.restart_policy(policy: Arc<dyn ConnectionRestartPolicy>) → ClientBuilder
Override the reconnect behaviour after a connection drop. The default is
NeverRestart: the event loop exits on disconnect and the shutdown
signal fires. Use FixedInterval for automatic reconnection, or
implement the trait for custom backoff logic.
#![allow(unused)]
fn main() {
use std::sync::Arc;
use std::time::Duration;
use ferogram::FixedInterval;
// Reconnect 5 seconds after any drop
.restart_policy(Arc::new(FixedInterval {
interval: Duration::from_secs(5),
}))
}
See Connection Restart Policy for all built-in types, custom implementation, and scheduled periodic restarts.
Experimental Features
sync
.experimental_features(features: ExperimentalFeatures) → ClientBuilder
Opt in to experimental behaviours that deviate from strict Telegram spec.
All flags default to false. Always use ..Default::default()
to stay forward-compatible with new flags.
#![allow(unused)]
fn main() {
use ferogram::{Client, ExperimentalFeatures};
Client::builder()
.api_id(12345)
.api_hash("abc")
.experimental_features(ExperimentalFeatures {
allow_zero_hash: true, // bots only: allow hash=0 on cache miss
..Default::default()
})
.connect()
.await?;
}
See Experimental Features for all flags, safety constraints, and when to use each one.
Terminal Methods
sync
.build() → Result<Config, BuilderError>
Build the Config struct without establishing a network connection. Useful if
you want to pass the Config to Client::connect(config) manually, or to
inspect the built configuration.
async
.connect() → Result<(Client, ShutdownToken), BuilderError>
Build the Config and connect in one step. Returns
Err(BuilderError::MissingApiId) or Err(BuilderError::MissingApiHash)
before attempting any network I/O if required fields are absent.
BuilderError
Variant Meaning
BuilderError::MissingApiId.api_id() was not called (or set to 0)
BuilderError::MissingApiHash.api_hash() was not called (or left empty)
BuilderError::Connect(InvocationError)Network / MTProto connection failed
#![allow(unused)]
fn main() {
match client_result {
Err(BuilderError::MissingApiId) => eprintln!("Set API_ID"),
Err(BuilderError::MissingApiHash) => eprintln!("Set API_HASH"),
Err(BuilderError::Connect(e)) => eprintln!("Network error: {e}"),
Ok((client, _)) => { /* use client */ }
}
}