Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Session Backends

ferogram ships five session backends out of the box. They all implement the SessionBackend trait and are hot-swappable: switch by changing one line.


Built-in backends

BackendFeature flagBest for
BinaryFileBackend(default, no flag)Single-process bots, local scripts
InMemoryBackend(default, no flag)Tests, ephemeral tasks
StringSessionBackend(default, no flag)Serverless, env-var storage, CI bots
SqliteBackendsqlite-sessionMulti-session local apps
LibSqlBackendlibsql-sessionDistributed / Turso-backed storage

BinaryFileBackend (default)

Saves the session as a binary file on disk. No feature flag needed.

#![allow(unused)]
fn main() {
use ferogram::Client;

let (client, _shutdown) = Client::builder()
    .api_id(12345)
    .api_hash("your_api_hash")
    .session("my.session")  // BinaryFileBackend at this path
    .connect()
    .await?;
}

Or construct directly:

#![allow(unused)]
fn main() {
use ferogram::session_backend::BinaryFileBackend;
use std::sync::Arc;

let backend = Arc::new(BinaryFileBackend::new("bot.session"));
}

InMemoryBackend

Non-persistent: lost on process exit. Ideal for tests.

#![allow(unused)]
fn main() {
let (client, _shutdown) = Client::builder()
    .api_id(12345)
    .api_hash("your_api_hash")
    .in_memory()
    .connect()
    .await?;
}

Or construct directly:

#![allow(unused)]
fn main() {
use ferogram::session_backend::InMemoryBackend;
use std::sync::Arc;

let backend = Arc::new(InMemoryBackend::new());
}

StringSessionBackend: portable auth

Encodes the entire session (auth key + DC + peer cache) as a single base64 string. Store it in an environment variable, a database column, or a secret manager.

Export

#![allow(unused)]
fn main() {
let session_string = client.export_session_string().await?;
println!("{session_string}"); // store this somewhere safe
}

Restore

#![allow(unused)]
fn main() {
// Via builder
let session = std::env::var("TG_SESSION").unwrap_or_default();

let (client, _shutdown) = Client::builder()
    .api_id(12345)
    .api_hash("your_api_hash")
    .session_string(session)
    .connect()
    .await?;
}
#![allow(unused)]
fn main() {
// Via Config shorthand
let (client, _shutdown) = Client::connect(Config::with_string_session(session_string))
    .await?;
}
#![allow(unused)]
fn main() {
// Construct backend directly
use ferogram::session_backend::StringSessionBackend;
use std::sync::Arc;

let backend = Arc::new(StringSessionBackend::new(session_string));
}

Pass an empty string to start a fresh session with no stored data.


SqliteBackend: local database

Requires feature flag:

ferogram = { version = "0.1.1", features = ["sqlite-session"] }
#![allow(unused)]
fn main() {
use ferogram::SqliteBackend;
use std::sync::Arc;

let (client, _shutdown) = Client::builder()
    .api_id(12345)
    .api_hash("your_api_hash")
    .session_backend(Arc::new(SqliteBackend::new("sessions.db")))
    .connect()
    .await?;
}

The file is created if it doesn’t exist.


LibSqlBackend: libsql / Turso

Requires feature flag:

ferogram = { version = "0.1.1", features = ["libsql-session"] }
#![allow(unused)]
fn main() {
use ferogram::LibSqlBackend;
use std::sync::Arc;

// Local embedded database
let backend = LibSqlBackend::new("local.db");

// Remote Turso database
let backend = LibSqlBackend::remote(
    "libsql://your-db.turso.io",
    "your-turso-auth-token",
);

let (client, _shutdown) = Client::builder()
    .api_id(12345)
    .api_hash("your_api_hash")
    .session_backend(Arc::new(backend))
    .connect()
    .await?;
}

Custom backend

Implement SessionBackend to use any storage: Redis, Postgres, S3, or anything else:

#![allow(unused)]
fn main() {
use ferogram::session_backend::{SessionBackend, PersistedSession, DcEntry, UpdateStateChange};
use std::io;

struct MyBackend {
    // your fields (e.g. a DB pool)
}

impl SessionBackend for MyBackend {
    fn save(&self, session: &PersistedSession) -> io::Result<()> {
        // Serialize and store session
        let bytes = serde_json::to_vec(session)
            .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
        // e.g. write to Redis / Postgres
        Ok(())
    }

    fn load(&self) -> io::Result<Option<PersistedSession>> {
        // Load and deserialize
        Ok(None)
    }

    fn delete(&self) -> io::Result<()> {
        Ok(())
    }

    fn name(&self) -> &str {
        "my-custom-backend"
    }

    // Optional: override granular methods for better performance
    // Default impls call load() → mutate → save()

    fn update_dc(&self, entry: &DcEntry) -> io::Result<()> {
        // UPDATE single DC row (e.g. SQL UPDATE)
        todo!()
    }

    fn set_home_dc(&self, dc_id: i32) -> io::Result<()> {
        // UPDATE home_dc column only
        todo!()
    }
}

// Use it
let (client, _shutdown) = Client::builder()
    .api_id(12345)
    .api_hash("your_api_hash")
    .session_backend(Arc::new(MyBackend { /* ... */ }))
    .connect()
    .await?;
}

Granular SessionBackend methods

High-performance backends can override these to avoid full load/save round-trips:

MethodWhen calledDefault behaviour
save(session)Any state changeRequired
load()On connect, and by default implsRequired
delete()Session wipeRequired
name()Logging/debugRequired
update_dc(entry)After DH handshake on a new DCload → mutate → save
set_home_dc(dc_id)After a MIGRATE redirectload → mutate → save
apply_update_state(change)After update-sequence changeload → mutate → save

Using ClientBuilder to attach a backend

#![allow(unused)]
fn main() {
use std::sync::Arc;
use ferogram::{Client, session_backend::SessionBackend};

async fn connect_with_backend(
    backend: impl SessionBackend + 'static,
    api_id: i32,
    api_hash: &str,
) -> anyhow::Result<()> {
    let (client, _shutdown) = Client::builder()
        .api_id(api_id)
        .api_hash(api_hash)
        .session_backend(Arc::new(backend))
        .connect()
        .await?;

    // ...
    Ok(())
}
}

Additional backend methods

StringSessionBackend::current()

Reads the current serialised session string at any time:

#![allow(unused)]
fn main() {
use ferogram::session_backend::StringSessionBackend;

let backend = StringSessionBackend::new("");
// ... after connecting and authenticating ...
let s = backend.current(); // base64 session string
}

BinaryFileBackend::path()

#![allow(unused)]
fn main() {
use ferogram::session_backend::BinaryFileBackend;

let backend = BinaryFileBackend::new("bot.session");
println!("Saving to: {}", backend.path().display());
}

InMemoryBackend::snapshot()

Take a point-in-time snapshot of the in-memory session (useful in tests):

#![allow(unused)]
fn main() {
use ferogram::session_backend::InMemoryBackend;

let backend = InMemoryBackend::new();
// ... after auth ...
if let Some(session) = backend.snapshot() {
    // inspect or serialize session data
}
}