This document describes the trust model of Vasya — in particular
vasya-server, the agent-native API gateway that runs a Telegram client on your
behalf. Read it before self-hosting the server or issuing agent (vk_) keys.
vasya-server is a real Telegram (MTProto) client. To talk to Telegram it must
hold your account session keys. Consequently:
- Anyone with operator/root access to the server host can read those sessions and act as the Telegram account.
- Anyone you give a
vk_agent key to can act as that account, limited to the key's scopes and per-account allowlist.
If that trust boundary is unacceptable, self-host the server (the desktop app runs the same engine locally with no remote operator) and keep agent keys narrow and short-lived.
- Sessions encrypted at rest — Telegram sessions are encrypted with
SESSION_MASTER_KEY(an environment variable on servers; the OS keychain on desktop). They persist across restarts but are unreadable without the key. - Per-user isolation — every Telegram account is owned by a user id
(
localin embedded mode, the JWTsubotherwise). Requests can only reach accounts their caller owns; cross-user access returns403. - Scoped, revocable agent keys — a
vk_<id>_<hex>key carries only its granted scopes, an optional per-account allowlist, and an optional TTL. Only a SHA-256 hash of the secret is stored; the secret is shown once at creation; keys can be revoked at any time. - Least privilege for agents — agent keys cannot mint or manage keys,
read the audit log, or act outside their scopes/allowlist. Destructive scopes
(
accounts:delete,chats:delete,messages:forward) are separate and never auto-granted. - Audit log — every mutating call (human or agent) is appended to
data_dir/audit.log, including the agent key id; readable by humans viaGET /api/v1/audit. - Idempotency & rate limiting — mutations honor an
Idempotency-Keyheader (safe retries) and agent mutations are rate-limited per key.
| Mode | Trust |
|---|---|
| EmbeddedLocal | Single shared bearer token, bound to 127.0.0.1 in the desktop app — only local processes reach it. |
| JWT | HS256 user tokens issued by the sync backend; this server only validates them. Protect JWT_SECRET (shared between backend and server). |
Agent key (vk_) |
Scoped delegate of one user. Treat it like a password for the subset of that user's Telegram activity it can perform. |
SESSION_MASTER_KEYandJWT_SECRETare critical — leaking either compromises every account on the host. Store them in your platform's secret manager, never in the repo.- Per-user provider keys (e.g. a Deepgram STT key) are stored server-side and returned only masked by the API — but they are not E2E and a host operator can read them. Use restricted, rotatable provider keys.
- Agent keys: grant the minimum scopes, set an
accountIdsallowlist and attlSecs, and revoke when done.
Please report security issues privately to the maintainer rather than opening a
public issue. Include reproduction steps and the affected component
(vasya-server, vasya-mcp, desktop app, or backend).
For the full API surface and how scopes map to endpoints, see
docs/api/README.md.