Skip to content
/ xmtp Public

Safe, ergonomic Rust client SDK for the XMTP messaging protocol.

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT
Notifications You must be signed in to change notification settings

qntx/xmtp

xmtp

CI License Rust

Safe, ergonomic Rust SDK for the XMTP messaging protocol — E2E encrypted messaging via MLS (RFC 9750), with a batteries-included TUI chat client.

xmtp wraps the official libxmtp FFI layer with idiomatic Rust types, providing a high-level ClientConversationMessage API for DMs, groups, content types, identity management, ENS resolution, and real-time streaming. The CLI crate ships a full-featured terminal chat interface with profile-based persistent configuration.

Crates

Crate Description
xmtp crates.io docs.rs SDK — Client, Conversation, Message, content codecs, ENS, Ledger
xmtp-sys crates.io docs.rs Raw FFI bindings to libxmtp_ffi static library
xmtp-cli crates.io TUI chat client + profile management CLI

Quick Start

Install the CLI

Shell (macOS / Linux):

curl -fsSL https://sh.qntx.fun/xmtp | sh

PowerShell (Windows):

irm https://sh.qntx.fun/xmtp/ps | iex

CLI

# Create a profile (generates a new key, registers with XMTP)
xmtp new alice

# Create a profile with a Ledger hardware wallet
# (connect via USB and open the Ethereum app on the device first)
xmtp new bob --ledger

# Create a profile with an imported private key
xmtp new carol --import 0xdeadbeef...

# Launch the TUI chat interface
xmtp              # uses default profile
xmtp alice        # uses profile "alice"

# Profile management
xmtp list          # list all profiles (* = default)
xmtp info alice    # show profile details + installations
xmtp default alice # set default profile
xmtp remove alice  # delete a profile
xmtp clear         # delete ALL profiles

# Revoke all other installations (requires wallet signature)
xmtp revoke alice

Library

use xmtp::{Client, Env, AlloySigner};

// Create a client and register identity.
let signer = AlloySigner::random()?;
let client = Client::builder()
    .env(Env::Dev)
    .db_path("./alice.db3")
    .build(&signer)?;

// Send a DM.
let conv = client.dm(&"0xBob...".into())?;
conv.send_text("hello from Rust")?;

// List conversations.
let convs = client.list_conversations(&Default::default())?;
for c in &convs {
    println!("{}: {}", c.id()?, c.name().unwrap_or_default());
}

Architecture

  • xmtp — High-level SDK. Owns all unsafe FFI calls behind safe types. Client is built via ClientBuilder with optional signer, ENS resolver, and environment selection. Conversation provides send/receive/sync/metadata/consent operations. Content codecs handle text, markdown, reactions, replies, attachments, and read receipts.
  • xmtp-sys — Auto-generated bindings from xmtp_ffi.h. Downloads pre-built static libraries at build time. No libclang required for end users.
  • xmtp-cli — Profile-based TUI chat client. Profiles persist configuration (environment, signer type, wallet address) in platform data directories. Signer is only required for identity-changing operations (new, revoke); TUI and info operate without it.

Feature Flags

xmtp crate

Feature Description
content Content type codecs (text, reactions, replies, attachments, read receipts) — enabled by default
alloy Local private key signer via alloy-signer-local
ledger Ledger hardware wallet signer via alloy-signer-ledger
ens ENS name resolution via alloy-ens + alloy-provider

ENS display names — To show your .eth name in the TUI header and conversation list, you must set a Primary Name (reverse record) for your wallet address at primary.ens.domains. Without it, the app displays your truncated address instead.

xmtp-sys crate

Feature Description
regenerate Re-generate bindings from xmtp_ffi.h at build time (requires libclang)

XMTP Protocol Overview

Identity Model

XMTP V3 uses MLS (RFC 9750) for E2E encryption. Each user is identified by an Inbox ID, which can own multiple blockchain identities, each with up to 10 installations (devices/apps).

Inbox ID
 ├── Identity (EOA / Smart Contract Wallet / Passkey)
 │    └── Installation 1  ← unique key pair, stored in local DB
 │    └── Installation 2  ← independent key pair, separate DB
 │    └── ...  (up to 10 per identity)
 └── Identity 2
      └── ...

Two-Tier Key Architecture

XMTP separates identity-level operations (rare, high-privilege) from messaging operations (frequent, automated) using two distinct key tiers:

Wallet key (EOA private key / hardware wallet / smart contract wallet) — used exclusively for identity mutations that alter the on-chain association graph:

Operation When Signers Required
Create inbox (register) First launch Wallet
Add identity to inbox Linking a new wallet Existing wallet + new wallet
Remove identity from inbox Unlinking a wallet Wallet
Revoke installations Device lost/compromised Wallet
Change recovery identifier Security rotation Wallet

Installation key (ed25519 key pair, auto-generated, stored in the local encrypted database) — handles everything else with zero user interaction:

  • Sending and receiving messages
  • Creating / joining conversations
  • Syncing conversations, messages, and preferences
  • Streaming real-time updates
  • Managing consent state
  • Group admin operations (add/remove members, update metadata)

After initial registration, the wallet key is never needed for day-to-day messaging. For CLI users with Ledger hardware wallets, this means the device only needs to be connected for xmtp new and xmtp revoke. Make sure the Ledger is connected via USB with the Ethereum app open before running those commands.

Compromise & Recovery Model

Each installation holds independent MLS epoch keys. This architecture provides strong security guarantees through MLS forward secrecy and post-compromise security:

Scenario Messages before compromise Messages during compromise Messages after revoke + new installation
Single installation compromised Safe (on other devices) Exposed (on that device only) Safe (new keys, new epoch)
Wallet key compromised Safe (cannot decrypt) N/A (wallet key ≠ message key) Rotate wallet, revoke installations
All installations lost Unrecoverable* N/A Fresh start, no history

* Unless another installation was online to complete History Transfer beforehand.

Key insight: compromising an installation key exposes only messages decrypted by that specific installation during the compromise window. The attacker cannot decrypt messages on other devices, nor future messages after the compromised installation is revoked — because MLS generates fresh epoch keys that exclude the revoked installation.

Message Synchronization

Every installation maintains independent MLS state. Understanding what syncs automatically and what requires explicit action is critical:

Scenario Automatic? Mechanism
New messages across existing installations Yes MLS group — all member installations decrypt in real time
New conversations (invites/welcomes) Yes conversations.sync() or syncAll() fetches new welcomes
Consent & preference state Yes Preference sync via a hidden sync group between your devices
Historical messages on a new installation No History Transfer — requires an existing installation online

Each installation tracks a cursor (bookmark) per conversation. sync() only fetches messages after the cursor, making incremental sync efficient. Streaming (stream()) delivers messages in real time but does not advance the cursor — only sync() does.

History Transfer

A new installation cannot decrypt messages sent before it joined the MLS group. To access historical messages, XMTP provides History Transfer (defined in XIP-64):

  1. New device requests history via the sync group (initiateHistoryRequest)
  2. Existing device (must be online) prepares and uploads an encrypted archive to the history server
  3. New device downloads and imports the archive into its local database

Key details:

  • History transfer is enabled by default — the SDK sets historySyncUrl to an XMTP Labs-hosted server based on your env setting
  • The encrypted payload is stored on the history server for 24 hours only
  • The archive includes conversations, messages, consent state, and HMAC keys
  • If all installations are lost, message history is unrecoverable (no server-side plaintext storage)
  • You can self-host the history server or disable it by setting historySyncUrl to an empty string

Supported Platforms

Target Status
x86_64-unknown-linux-gnu
aarch64-unknown-linux-gnu
aarch64-apple-darwin
x86_64-pc-windows-msvc
aarch64-pc-windows-msvc

Security

This library has not been independently audited. See SECURITY.md for full disclaimer, supported versions, and vulnerability reporting instructions.

  • All FFI calls are bounds-checked and null-pointer guarded
  • Private keys remain in memory only as long as needed; Ledger keys never leave the device
  • No key material is logged or persisted by the SDK (key files are managed by the CLI layer)
  • E2E encryption handled by libxmtp's MLS implementation (RFC 9750)

License

Licensed under either of:

at your option.

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this project shall be dual-licensed as above, without any additional terms or conditions.

About

Safe, ergonomic Rust client SDK for the XMTP messaging protocol.

Topics

Resources

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Sponsor this project

  •  

Languages