Skip to content
teleproto

Sessions

A Session holds the per-DC auth key plus an entity cache. Without one, you'd re-log-in on every restart. teleproto ships three session implementations — pick based on where the process lives.

What a session stores#

A session is four things glued together:

  • Auth keys, per DC. Telegram's network is split across several data centers and each one needs its own auth key. The session holds whichever ones this client has negotiated.
  • Current DC. The active dcId, serverAddress, and port — used by connect() to dial back in without a round-trip to the config.
  • Entity cache. Users and channels you've seen, keyed by id and username, so client.getEntity("@someone") can resolve locally instead of hitting the network.
  • testServers flag. Whether this session was minted against Telegram's test cluster. Set once at login, locked in afterwards — production keys can't talk to test servers and vice versa.

Each backend reduces this state to its own storage shape via session.save() and rehydrates through session.load(). You rarely call either directly — StringSession hands you a string, StoreSession writes through automatically — but the contract is there if you need a custom backend.

StringSession#

The portable one. State serializes to a single base64 string with a one-character version prefix; drop it in an env var, a secret manager, a row in your database. Best fit for serverless and 12-factor configs where there's no writable disk.

string-session.ts
import { TelegramClient } from "teleproto";
import { StringSession } from "teleproto/sessions";

// First run: empty string means "no saved state, log in from scratch"
const session = new StringSession(process.env.TELEPROTO_SESSION ?? "");

const client = new TelegramClient(session, apiId, apiHash, {
  connectionRetries: 5,
});

await client.start({ /* resolvers... */ });

// Save this somewhere safe (secret manager, env var) and reuse next run
const saved = client.session.save();
console.log("TELEPROTO_SESSION=", saved);

// Next run:
//   new StringSession(process.env.TELEPROTO_SESSION!)
// skips the entire auth flow.

StringSession also reads the 352-character session format from Telethon for one-way migrations, but only the IPv4 variant — IPv6 Telethon strings aren't supported. Once loaded, calling save() re-emits in teleproto's prefixed format.

StoreSession#

Disk-backed via node-localstorage and store2. Pass a name; teleproto creates a directory of the same name in process.cwd() and persists every change as it happens. Best fit for long-running processes on a server you control.

store-session.ts
import { TelegramClient } from "teleproto";
import { StoreSession } from "teleproto/sessions";

// Persists everything to ./my-account/ on disk
const session = new StoreSession("my-account");

const client = new TelegramClient(session, apiId, apiHash, {
  connectionRetries: 5,
});

await client.start({ /* resolvers... */ });
// No explicit save() — StoreSession writes through on every change.

MemorySession#

In-process only. Nothing touches disk; state evaporates the moment the process exits. Best fit for unit tests, short-lived scripts, and CI smoke checks where re-logging in each run is acceptable (or desired).

memory-session.ts
import { TelegramClient } from "teleproto";
import { MemorySession } from "teleproto/sessions";

const session = new MemorySession();
const client = new TelegramClient(session, apiId, apiHash, {
  connectionRetries: 5,
});
// Logs in fresh every test run. Nothing is persisted.

Custom sessions#

Need Redis, Postgres, S3, your own KV? Extend the abstract Session class from teleproto/sessions and implement the storage surface. The teleproto client only ever talks to that surface, so any backing store works.

redis-session.ts
import { Session } from "teleproto/sessions";

export class RedisSession extends Session {
  // Required surface (abstract on Session):
  //   setDC(dcId, serverAddress, port)
  //   get dcId(): number
  //   get serverAddress(): string
  //   get port(): number
  //   get authKey(): AuthKey | undefined
  //   setAuthKey(authKey, dcId?)
  //   getAuthKey(dcId?): AuthKey
  //   getInputEntity(key): any
  //   processEntities(tlo): void
  //   load(): Promise<void>
  //   save(): unknown
  //   close(): void
  //   delete(): Promise<void>
  //
  // Plus the testServers flag.

  async load() { /* read from redis */ }
  save()       { /* serialize and write to redis */ }
  // ...rest of the surface
}

const client = new TelegramClient(new RedisSession(), apiId, apiHash, {
  connectionRetries: 5,
});

Use one of the built-in classes (StringSession, StoreSession, MemorySession) as a reference implementation when you're filling in the abstract methods.

Security#

A saved StringSession (or the contents of a StoreSession directory) is a long-lived auth token. Anyone holding it can log into the account from anywhere in the world without a password and without an SMS code. Treat it exactly like a password: secret manager or environment variable, never committed to git, never in logs, never in error reports you share with strangers.

If you suspect a session has leaked, revoke immediately:

revoke.ts
import { Api } from "teleproto";

// Nuclear option: revoke every active authorization except this one
await client.invoke(new Api.auth.ResetAuthorizations());

Or do it from any logged-in Telegram client: Settings > Devices > Terminate all other sessions. Then re-run client.start(...) on a fresh session to mint a new auth key.