cachedParticipants returned a participant page with Hash=0 whenever the channel_base / channel_participants rows in read_model_versions were never seeded for a channel (e.g. groups created via messages.createChat). With Hash=0 the RPC layer can never answer channels.channelParticipantsNotModified, so a client that polls the member list re-fetches it in a tight loop forever. Fall back to a deterministic content hash derived from the page itself (channel id, page key, count, and each member's id/role/status/rank) so an unchanged member list yields an identical non-zero Hash and the client converges. The read-model-backed path is unchanged. |
||
|---|---|---|
| .claude | ||
| .github | ||
| cmd | ||
| data | ||
| deploy | ||
| docs | ||
| internal | ||
| media/readme | ||
| scripts | ||
| tui-panel | ||
| .containerignore | ||
| .dockerignore | ||
| .env.example | ||
| .gitignore | ||
| Containerfile | ||
| Dockerfile | ||
| go.mod | ||
| go.sum | ||
| LICENSE | ||
| owpengram-server.bat | ||
| owpengram-server.sh | ||
| README.md | ||
| sqlc.yaml | ||
🚀 OwpenGram Server
Your own private messaging server — self-hosted, protocol-compatible, fully yours.
The protocol stack is built on the published
github.com/iamxvbaba/td module
(v1.3.2), using a canonical Layer 229 schema with sparse tlprofile
exact Layer 225-229 compatibility profiles.
If you are looking for a Telegram server, MTProto server, Telegram backend, Telegram clone server, or self-hosted Telegram-like chat server, this repository is the server-side implementation to study, run, and improve.
OwpenGram Server is an open-source, Telegram-compatible MTProto backend written in Go. Run it on your own network for a private, closed setup, or on a VPS to be reachable anywhere in the world. Your data, your keys, your rules — no cloud, no lock-in, no censorship.
🔗 Implements MTProto API layers 225-229 — a client is admitted on the exact layer it announces, so older builds keep working after the server moves forward. The running server reports its version, the layers it accepts, and its build in the admin panel sidebar.
OwpenGram Server is independent and unofficial. It is not affiliated with, endorsed by,
or sponsored by Telegram or the official Telegram team.
✨ Why OwpenGram?
- 🔒 Private & self-hosted — messages live on infrastructure you control.
- 🧩 Telegram-compatible — works with the OwpenGram Android and Desktop clients.
- 🌍 Reachable anywhere — host it globally, or keep it on your own network.
- 🛡️ Censorship-resistant — no central authority can shut you down.
- ⚙️ Single binary — one Go program prepares keys, runs migrations, serves MTProto, and dispatches updates and background workers.
- 📦 One command to install — the launcher installs the prerequisites it needs (Go, Python, Docker, OpenSSL), brings the stack up, and hands you a browser setup wizard.
- 🆓 Free & open source — Apache-2.0, audit and extend it freely.
🎯 What works today
- 💬 Private chats, groups, supergroups & channels
- 📞 Voice & group calls, live streams, SFU/TURN building blocks
- 🖼️ Media & files — photos, videos, documents, stickers, reactions
- 🪣 Media storage on local disk or an S3/MinIO-compatible object store
- 🤖 Bots and mini apps, with a minimal Bot API gateway
- 🏷️ Fragment-style NFT/collectible usernames and bot verification marks
- 📢 Admin-panel broadcasts — announce to every user, or a picked list
- 🔑 Self-hosted "Log in with Telegram" (OpenID Connect) and passkey sign-in
- 🌐 Message translation and AI-assisted compose
- 📇 Contacts, dialogs sync, chat folders, public link landing pages
- 🔎 Self-configuring clients — "Add Server" needs only
host:port; the server publishes its DC id, RSA key and identity over a well-known HTTP path - 👥 Multi-operator admin panel — named operator accounts with scoped permissions, instead of one shared password
- 🗄️ Storage management — usage breakdown, retention rules, and guarded purge of orphaned or expired media
- 👋 Welcome messages and login-code templates you can edit from the panel
- 🧙 First-run web setup wizard — server identity, public address, Bot API and your operator account, then a restart, all from the browser
- 🖥️ Admin API and web UI for operations, plus a TUI server panel to run it all
📋 Full feature checklist (click to expand)
| Status | Feature | What works today |
|---|---|---|
| ✅ | MTProto server edge | TCP transport, RSA key exchange, auth keys, encrypted sessions, salts, ack/resend, bad messages, RPC dispatch, canonical Layer 229, sparse exact Layer 225-229 compatibility profiles, and a same-port HTTP side that serves WebSocket transport plus the /owpengram/server-info and /owpengram/server-icon self-configuration endpoints. |
| ✅ | Login and accounts | Development login code, configurable external code delivery (SMS webhook or SMTP), login email as a second factor, email-as-identity sign-up (no phone number needed), sign-in, sign-up, log-out, authorizations, account settings, SRP/password state, WebAuthn passkey sign-in, and a self-hosted Telegram Login (OpenID Connect) provider for third-party sites. |
| ✅ | Users and contacts | User profiles, usernames, profile photos, contact import/search, blocked/privacy state, presence, and last-seen style status. |
| ✅ | Dialogs and sync | Dialog list, pinned dialogs, manual unread, folders/filters, drafts, read boundaries, durable updates, online fan-out, and offline difference recovery. |
| ✅ | Chatlists and public links | Chat folder sharing, exported chatlist invite links, join/import flows, revoked invite handling, public username landing pages, and shared public link landing pages. |
| ✅ | Private chats | Send, history, read receipts, edit, delete, forward, reply, rich entities, grouped/media messages, reactions, scheduled/TTL-oriented paths. |
| ✅ | Rich messages | Telegram Desktop rich text messages, rich content conversion, send/edit/scheduled flows, dialog/history projections, and memory/PostgreSQL persistence. |
| ✅ | AI compose and ChatBot | Input-box rewrite/polish, default and custom tones, addstyle previews, local and external provider chains, streamed @ChatBot draft replies, and Business AI reply hooks. |
| ✅ | Message translation | Telegram messages.translateText, provider-backed batch translation, peer language settings, per-account rate limits, and privacy-conscious logging defaults. |
| ✅ | Supergroups and channels | Create, join, leave, invite links, participants, admins, forum topics, linked discussion guests, history, send/edit/delete/read, reactions, public search, and previews. |
| ✅ | Media and files | Upload, download, local-disk or S3/MinIO-compatible object storage (switchable per deployment, existing files stay reachable after a switch), low-space upload guard, automatic stale-media cleanup, photos, documents, thumbnails, canonical GIFv conversion, external media fetch, web page previews, map tile cache hooks, profile/channel photos. |
| ✅ | Stickers, reactions, and GIFs | Sticker/reaction catalog, seed support, recent/top/default reactions, moderation-oriented reaction paths, and an admin-curated @gif inline catalog (categorized, auto-save-to-Saved-GIFs on send, category-icon filtering in the client's GIF picker) served alongside the client's Saved GIFs. |
| ✅ | Collectible usernames and verification | Fragment-style NFT/collectible usernames (mint, transfer, activate/deactivate), the official platform-checkmark flow (@verifybot), and a third-party bot-verification mark mechanism (@marksbot, icon + description before a name) — the latter is experimental and hidden by default. |
| ✅ | Bots and mini apps | Bot service foundations, callbacks, inline helpers, webview/mini-app paths, a minimal Bot API gateway for libraries such as python-telegram-bot, persistent getUpdates delivery, and demo tools. |
| ✅ | Calls and live streams | Private call signaling foundations, group call state, RTMP live streaming, scheduled video chats, channel join_as, SFU/TURN building blocks, liveness, and expiry workers. |
| ✅ | Admin and operations | Admin API/UI backend, a first-run web setup wizard (server identity, public network fields, optional Bot API gateway, first operator account), named operator accounts with per-section permissions and a wildcard "full access" grant, per-account freeze (admin-set read-only restriction, advertised to the client via appConfig), broadcast messaging (announce from the official account to every user or a picked list), editable welcome and login-code message templates, storage management (usage breakdown, retention rules, guarded purge), shared-device detection across accounts, RBAC-scoped admin API tokens, PostgreSQL migrations, Redis volatile state, retention workers, pprof/debug hooks, load-test helpers, one-click update with a dry run before it applies, and a bundled TUI server panel as an alternative to the web UI. |
| ✅ | Desktop, Android, iOS, and Web focus | Telegram Desktop is the primary target, with Android, iOS, and Web compatibility paths actively covered by the same server. |
Some items are compatibility-first or experimental, but they are real open server code, not hidden product-only features.
🚀 Want to see it first?
You do not have to run a server to try OwpenGram. We keep a public server live, and it ships inside both clients as a ready-made entry — install a client, pick OwpenGram on the server-selection screen, sign in. Nothing to configure.
Come back here when you want that server to be yours.
⚡ Quick Start
1. Clone the repository
git clone https://github.com/owpengram/owpengram-server.git
cd owpengram-server
2. Run the launcher
./owpengram-server.sh # Linux
.\owpengram-server.bat # Windows
The launcher checks what the server needs — Go 1.25+, Python 3, Docker,
OpenSSL — and installs whatever is missing instead of handing you a
shopping list: scripts/install-prereqs.sh on Arch and Ubuntu/Debian (asks for
root once, then works unattended) or scripts/install-prereqs.ps1 on Windows
via winget. Run either directly with --dry-run to see what it would install
without touching anything.
Docker on Windows is the one thing the script will not install for you: its containers are Linux images, so the daemon needs Docker Desktop's WSL2 backend. The launcher reports it with a link instead of starting it.
3. Answer the first-run form
With the prerequisites in place the launcher opens the server panel. On a fresh
clone it shows a short form instead of the menu — only the values that need a
human decision, with .env.example defaults for everything else; the admin API
token and session key are generated for you. Confirm it and the panel writes
.env, starts PostgreSQL/Redis, builds both binaries, runs them, and shows the
admin panel address and password ready to copy.
4. Finish setup in the browser
Open that address. On a fresh install the panel opens a web setup wizard that walks through the server name, description and icon, the public address clients will connect to, the optional Bot API gateway, and your own operator account — then restarts the server so it all takes effect. Nothing has to be hand-edited to get going.
🔧 Prefer to do it manually? (click to expand)
Requirements: Go 1.25+, Docker (or Docker Desktop) for PostgreSQL and Redis, and OpenSSL.
Start the infrastructure (PostgreSQL + Redis)
docker compose -f deploy/docker-compose.yml up -d
Build and run the server
Windows (PowerShell):
go build -o bin/gramsrv.exe ./cmd/telesrv
.\bin\gramsrv.exe
Linux / macOS:
go build -o bin/gramsrv ./cmd/telesrv
./bin/gramsrv
On first start, the server creates data/server_rsa.pem, applies database
migrations, seeds bundled language packs, prepares optional media resources,
starts MTProto on 0.0.0.0:2398, and brings up the update/media/background
workers in the same process.
Default local login code:
12345— change it before any real use!
Without the web wizard you also have to fill in .env yourself; see
"⚙️ Configuration" below and .env.example.
🖥️ Server Panel
The launcher from step 2 is also a cross-platform interactive TUI: once the
prerequisites are in place it drops into a menu that wraps everything above —
Docker naming migration, docker compose up, go build, and launching both
owpengram-server and owpengram-admin-panel — so you don't re-run commands
from scratch every time.
What it does:
- 🧙 First-run setup — the short
.envform from step 3. It is the only thing the panel offers on a fresh clone; once.envexists the menu below replaces it, and later changes go through the.enveditor. - ▶️ Start / Stop / Restart — launches
owpengram-serverandowpengram-admin-panelas detached background processes; closing the panel does not stop them, only "Stop" does. Reopening the panel later picks the same processes back up and reports live status. - ⬆️ Update —
git pull --ff-only, rebuilds both binaries, restarts them, and re-execs the panel itself so it also picks up any change to its own code — one menu action instead of a manual pull/build/restart sequence. The web panel has the same action, with a dry run that reports what an update would do before anything is applied. - 📜 Live log viewer — tail either binary's log, or both in a split view.
- ⚙️
.enveditor — edit configuration from inside the panel, grouped by feature, without hand-editing the file.
🏷️ Version and build
The admin panel's sidebar footer identifies exactly what is running:
Version: O7
API layers: 225-229
Build: 7ad68c3
- Version — the OwpenGram server release line (
O7). - API layers — every MTProto TL schema layer this binary can talk, read straight from the compatibility profiles rather than hardcoded.
- Build — the short commit the binary was built from, stamped
automatically by Go's VCS info (no special build flags needed); a trailing
+means it was built from a working tree with uncommitted changes. Hover it for the full hash.
Quote the Version / Build pair in bug reports — it pins the exact code,
which a release tag alone does not.
👥 Operators and permissions
The panel is no longer one shared password. Its Operators page creates
named accounts, each with its own login and an explicit set of permissions,
granted per section and per level — for example accounts.read
vs accounts.manage, storage.read vs storage.manage, broadcasts.send,
moderation.review, server.manage, admins.manage. An operator only sees
the sections they hold a permission for; reaching anything else lands on a
clear 403 screen instead of an empty page.
A Full access checkbox at the top of the permission grid grants the *
wildcard — everything, including permissions added by future releases. It is
deliberately separate from the grid (which is disabled while it is on), so
"this person is a full admin" and "this person may do these six things" never
get confused.
The one thing the panel will not let you do is lock yourself out: removing
admins.manage from the last enabled operator who holds it — by editing,
disabling, or deleting them — is refused, whether the grant is explicit or via
the wildcard. There is always someone left who can manage operators.
Your first operator is created by the web setup wizard. Before it exists, the
panel lets you in with a password it generates for that one purpose — and stops
accepting that generated password the moment the wizard finishes. A password you
set yourself (in Server Settings, or TELESRV_ADMIN_UI_PASSWORD in .env)
keeps working as a full-access break-glass login alongside the operator
accounts, so leave it unset or treat it like a root password.
⚙️ Configuration
.env.example is the complete configuration reference — every
variable is documented there with an explanatory comment, grouped by feature.
Copy it to .env and edit, or set the variables directly as environment
variables. Most commonly used variables:
| Variable | Default | Meaning |
|---|---|---|
TELESRV_LISTEN |
0.0.0.0:2398 |
MTProto listen address |
TELESRV_ADVERTISE_IP |
127.0.0.1 |
client-reachable fallback IP for media and calls |
TELESRV_DC |
2 |
self-hosted DC id |
TELESRV_DEV_AUTH_CODE |
12345 |
fixed login code for local development |
TELESRV_AUTH_CODE_MAX_ATTEMPTS |
5 |
wrong-code attempts before the code hash is deleted |
TELESRV_POSTGRES_DSN |
local Compose DSN | PostgreSQL connection string |
TELESRV_REDIS_ADDR |
127.0.0.1:6399 |
Redis address |
TELESRV_BLOB_DIR |
data/blobs |
local media blob directory |
TELESRV_PUBLIC_LINK_WEB_ADDR |
empty | optional public link landing listener, for example 127.0.0.1:2401 |
TELESRV_BOT_API_ADDR |
empty | optional HTTP Bot API gateway listen address, for example 127.0.0.1:8081 |
TELESRV_AI_ENABLED |
true |
enable AI compose entry points |
TELESRV_TRANSLATION_ENABLED |
true |
enable Telegram message translation RPCs |
Optional OpenAI-compatible, Kimi/Moonshot, Gemini, and Anthropic AI provider
variables, login email/SMTP settings, and Business AI settings are documented
in .env.example.
✉️ Login codes, email login, and email sign-up
By default the server just uses a fixed development code
(TELESRV_DEV_AUTH_CODE, 12345) for everything and writes it as a durable
777000 message on the account. Three optional, independent features build on
top of that:
1. External code delivery providers — send the real login/phone-change code out via SMS webhook or SMTP instead of only the fixed dev code. An existing account's durable 777000 message is always written first; the provider is an additional delivery channel, never a replacement.
| Variable | Default | Meaning |
|---|---|---|
TELESRV_PHONE_CODE_DELIVERY_PROVIDER |
development |
development keeps the fixed code; webhook generates a random code and posts it to your own SMS webhook (protocol in TELESRV_OTP_WEBHOOK_URL/_SECRET/_TIMEOUT) |
TELESRV_EMAIL_CODE_DELIVERY_PROVIDER |
smtp |
smtp sends via TELESRV_SMTP_*; webhook reuses the same SMS webhook above |
2. Login email — an extra verification factor on top of the phone number (not a replacement for it), similar to Telegram's own login-email feature.
| Variable | Default | Meaning |
|---|---|---|
TELESRV_LOGIN_EMAIL_ENABLE |
false |
turn the feature on |
TELESRV_LOGIN_EMAIL_REQUIRE_SETUP |
false |
force accounts without a login email to set one during the phone login flow |
TELESRV_LOGIN_EMAIL_CODE_LENGTH |
6 |
length of the emailed code |
TELESRV_SMTP_HOST / _PORT / _USERNAME / _PASSWORD / _FROM / _FROM_NAME / _TLS / _TIMEOUT |
— | outbound SMTP settings, required when the email provider is smtp |
3. Email-as-identity sign-up — lets patched clients register and log in
with just an email address, no phone number at all. The client encodes the
email into a synthetic 888-prefixed number and drives the ordinary
sendCode/signUp/signIn/changePhone flow unchanged; the server decodes
that number back to the email and delivers the code over the same channel as
login email above (TELESRV_EMAIL_CODE_DELIVERY_PROVIDER). The account's
real, visible phone number is a random-looking short number from
TELESRV_EMAIL_SIGNUP_PHONE_PREFIXES — cosmetic only, never used to route the
code — so admins can make freshly signed-up accounts look locally flavored
(e.g. 888,380,373) without any client update.
| Variable | Default | Meaning |
|---|---|---|
TELESRV_EMAIL_SIGNUP_ENABLE |
false |
turn the feature on (requires the email delivery channel above to be configured) |
TELESRV_EMAIL_SIGNUP_PHONE_PREFIXES |
888 |
comma-separated prefixes for the account's cosmetic display number |
🪣 Media storage: local disk or S3/MinIO
Uploaded media (photos, documents, stickers) can live on this machine's disk,
or in an S3-compatible object store. deploy/docker-compose.yml bundles a
self-hosted MinIO container, pre-wired to the defaults below, so s3 (the
default) works out of the box with no extra setup — point the same variables
at AWS S3 instead if you'd rather not self-host it.
| Variable | Default | Meaning |
|---|---|---|
TELESRV_BLOB_BACKEND |
s3 |
s3 for MinIO/AWS S3, localfs to write to TELESRV_BLOB_DIR on disk instead |
TELESRV_S3_ENDPOINT |
127.0.0.1:9000 |
S3 API endpoint (MinIO's default) |
TELESRV_S3_REGION |
us-east-1 |
S3 region |
TELESRV_S3_BUCKET |
owpengram-media |
bucket name |
TELESRV_S3_ACCESS_KEY_ID / TELESRV_S3_SECRET_ACCESS_KEY |
owpengram / owpengram123 |
credentials — also what seeds MinIO's root user in docker-compose.yml; change both before any real use |
TELESRV_S3_USE_SSL |
false |
true for AWS S3 or a MinIO behind TLS; local MinIO runs plain HTTP |
TELESRV_S3_PATH_STYLE |
true |
required for MinIO (bucket in the URL path); leave false for AWS S3 |
Switching backends only affects new uploads — existing files stay wherever
they were written and remain reachable as long as that backend's settings
stay filled in. MinIO's own web console is reachable at
http://localhost:9001 (docker compose -f deploy/docker-compose.yml up -d
also starts it) using the same access key/secret as above.
Related toggles (defaults in .env.example's Advanced section): a low-space
guard that rejects new uploads once storage nears full, and automatic
cleanup of media no longer referenced by any message.
Storage management in the panel. The admin panel's Storage page puts the
rest of this behind a UI: a usage breakdown per media type, an upload size cap
(TELESRV_STORAGE_MAX_UPLOAD_FILE_BYTES, validated against the protocol's own
upload ceiling), retention rules that can expire media globally or per type
(TELESRV_STORAGE_RETENTION_MODE and the TELESRV_STORAGE_RETENTION_MAX_AGE*
family), and a "danger zone" for manual purges by media category and age.
Destructive panel actions — purge, update, restart — all go through the same three-step flow: type a reason, run a dry run that reports exactly what would happen, then confirm. Nothing irreversible fires on a single click.
🔌 Ports to open
When deploying on a public server, open the following according to the features you enable.
Minimal (chat only)
| Port | Protocol | Purpose | Required |
|---|---|---|---|
| 2398 | TCP | MTProto main port; also handles WebSocket when TELESRV_WEBSOCKET_ENABLE=true |
Yes |
With admin backend
| Port | Protocol | Purpose | Notes |
|---|---|---|---|
| 2399 | TCP | Admin REST API | Restrict to trusted IPs or put behind VPN |
| 2600 | TCP | Admin Web UI | Use Nginx/reverse proxy + HTTPS in production |
Optional features
| Port | Protocol | Purpose | When needed |
|---|---|---|---|
| 2400 | TCP | RTMP live stream ingest | Live streaming |
| 12399 | UDP | SFU/WebRTC conferencing | Voice/video group calls |
| 12400 | UDP | TURN/STUN server | P2P/call relay |
| 12500-12999 | UDP | TURN relay port range | TURN relay |
| configurable | TCP | Bot API | When TELESRV_BOT_API_ADDR is set |
| 2401 example | TCP | Public username/sticker/chatlist landing pages | When TELESRV_PUBLIC_LINK_WEB_ADDR=127.0.0.1:2401 is set |
Internal/debug (do not expose publicly)
| Port | Default bind | Purpose |
|---|---|---|
| 6060 | 127.0.0.1:6060 |
pprof debugging endpoint |
| 5432 | 127.0.0.1:5432 |
PostgreSQL |
| 6399 | 127.0.0.1:6399 |
Redis |
| 9000 | 127.0.0.1:9000 |
MinIO S3 API (only when TELESRV_BLOB_BACKEND=s3 and self-hosting MinIO) |
| 9001 | 127.0.0.1:9001 |
MinIO web console |
Make sure TELESRV_LISTEN=0.0.0.0:2398 is set, and TELESRV_ADVERTISE_IP
points to your public IP so clients can connect.
🌐 Public link landing pages
The server can serve public landing pages for /<username>, profile avatars,
/addstickers/<shortName>, /addemoji/<shortName>, and /addlist/<slug>.
TELESRV_PUBLIC_LINK_WEB_ADDR=127.0.0.1:2401
TELESRV_PUBLIC_BASE_URL=https://your-domain.example
TELESRV_PUBLIC_APP_SCHEME=yourapp
TELESRV_PUBLIC_WEB_BASE_URL=https://web.your-domain.example
TELESRV_PUBLIC_APP_NAME=YourApp
In production, keep TELESRV_PUBLIC_LINK_WEB_ADDR on loopback and reverse-proxy
the public routes to it with HTTPS.
📱 Connect a client
Use the OwpenGram clients, which have a built-in Add Server option on the server-selection screen at login — no source patching or custom build needed. They also ship with our public server already in that list, so a client is useful the moment it is installed, with or without a server of your own. Both are forks of the official apps, kept on the same TL layer as the server and rebased on the upstream release that introduced it:
| Client | Upstream base | Upstream commit | TL layer |
|---|---|---|---|
| 💻 Desktop | Telegram Desktop v7.2.2 |
7b4481b6941212bb9dbf08e533adea97947b0f44 |
229 |
| 🤖 Android | Telegram for Android v12.10.1 |
62b56a07ca7e30e39f7fd00a6728d6bbd716ca1c |
229 |
A stock Telegram client will not connect, since it only trusts Telegram's own
DC list and RSA keys. The server's canonical layer is 229, with exact
compatibility profiles for layers 225-229 — so an older fork build keeps
working after the server moves forward. Locally that server is
127.0.0.1:2398, DC id 2.
All you need is host:port. On the login screen open server selection →
Add Server and type the address (e.g. chat.example.com:2398, or
192.168.1.50:2398). The client fetches /owpengram/server-info from the same
port and fills in the rest by itself — RSA public key, DC id, and the server's
name, description and icon as you set them in the setup wizard. No
openssl, no PEM copy-paste.
The RSA key and DC id are still there under Advanced if you want to check or override them. To get the key by hand — for an air-gapped machine, or to verify what the client fetched — export it from the server's private key:
openssl rsa -in data/server_rsa.pem -RSAPublicKey_out -out data/server_rsa.pub
or just read the JSON the client reads:
curl http://your-server:2398/owpengram/server-info
Self-configuration rides the same-port HTTP side that also serves the WebSocket transport, so it needs
TELESRV_WEBSOCKET_ENABLE=true(the default) and opens no extra port. With it off, fill in Advanced manually.
🧪 Development: multi-device smoke test
Use separate client working directories so sessions do not share local tdata:
$tdesktop = "C:\path\to\tdesktop\out\Debug\Telegram.exe"
Start-Process $tdesktop -ArgumentList @("-workdir", "$PWD\.tdata-alice")
Start-Process $tdesktop -ArgumentList @("-workdir", "$PWD\.tdata-bob")
Log in with different phone numbers — the local login code is 12345 unless
you changed TELESRV_DEV_AUTH_CODE. Recommended checks:
- Send private messages, stickers, media, replies, forwards, edits, deletes, and read receipts between two users.
- Keep one device online and restart another device to verify offline
updates.getDifferencerecovery. - Open the same account from multiple sessions and confirm current-session echoes are not duplicated while other online sessions receive updates.
- Check server logs for no new
NOT_IMPLEMENTED,Unhandled RPC,bad_msg, panic, or internal errors.
📂 Repository layout
owpengram-server.sh/.bat one-command launcher (installs prerequisites, then the panel)
scripts/install-prereqs.* unattended prerequisite installers (Arch/Ubuntu, Windows)
cmd/telesrv/ server entrypoint
cmd/telesrv-admin/ admin backend and embedded React web UI (incl. the setup wizard)
cmd/telesrv-update/ one-click update helper used by the panels
tui-panel/ interactive TUI server panel (setup, start/stop, update, logs, .env editor)
deploy/ docker-compose (incl. MinIO), migrations, deploy helpers
data/ bundled language packs and optional seed data
internal/mtprotoedge/ MTProto transport, auth key, session, ack/resend, server-info endpoints
internal/rpc/ TL router and client compatibility handlers
internal/app/ domain services
internal/domain/ protocol-independent domain models
internal/store/ memory/postgres/redis storage backends
internal/identity/ admin-editable server name, description, and icon
internal/botapi/ minimal HTTP Bot API gateway
internal/seed/ bundled seed catalog loaders
internal/sfu/ real-time SFU experiments
internal/turnsrv/ TURN/STUN building blocks
🤝 Contributing
This server gets better fastest with real usage and focused fixes:
- Telegram Desktop and Android compatibility reports with reproducible steps.
- RPC traces for startup, sync, chat, media, calls, bots, or edge cases.
- Focused fixes for implemented paths instead of broad rewrites.
- Tests for online/offline updates, multi-device sessions, read state, media, and channel behavior.
- Performance work on hot paths such as fan-out, pagination, storage queries, media upload/download, and connection handling.
If a change affects visible client behavior, please include the client
version/commit, the RPC path you tested, and whether server logs stayed free
of new NOT_IMPLEMENTED, Unhandled RPC, bad_msg, panic, or internal errors.
💬 Community
- 📢 Channel: @owpengram
- 💬 Chat: Join the discussion
OwpenGram Server builds on the open-source gramsrv project.
📄 License
⭐ If OwpenGram is useful to you, a star on GitHub helps the project grow.