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 Rust agent core powers Android and desktop through a loopback REST/SSE API and a Wear OS companion through the authenticated phone bridge. Kotlin owns UI state, rendering and platform pickers; the Rust core owns storage, chat turns, tools, models and cloud synchronization.
Key highlights:
- 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 (Android/Desktop native core)
- Clone the repo
git clone https://github.com/originalFactor/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 are minified (R8 + resource shrinking) and ship the
./gradlew :androidApp:assembleDebug # arm64-v8a APK ./gradlew :wear:assembleDebug # Wear OS companion (armeabi-v7a) ./gradlew :desktopApp:run # Desktop (JVM)
arm64-v8aAPK; the 32-bitarmeabi-v7aand emulatorx86_64ABIs are opt-in with-Pmessenger.abis=armeabi-v7a(orANDROID_ABIS=鈥). Debug builds shipx86_64only (phone apps and watch app), so Android Studio Run installs on an AVD with no flags and never pays for the release ABI's Rust cross-compile; release staysarm64-v8a(phone) /armeabi-v7a(wear). Place your keystore atkeyring/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); the release build runs ProGuard. Android/Wear/Runtime and Desktop 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., since native packages need a numeric, increasing product version). For a one-off local build both can be overridden with theVERSION_NAME/VERSION_CODE` environment variables. - 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 originalFactor/Messenger git remote -v # confirm the changes
- Install Messenger on your phone, tablet, desktop, or Wear OS watch
- 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 and desktop.
Messenger speaks the OpenAI-compatible Chat Completions API, so any provider that exposes that interface works out of the box.
Android, Desktop, and the terminal client start the Rust core on an ephemeral loopback port. Every
non-health request carries a per-start bearer token, and all business calls use
the versioned /api/v1 HTTP API; chat turns and core change notifications use
SSE. The OpenAPI contract lives at
core/rust/crates/messenger-http/openapi.yaml and the SSE contract at
core/rust/crates/messenger-http/docs/SSE.md.
Markdown and LaTeX on Android/Desktop are rendered in Kotlin with
llm-typewriter. The Rust
core owns agent execution, storage, tools, models and synchronization; it does
not contain a Rust document renderer.
The Python TUI uses Textual for the command palette, composer, and conversation view. It connects to the Rust Core API for model configuration, history, and streaming chat.
From the repository root, with Python 3.11+, Poetry 2, and a current stable Rust toolchain:
poetry -C tui install
poetry -C tui run python workflow.py dev
poetry -C tui run python workflow.py test
poetry -C tui run python workflow.py build
poetry -C tui run python workflow.py releaseThe workflow reuses Cargo's regular build cache. Pass application options
after --, for example poetry -C tui run python workflow.py dev -- --db ./test-chat.db.
The example stores the test profile relative to tui/.
The release command bundles the optimized Core and Python dependencies into
a host archive under tui/dist/. Pass --out PATH to choose another directory.
Extract the archive and run ./messenger on Linux/macOS or .\messenger.cmd
on Windows. The extracted client requires Python 3.11+. Run the launcher
with --version to check the packaged Core version.
Type / in the composer to open command suggestions and keep typing to filter
by prefix. Use the arrow keys to select a suggestion, Tab to fill it, or Enter
to run the highlighted command. Esc closes suggestions and keeps the draft.
Use /providers to search and manage providers. Add provider saves the
provider and automatically fetches and imports available models. For an
existing provider, open Manage models to fetch again, add a model manually,
edit it, enable or disable it, or delete it. Discovery imports new models and
refreshes metadata using the provider's extended /models response first and
pi-catalog for missing fields. Saved overrides survive refresh. The model editor
has Fetch metadata and supports display name, input modalities, reasoning
efforts, context window, max output tokens, and support for temperature, top_p,
and penalties. If fetching fails, you can still add models by ID.
Deleting a provider also deletes its models. Use /connect to add a provider
and choose a model in one flow. Enter sends a message,
Ctrl+O inserts a newline, and Esc cancels the current turn. Ctrl+P opens the
command palette. Use /new, /sessions, /models, and /agents to
start a session, resume one, change the model, or select an agent for a new
session. Use Page Up and Page Down to read older messages.
/quit and /exit both close the terminal client.
Messenger opens the closest local project containing your current directory.
If the directory is outside a project, choose Trust and create project to
create a project owned by this device, or Continue without a project to
start a standalone chat. New conversations belong to the active project, and
/sessions shows only the active project's conversations in project mode.
Use --workspace PATH to open a different directory.
In standalone mode, use /project to create a project or reopen one by its
folder. Inside a project, use /project to edit its name or workspace folder,
or select Leave project to start a standalone chat. The saved project and
its conversations remain available when you reopen it.
Use /cloud to sign in to Messenger Cloud, view the saved account, or sign out.
The dialog shows the configured Cloud base URL. Enter an HTTP(S) URL for a
self-hosted server and choose Save address, or sign in with that address.
Saving a different address signs out the current account first.
Signing in with a Cloud AI key automatically adds Messenger Cloud AI and
syncs its models from that server. Use /models to select a Cloud model, or
open the built-in entry in /providers to sync the catalog, view its read-only
Cloud metadata, or enable and disable individual models. Signing out removes the
Cloud provider and its models.
New standalone chats started while signed in upload to Messenger Cloud after
each turn. Run /sync to upload older local chats or retry an upload. Core
links uploaded chats to their Cloud server and account. TUI conversation sync
uploads local history and reports detected newer Cloud edits as conflicts.
The TUI stores chats, provider credentials, and its Cloud session in ~/.messenger/tui.db.
Use --db PATH for a separate TUI profile and --core PATH for a custom
Core executable. Keep the TUI database separate from a running Desktop
instance. The TUI renders plain text and supports chat.
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.
originalFactor - Project Link: https://github.com/originalFactor/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
- llm-typewriter 鈥?streaming Markdown, code and LaTeX rendering on Android and Desktop
- Axum 鈥?versioned loopback HTTP/SSE API