# Monogram: a Telegram client that implements MTProto in Rust

> Monogram is an Android Telegram client written in Kotlin and Jetpack Compose, with the MTProto protocol implemented in Rust through UniFFI bindings. It is a 0.x project that states outright that features and architecture are still moving, and building it needs two sibling checkouts and a full native toolchain.

**monogram-android/monogram** — Native Telegram client for Android

- Repository: https://github.com/monogram-android/monogram
- Website: https://monogram.maki.su
- Stars: 976 · Forks: 64
- Language: Kotlin
- License: GPL-3.0
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/monogram-android-monogram

## A native Android client with its own protocol implementation

Monogram describes itself as a fully native Telegram client for Android 7.0 and newer, built with Kotlin, Jetpack Compose and Material 3, paired with what the project calls its own MTProto implementation written in Rust. That pairing is the defining decision. Most of the difficulty in a messaging client is not the interface, it is the protocol layer, and Monogram takes that layer on directly rather than delegating it.

The interface side follows current Android practice. Material 3 layouts adapt to phones, tablets and larger displays, and conversations carry photos, videos and animated stickers. The project also sets a scope limit explicitly: Monogram will not include NFT promotions, gifts, or other Telegram features that the project considers outside the scope of a messaging app.

The repository layout reflects a multi module project. The root holds build.gradle.kts, settings.gradle.kts, gradlew and gradlew.bat, local.properties.example, AGENTS.md and a vendor directory, alongside app/, core/, feature/, network/, native/, scripts/ and website/ directories. The topic tags carry the same story: android, compose, decompose, kotlin, mtproto, mvikotlin and rust.

Maintenance is current. The last push was on 2026-10-19, and the tagged releases moved quickly before it, with 0.4.2 on 2026-10-17, 0.4.1 on 2026-10-07 and 0.4.0 on 2026-09-27.

## The Rust native layer and the UniFFI bridge into Kotlin

The native side is not a plugin. Gradle builds the Rust libraries for armeabi-v7a, arm64-v8a and x86_64 as part of a normal build, and it regenerates the UniFFI Kotlin bindings from host debug libraries while doing so. So the Kotlin app talks to Rust through generated bindings, and both halves ship in the APK.

Two native areas are called out by name in the build tasks. mtproto is the protocol implementation, and markup handles the content rendering side. Prebuilt libvpx libraries live under native/vpx/prebuilt and cover video stickers.

The tasks can be invoked directly:

```sh
./gradlew :native:mtproto:buildNativeMtproto :native:markup:buildNativeMarkup
```

That command compiles the native libraries for the three Android ABIs and regenerates the bindings. On Windows the same thing runs with ./gradlew.bat in place of ./gradlew.

There is also an escape hatch. The build accepts -PskipNativeBuild=true, which is documented for Kotlin only work where compatible native libraries and generated bindings already exist. It skips both the native compilation and the binding regeneration, so a stale binding set will silently produce a Kotlin only build against old native code. That flag is a development convenience rather than a supported configuration.

## Two sibling checkouts, because the Cargo manifests want a neighbour

The build instructions are unusual and worth reading before you start, because a single clone will not work. The repository is cloned with submodules, but the README states plainly that the current Cargo manifests require ../telers-mtproto-impl next to the Monogram checkout, and that the submodule under vendor does not replace this sibling checkout. CI uses the same layout.

That means two repositories, cloned from the same parent directory:

```sh
git clone --recurse-submodules https://github.com/monogram-android/monogram.git
git clone --recurse-submodules https://github.com/gdlbo/telers-mtproto-impl.git
cd monogram
git submodule update --init --recursive
```

The native toolchain is pinned too. The requirements list JDK 17, an Android Studio version compatible with the project's Android Gradle Plugin, Android SDK Platform 37 with platform tools, and an Android NDK, with CI using NDK r28c. The NDK is selected either by setting ANDROID_NDK_HOME explicitly or by letting Gradle look under the SDK's ndk directory.

Rust 1.98 or newer is needed, along with Cargo and the host platform's C and C++ build tools, and cargo-ndk for the Android targets:

```sh
rustup target add aarch64-linux-android armv7-linux-androideabi x86_64-linux-android
cargo install cargo-ndk --locked
```

If your interest is reading the protocol implementation rather than shipping an app, that sibling layout is also the clearest sign of how the project is divided: the Rust side lives in its own repository, and Monogram is the Android shell around it.

## local.properties, Telegram credentials and signing keys

Local configuration starts from a checked in template. Copy local.properties.example to local.properties, then set sdk.dir, API_ID and API_HASH. The README gives a separate command for Unix like systems and for Windows PowerShell:

```sh
cp local.properties.example local.properties
```

The API credentials come from my.telegram.org/apps. The important nuance is that builds can be produced without them, but Telegram login requires valid credentials, so an APK without them installs and runs but cannot sign in.

Signing has its own set of keys, set as properties for a release signing key:

```properties
RELEASE_STORE_FILE=keystore/release.jks
RELEASE_STORE_PASSWORD=your_store_password
RELEASE_KEY_ALIAS=your_key_alias
RELEASE_KEY_PASSWORD=your_key_password
```

Without RELEASE_STORE_FILE, release and beta builds fall back to the debug signing key. The -Punsigned=true property disables that fallback when no release key is configured, and the README is explicit that debug signed builds are for development, not for distribution under your production signing identity.

Two handling rules are repeated in the documentation. Keep local.properties, signing keys and service account credentials untracked, and never include them in logs or commits.

## Push notifications through FCM or UnifiedPush

Push support is optional, and the project offers two paths with a meaningful difference in what they demand of you.

The Firebase Cloud Messaging route requires registering a Firebase Android app for org.monogram, the same application ID used for debug, release and beta builds, and placing the configuration at app/google-services.json. Gradle applies the Google Services plugin only when that file exists, so a checkout without it still builds, and the Firebase dependencies stay part of the app either way. The matching FCM credentials then have to be registered against the Telegram API ID at my.telegram.org/apps. The documentation states that FCM testing requires Google Play services, so emulator checks need a Google Play emulator image.

The UnifiedPush route avoids Google services entirely. You install and configure a compatible distributor on the device, and the app includes UnifiedPush registration support.

For a project that publishes in six README languages and positions itself as a privacy conscious client with no NFT or crypto features, having a push path that does not depend on Google Play services is a real design decision rather than an extra. The cost is that UnifiedPush needs a distributor you have to pick and trust yourself.

## Build variants and the difference between release and beta

The Gradle tasks produce three variants and the difference between them is one flag. On Unix like systems the four commands are:

```sh
./gradlew :app:assembleDebug
./gradlew :app:installDebug
./gradlew :app:assembleRelease
./gradlew :app:assembleBeta
```

On Windows the same commands run through ./gradlew.bat. The installDebug task requires a connected device or a running emulator, which is the practical difference between assembling and installing.

The variants are defined as debug for development, release as an optimized build with R8, and beta as a release derived build with R8 disabled for easier debugging. R8 is the code shrinking and optimisation step, so the beta build ships the same feature set as release with the shrinking and obfuscation pass turned off. That is what makes beta builds debuggable: stack traces and class names survive, which matters for a project that expects contributors to attach logs from a device they do not control.

The default branch is develop rather than master, and the repository also carries an AGENTS.md file at the root, which suggests the project expects automated agents to work on it and keeps their instructions in the repository.

## A 0.x licence and a project that names its own rough edges

The licence is GPL-3.0, stated in the README badge and in the LICENSE file at the repository root. For a GPL project the usual copyleft obligations apply to distributions of modified versions, so anyone planning to ship a build under their own identity needs to read the licence text rather than a summary of it.

The more useful signal for an adopter is the release line. The tagged versions are 0.4.0, 0.4.1 and 0.4.2, all within the last month, which is a fast cadence on a project that has not yet left the zero series. The README states the position directly: Monogram is in active development, features and architecture are still evolving, and bugs and incomplete behavior are possible.

That warning is paired with the project's own scope decisions. No NFT promotions, no gifts, and no crypto features, on the stated grounds that they fall outside the scope of a messaging app. The effect is that the client offers a smaller feature surface than a general purpose Telegram app, which is a defensible trade for people who want a plain messenger.

The alternative to all of this is to use an existing Telegram client, where the trade is the opposite one: no Rust toolchain, no sibling checkout, no NDK and no signing keys to manage, at the cost of not owning or reading the protocol layer. Monogram earns its complexity only if the protocol implementation itself is part of what you want.

Project support runs through Boosty, and the README is translated into Russian, Turkish, Korean, Urdu and Spanish alongside English, with the homepage at monogram.maki.su.

## Conclusion

Monogram fits Android developers who want to read or contribute to a Telegram client built entirely on their own protocol stack, and it does not fit users who want a finished messaging app today. Verify first that you can satisfy the Rust 1.98, NDK and cargo-ndk requirements, that the telers-mtproto-impl checkout sits next to the Monogram checkout as the Cargo manifests demand, and that you accept a 0.x release line where the project itself warns that bugs and incomplete behavior are possible.

## FAQ

### What do I need to build Monogram from source?

JDK 17, an Android Studio version matching the project's Android Gradle Plugin, Android SDK Platform 37 with platform tools, an Android NDK with CI using NDK r28c, Rust 1.98 or newer with Cargo, host C and C++ build tools, Git and cargo-ndk. Set ANDROID_NDK_HOME to select an NDK explicitly, otherwise Gradle looks under the SDK's ndk directory.

### Where do I get the Telegram API credentials for Monogram?

They come from my.telegram.org/apps and are set as API_ID and API_HASH in local.properties. Builds can be produced without them, but Telegram login requires valid credentials.

### How do I enable push notifications in Monogram?

Either register a Firebase Android app for org.monogram and place the configuration at app/google-services.json, or install and configure a UnifiedPush distributor on the device. FCM testing requires Google Play services, so emulator checks need a Google Play emulator image.

### What is the difference between the release and beta builds of Monogram?

The release build is optimized with R8, while the beta build is derived from release with R8 disabled for easier debugging. Without RELEASE_STORE_FILE, both fall back to the debug signing key, and -Punsigned=true disables that fallback.

## Sources

- [License: GPL-3.0](https://github.com/monogram-android/monogram/blob/develop/LICENSE)
- [monogram-android/monogram on GitHub](https://github.com/monogram-android/monogram)
- [Project website](https://monogram.maki.su)
- [README](https://github.com/monogram-android/monogram/blob/develop/README.md)
- [Releases](https://github.com/monogram-android/monogram/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/monogram-android-monogram
