A beautifully designed, cross-platform AI chat app.
Chat with your favourite AI models on your phone, tablet, desktop, Wear OS watch, browser, or terminal — Material 3 design, Bring Your Own Key, fully open-source and offline-capable.
Table of Contents
Messenger is a Material-3-designed, cross-platform LLM chat application built with Kotlin Multiplatform and Jetpack Compose. It is fully open-source, free, and offline-capable, using a BYOK (Bring Your Own Key) model.
The same agent core powers four surfaces: a Kotlin Multiplatform app for Android and desktop, a Wear OS companion, a browser client compiled to WebAssembly, and a native Rust terminal client. Chat with your favourite AI models wherever you are — with a clean, modern interface that feels great to use. A shared Kotlin Multiplatform core keeps Android and Desktop in lockstep, the Wear OS companion keeps chat simple by syncing your agents from mobile and using your phone as the configured AI backend, and the two Rust targets link the core crates directly.
Key highlights:
- Chat anywhere — Works on your phone, tablet, Wear OS watch, desktop (JVM), and in your browser
- In your browser too — the web client is the same Compose UI compiled to WebAssembly on the Rust core, served from the account server at
/app; sign in and your agents, providers and conversations sync like everywhere else - In your terminal too —
messenger-tuiis a native Rust client that links the same agent core directly (no Kotlin, no bridge): the same agents, providers, MCP servers and cloud account, rendered for an ANSI terminal, with its own store and an optional one-shot import of the Desktop app's data - Bring your own key — Use API keys from your preferred AI providers, no middleman
- Custom AI agents — Create and switch between different AI personas and assistants
- Built-in terminal tool — Agents can run model-requested commands through the consent-gated terminal tool. On Android, commands execute inside an app-private workspace using the pinned runtime packaged for the device ABI; no separate Termux installation is required.
- Multiple providers — Connect to various OpenAI-compatible model providers in one app
- Streaming responses — SSE streaming with progressive Markdown and inline / display LaTeX math rendering (powered by RaTeX)
- Modern design — Clean Material 3 interface that's easy on the eyes, with adaptive layouts (bottom nav on phones, navigation rail + dual-pane on tablet/desktop)
- Open source & free — Fully open-source, no subscriptions, no hidden costs
- Privacy first — Your conversations stay on your device
To get a local copy up and running, follow these simple steps.
- JDK 17
- Android SDK (Compile SDK 37, Target SDK 36, Min SDK 30)
- Gradle wrapper is included in the repository
- Rust toolchain (only for the terminal client and the browser build — the Gradle builds do not need it)
rustup target add wasm32-unknown-unknown # browser build cargo install wasm-bindgen-cli --version 0.2.129 --locked # must match core/rust/Cargo.lock
- Clone the repo
git clone https://github.com/ECSDevs/Messenger.git
- Create a
local.propertiesfile in the project root pointing to your Android SDKsdk.dir=/path/to/android/sdk - Initialize submodules
git submodule update --init --recursive
- Build the debug APK / Desktop app
Android builds download the pinned Termux bootstrap archives during the build and verify their SHA-256 digests before packaging them. Android release builds produce the same three ABI-specific APKs; place your keystore at
./gradlew :androidApp:assembleDebug # ABI-specific arm64-v8a/armeabi-v7a/x86_64 APKs ./gradlew :wear:assembleDebug # Wear OS companion ./gradlew :desktopApp:run # Desktop (JVM)
keyring/messenger-release.jksand provide the environment variablesKEYSTORE_PASSWORD,KEY_ALIAS, andKEY_PASSWORD. Native Desktop distributions are built with./gradlew :desktopApp:packageReleaseDmg(macOS),:desktopApp:packageReleaseMsi(Windows), or:desktopApp:packageReleaseDeb(Linux). Android/Wear/Runtime, Desktop, Web and the terminal client all report the same version: the semantic version name in the repository-rootVERSIONfile plus the git commit count as the version code (the Desktop MSI usesMAJOR.MINOR.<commitCount>, since native packages need a numeric, increasing product version). For a one-off local build both can be overridden with theVERSION_NAME/VERSION_CODEenvironment variables. - Build the browser client and the terminal client (optional)
The browser build compiles the Rust core to WebAssembly and generates the bindings automatically (the pinned
./gradlew :webApp:wasmJsBrowserDevelopmentRun # browser dev server (COOP/COEP on) ./gradlew :webApp:wasmJsBrowserDistribution # production bundle under webApp/build/dist ./gradlew :webApp:copyWebAppDistribution # local: serve it from server/public/app/ cd core/rust && cargo build -p messenger-tui # terminal client
wasm-bindgenCLI from the Prerequisites is what it invokes). The compiled bundle is not committed: CI republishes it to a rollingweb-clientGitHub release, and the account server (server/) downloads and serves it at/appduringpnpm build. To try it against your own server checkout, run./gradlew :webApp:copyWebAppDistributionso the bundle lands inserver/public/app/, then start the server withpnpm dev. - Cutting a release
The push is what triggers the release build. The script refuses to tag when
./release.ps1 -Bump patch # 1.0.0 -> 1.0.1: bumps VERSION, commits it, tags v1.0.1, pushes ./release.ps1 # tag and push the version already committed in VERSION ./release.ps1 -NoPush # create the tag locally, inspect it, push later
VERSIONis uncommitted or differs fromHEAD(a tag names a commit, so the mismatch would publish the wrong version), when the tree is dirty, or when the tag already exists locally or on origin;-WhatIfpreviews, and-AllowDirtytolerates unrelated local edits..github/workflows/release.ymlre-checks the tag againstVERSIONat the tagged commit and fails rather than publishing a mismatch. - (Optional) Change the git remote URL to avoid accidental pushes to the base project
git remote set-url origin ECSDevs/Messenger git remote -v # confirm the changes
- Install Messenger on your phone, tablet, desktop, Wear OS watch, or browser
- Add your API key from your preferred AI provider
- Pick a model and start chatting
- Create custom agents for different tasks
- Enable Tools for an agent when terminal access is needed. On Android, terminal commands execute inside an app-private workspace using the pinned runtime packaged for the device ABI — no separate Termux installation is required. Read-only mode (the default) exposes inspection commands only; switch an agent to writable mode to let it edit and create files in that workspace.
- (Optional) Install the Wear OS companion — it discovers your phone over the local network (NSD mDNS) and syncs your agents automatically over a WebSocket on TCP
18765 - (Optional) Sign in to a Messenger account to sync agents, providers and conversations across every client. Each client seeds its own local store and mirrors the account's data into it, so the same conversation is available on your phone, in your browser and in your terminal.
Messenger speaks the OpenAI-compatible Chat Completions API, so any provider that exposes that interface works out of the box.
The browser client is the same UI as the desktop app, compiled to WebAssembly and backed by the same Rust agent core (its SQLite store is persisted in the browser's origin-private file system). The production bundle is served by the account server at /app alongside the website and console — sign in there and your agents, providers and conversations sync like everywhere else. Because it runs in a browser sandbox it has no shell or filesystem access, so tools are disabled there (remote MCP servers over SSE still work); the terminal and desktop clients are the ones with tools.
messenger-tui is a native Rust terminal client built on the same agent core — it links the Rust crates directly, with no bridge. It keeps its own store at ~/.messenger/tui/store.db, reads settings.toml next to it, and can import an existing Desktop database once:
cd core/rust
cargo run -p messenger-tui # start it
cargo run -p messenger-tui -- --import-desktop # also import the Desktop app's data (one-shot)
cargo run -p messenger-tui -- --help # every flagInside, F1–F6 switch between conversations, chat, agents, providers, MCP servers and settings; ? lists every key binding. In the chat view all printable keys type into the message, so its commands are Ctrl-modified (Ctrl+W toggles read-only/writable tool mode, Esc cancels a running turn). Tools execute natively in your shell and in the workspace directory (~/.messenger/agent-runtime/workspace), and signing in from the Settings view syncs the same cloud account as the other apps. Math renders as its LaTeX source — a terminal cannot stack fractions.
For more details on architecture, conventions, and hard constraints, please refer to AGENTS.md
Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are greatly appreciated.
If you have a suggestion that would make this better, please fork the repo and create a pull request. You can also simply open an issue with the tag "enhancement".
Don't forget to give the project a star! Thanks again!
- Fork the Project
- Create your Feature Branch (
git checkout -b feature/AmazingFeature) - Commit your Changes (
git commit -m 'Add some AmazingFeature') - Push to the Branch (
git push origin feature/AmazingFeature) - Open a Pull Request
Distributed under the Apache License, Version 2.0. See LICENSE for more information.
ECSDevs - Project Link: https://github.com/ECSDevs/Messenger
- RaTeX / RaTeX-CMP — Pure-Rust KaTeX-compatible LaTeX engine shared across Android and JVM Desktop
- Best-README-Template — README template
- Jetpack Compose
- Compose Multiplatform
- Material 3
- Ratatui / Crossterm — terminal rendering and input for the Rust terminal client
- syntect — Sublime Text syntax engine behind code highlighting in all clients
- wasm-bindgen — Rust ↔ JavaScript boundary for the browser client