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

layer-client ships four session backends out of the box. All implement the SessionBackend trait, so you can also plug in your own.

Overview

BackendFeature flagStorageBest for
BinaryFileBackend(default)Local binary fileDevelopment, simple scripts
InMemoryBackend(default)RAM onlyTests, ephemeral bots
SqliteBackendsqlite-sessionLocal SQLite fileProduction bots, long-running servers
LibSqlBackendlibsql-sessionlibsql / Turso (remote or embedded)Serverless, distributed deployments
StringSessionBackend(default)Caller-provided stringEnv vars, DB columns, CI environments

BinaryFileBackend (default)

# No extra feature needed
layer-client = "0.4.5"
#![allow(unused)]
fn main() {
use layer_client::{Client, Config};

let (client, _shutdown) = Client::connect(Config {
    session_path: "my.session".into(),
    api_id:       12345,
    api_hash:     "abc123".into(),
    ..Default::default()
}).await?;
}

The session is stored in a compact binary format at session_path. Created on first login; reloaded automatically on subsequent connect() calls.

Security: treat this file like a password — add *.session to .gitignore and chmod 600.


InMemoryBackend

#![allow(unused)]
fn main() {
use layer_client::{Client, Config};
use layer_client::session_backend::InMemoryBackend;

let (client, _shutdown) = Client::builder()
    .session(InMemoryBackend::new())
    .api_id(12345)
    .api_hash("abc123")
    .connect()
    .await?;
}

Nothing is written to disk. Login is required on every run. Ideal for integration tests and short-lived scripts.


SqliteBackend

layer-client = { version = "0.4.5", features = ["sqlite-session"] }
#![allow(unused)]
fn main() {
use layer_client::{Client, Config};

let (client, _shutdown) = Client::connect(Config {
    session_path: "session.db".into(),  // use a .db extension
    api_id:       12345,
    api_hash:     "abc123".into(),
    ..Default::default()
}).await?;
}

SQLite is more resilient against crash-corruption than the binary format. A good choice for any bot that runs continuously or handles many accounts.


LibSqlBackend — New in 0.4.5

For libsql (the open-source Turso database engine):

layer-client = { version = "0.4.5", features = ["libsql-session"] }
#![allow(unused)]
fn main() {
use layer_client::session_backend::LibSqlBackend;
use layer_client::{Client, Config};

// Local embedded libsql file
let backend = LibSqlBackend::open_local("session.libsql").await?;

// OR: remote Turso cloud database
let backend = LibSqlBackend::open_remote(
    "libsql://your-db.turso.io",
    "your-turso-auth-token",
).await?;

let (client, _shutdown) = Client::builder()
    .session(backend)
    .api_id(12345)
    .api_hash("abc123")
    .connect()
    .await?;
}

LibSqlBackend is a drop-in replacement for SqliteBackend but works with remote databases, making it ideal for serverless or horizontally-scaled deployments.


StringSessionBackend — New in 0.4.5

Encodes the entire session as a portable base64 string. Store it in environment variables, a secrets manager, a database column, or anywhere else you can store a string.

Export an existing session

#![allow(unused)]
fn main() {
// After a successful login, export the session
let session_string = client.export_session_string().await?;
println!("{session_string}");
// → "AQAAAAEDAADtE1lMHBT7...LrKO3y8=" (example)
}

Save this string securely (e.g. in a SESSION environment variable).

Restore from string

#![allow(unused)]
fn main() {
use layer_client::{Client, Config};
use layer_client::session_backend::StringSessionBackend;

let session_str = std::env::var("TG_SESSION")?;

let (client, _shutdown) = Client::builder()
    .session(StringSessionBackend::new(&session_str))
    .api_id(12345)
    .api_hash("abc123")
    .connect()
    .await?;

// If the session is valid, is_authorized() returns true — no re-login needed
assert!(client.is_authorized().await?);
}

Convenience constructor

#![allow(unused)]
fn main() {
// Equivalent to the above — shorthand for StringSessionBackend
let session_str = std::env::var("TG_SESSION")?;
let (client, _shutdown) = Client::with_string_session(
    &session_str,
    12345,         // api_id
    "abc123",      // api_hash
).await?;
}

Typical workflow for CI / serverless

# One-time: generate session on your dev machine
cargo run --bin login_helper
# → prints: TG_SESSION=AQAAAAEDAADtE1lMHBT7...
# Add TG_SESSION to your CI secrets
#![allow(unused)]
fn main() {
// In production / CI
let (client, _shutdown) = Client::with_string_session(
    &std::env::var("TG_SESSION")?,
    std::env::var("TG_API_ID")?.parse()?,
    std::env::var("TG_API_HASH")?,
).await?;
}

Implementing a custom backend

#![allow(unused)]
fn main() {
use layer_client::session_backend::SessionBackend;

pub struct MyRedisBackend {
    key: String,
    // ... your redis client
}

#[async_trait::async_trait]
impl SessionBackend for MyRedisBackend {
    async fn load(&self) -> Option<Vec<u8>> {
        // read bytes from Redis
    }

    async fn save(&self, data: &[u8]) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
        // write bytes to Redis
    }
}
}

Then pass it via ClientBuilder::session().


Comparing session backends

FileSQLiteLibSQLStringMemory
Survives restart✅*
Crash-safe⚠️N/AN/A
Remote storage✅*
Zero disk I/O
Extra depsNonerusqlitelibsqlNoneNone

* — provided the caller re-saves the exported string after each connect.