Jabber - Giving every Solana wallet an inbox

Jabber is an end-to-end encrypted messaging protocol that lets a person or application send a message directly to any valid Solana address.

View source

Project
Jabber
Recognition
Solana hackathon winner
Scope
Protocol design, encryption, and product engineering
Two clay figures exchanging an encrypted message between wallet devices

Overview

A Solana application can see a user’s wallet address, but that address does not include an email, phone number, or social account. The application has no private way to contact the person behind it.

Jabber treats the wallet address itself as an inbox. It lets a person or application send an end-to-end encrypted message to any valid Solana address without requiring the recipient to create another account.

The repository contains three working layers: a Rust program that validates and writes protocol state, a TypeScript client that derives accounts and builds transactions, and a Next.js web application for profiles, threads, and direct messages.

Jabber won first place at Solana’s inaugural hackathon and received a $30,000 USDC-SPL award. It is now used by several venture-backed startups.

Why use the wallet as an inbox?

Wallet-native applications already know a user’s public key. Jabber makes that key sufficient for first contact, so a recipient does not need to reveal an email address, phone number, or social profile.

That makes several practical flows possible:

  • A DeFi protocol can warn a wallet about a position approaching liquidation.
  • An NFT buyer can contact the current owner directly.
  • A DAO can send governance notices to participating wallets.
  • A wallet or explorer can add private messages, comments, or community threads.
  • An application can deliver a message before the recipient has created a Jabber profile.

The last case shaped the protocol. A registered sender can start a thread with any valid address. If the recipient has no profile yet, the program links that thread into a root list of messages for unregistered wallets so it can be discovered later.

The on-chain account model

Jabber stores protocol state in four Borsh-encoded account types. Each address is derived deterministically, so the client can locate state without relying on a separate database.

Profile

A profile is derived from the wallet public key and the fixed seed profile. It stores an optional name and bio, a price-per-message field, and a pointer to the wallet’s newest thread.

Thread

A thread represents the relationship between two wallets. It stores both participant keys, the next message index, and a previous-thread pointer for each participant. Together, those pointers form a linked list of conversations for each wallet.

The first sender creates the thread as a rent-exempt account. After that, both clients derive the same two possible thread addresses and use whichever one exists.

Message

Every message gets its own deterministic account. Its seed combines the thread’s message index with the recipient public key. The serialized state contains a message kind, the payload bytes, and a timestamp read by the program from Solana’s clock sysvar.

Jabber root

The root account stores the tail pointer for threads sent to wallets that have not registered. When the recipient later opens the client, the prototype walks this list and filters for threads containing that wallet.

How an encrypted message is sent

  1. The client validates the recipient as a Solana public key and derives the sender profile, receiver profile, both possible thread accounts, and the next message account.
  2. If this is the first interaction, the transaction creates a rent-exempt thread account and starts its message counter at one.
  3. The client converts the sender’s Ed25519 signing key pair and the recipient’s Ed25519 public key to Curve25519.
  4. The plaintext is encrypted locally with tweetnacl.box. The first 24 bytes of the deterministic message-account public key are used as the nonce.
  5. The encrypted bytes and message kind are serialized with Borsh and included in the SendMessage instruction.
  6. The Rust program verifies the sender signature, checks that every supplied profile, thread, and message account was derived correctly, writes the network timestamp, increments the thread counter, and updates the linked thread pointers.
  7. The receiving client watches the thread account with a Solana account-change subscription. When the message count changes, it derives and fetches only the missing message accounts, then decrypts them locally.

The plaintext and private keys are not written to the network. Only the encrypted payload is stored in the message account.

Encryption and privacy boundaries

Using the wallet identity for encryption avoids asking users to create and exchange a second key pair. The client converts Ed25519 keys to Curve25519 with ed2curve, then uses TweetNaCl’s authenticated public-key encryption for the message body.

This protects message contents, but it does not make the interaction invisible. On a public chain, participant accounts, transaction timing, message-account creation, and ciphertext size remain observable.

It also creates an important client constraint: encryption needs access to the wallet’s secret key material. The prototype generates or imports a Solana account directly in the web client. A production version built around modern wallet adapters would need a safer key-agreement or delegated encryption-key flow because most wallets intentionally do not expose raw private keys.

Real-time delivery without a message server

The web client subscribes directly to Solana account changes. It watches the user profile for newly linked threads and each active thread for changes to its message counter.

That counter acts as a compact synchronization signal. When it advances, the client derives message addresses for the missing index range, fetches those accounts, determines which participant created each one, and decrypts encrypted message kinds locally.

The protocol specification also sketches push-notification nodes, PubSub, group chat, and mobile or JavaScript SDKs. Those are roadmap components; the repository itself implements direct messaging through the Rust program, TypeScript protocol client, and web interface.

Storage and cost decisions

Threads and profiles are long-lived, rent-exempt accounts. The specification explored making individual message accounts temporary, allowing clients or notification nodes to read them before their storage expired. That would reduce the cost of high-volume messaging while still allowing permanent storage where needed.

The checked-in client currently allocates rent-exempt message accounts, and the source marks temporary message storage as a future optimization. The profile also models a price-per-message field, but the current SendMessage instruction does not transfer that payment. Both details are useful examples of the difference between the protocol design and the hackathon implementation.

Prototype trade-offs

  • Discovering messages sent before registration requires scanning the shared unregistered-thread list. The source notes that this becomes expensive as the network grows.
  • Messages are fetched by deriving accounts across an index range. This is simple and deterministic, but a production client would benefit from indexing and caching.
  • Wallet-to-wallet encryption protects content, not public-chain metadata.
  • Raw secret-key access made the prototype possible, but it should not be carried into a production wallet integration unchanged.
  • Push notifications, PubSub, group storage, and SDKs remain design work rather than completed repository features.

What we did

  • Protocol and account-model design
  • Rust Solana program
  • Deterministic account derivation
  • End-to-end encryption and key conversion
  • TypeScript transaction client
  • Real-time account subscriptions
  • Messaging web application
  • Open-source implementation

Technology

  • Solana
  • Rust
  • TypeScript
  • Solana Web3.js
  • Borsh
  • TweetNaCl
  • Curve25519
  • Next.js
  • Redux

Read the source

The complete program, client, protocol specification, and web application are available in the open-source Jabber repository.

This will be a foundational primitive. So many use cases: DeFi developers communicating with users, public message boards and comments, troll boxes, and more.

Kyle Samani, Multicoin CapitalView original post
Solana hackathon
1st place
USDC-SPL award
$30K
Encrypted messages
E2E
No separate signup
Any wallet

Related projects

Building an AI chat product that remembers

AI product

FanChat lets people talk with AI characters in direct messages and group chats. It has reached 250,000+ users and more than one million messages, including students who use it to practise English.

Read more

Using photo data to speed up aerial mapping

Open-source mapping

MapKnitter helps people turn balloon, kite, pole, and drone photographs into georeferenced maps. During Google Summer of Code, we worked on using EXIF data to give each image a useful starting position before manual alignment.

Read more

Bring us the problem worth solving

Our office

  • Bangalore
    GoodWorks Infinity Park
    Electronics City