Skip to content

End-to-end encrypted messaging

XMTP provides end-to-end encrypted messaging for agent interactions, crucial for privacy, security, and compliance. Without it, messages are exposed to many security risks

Thesis

AI is transforming consumer tech, with messaging becoming the main channel for interacting with agent services. This shift will scale message traffic astronomically, analogous to the web’s rise in the 2000s. Just as Cloud-flare secured web traffic, messaging will need robust scalable end-to-end encrypted messages to protect sensitive communications.

Features:

  • E2EE: End to end encrypted MLS encryption
  • Multi-agent: Support multi-agent through group chats
  • Interoperable: Works across all platforms and frontends
  • Scalable: Decentralized, open-source
  • Anonymous: By defaul every identity is ephemeral and anonymous.

Risks

Risks of not using end-to-end encryption for agent interactions exposes the users to what is called as Man in the Middle Attacks.

Man in the Middle Attacks: Intercept requests in between to alter or manipulate data sent or received by the AI service

  • Phishing: Messages can be intercepted and manipulated.
  • Privacy: Sensible information read by unwanted parties
  • Tampering: Content can be altered without detection.

Installation

Install the xmtp package

cmd
bun install xmtp

Usage

This is how you can use the xmtp package to create a client and handle messages.

If you are in web use the xmtp-e2ee package instead.

import { XMTP } from "xmtp";
 
const xmtp = new XMTP(onMessage, {
  encryptionKey: process.env.WALLET_PRIVATE_KEY,
});
 
await xmtp.init();
 
const onMessage = async (message, user) => {
  console.log(`Decoded message: ${message.content.text} by ${user.address}`);
  // Your AI model response
  await xmtp.send({
    message: response,
    originalMessage: message,
  });
};

GPT example

  1. handleMessage: Triggered each time a new message is received from XMTP.
  2. client.send(): Used to send messages (e.g., AI prompts and responses) back to the XMTP network.

In this example, when handleMessage is invoked, it takes the incoming user message, passes it to the AI model (OpenAI's Chat Completion API), and then sends the AI's response back over XMTP.

import { XMTP } from "xmtp";
import { OpenAI } from "openai";
 
const openai = new OpenAI({
  apiKey: process.env.OPENAI_API_KEY,
});
 
const xmtp = new XMTP(onMessage, {
  encryptionKey: /*wallet private key*/,
});
await xmtp.init();
 
const onMessage = async (message, user) => {
  console.log(`Decoded message: ${response} by ${user}`);
 
  // Prepare conversation history or context as needed
  const history = [
    { role: "system", content: "You are a helpful assistant." },
    { role: "user", content: message },
  ];
 
  // Call the OpenAI API to process the user's message
  const response = await openai.createChatCompletion({
    model: process.env.GPT_MODEL || "gpt-4",
    messages: history,
  });
 
  const response = response.data.choices[0].message.content.trim();
 
  // Send the AI-generated response back to the XMTP network
  xmtp.send({
    message: response,
    originalMessage: message,
  });
};

React example

This is how you can use the xmtp-e2ee package to create a client and handle messages.

import { XMTP, Message } from "xmtp-e2ee";
// ... other imports ...
 
function Chat({ user }: { user: UserInfo }) {
  const [messages, setMessages] = useState<Message[]>([]);
  const [newMessage, setNewMessage] = useState("");
  const [xmtp, setXmtp] = useState<XMTP | undefined>(undefined);
  const [isLoading, setIsLoading] = useState(true);
  // ... other state variables ...
 
  useEffect(() => {
    const init = async () => {
      try {
        if (user?.address) {
          await initXmtp(newWallet);
        }
      } catch (error) {
        console.error("Error resolving recipient:", error);
        setIsLoading(false);
      }
    };
 
    init();
  }, [user.address]);
 
  const onMessage = async (message: Message | undefined) => {
    if (message) {
      setMessages((prevMessages) => [...prevMessages, message]);
    }
  };
 
  const initXmtp = async (wallet: any) => {
    try {
      const xmtpClient = new XMTP(onMessage);
      await xmtpClient.init();
      setXmtp(xmtpClient);
      setIsLoading(false);
    } catch (error) {
      console.error("Error initializing XMTP:", error);
      setIsLoading(false);
    }
  };
 
  const sendMessage = async (e: React.FormEvent) => {
    e.preventDefault();
    if (!xmtp || !newMessage || !recipientInfo?.address) return;
 
    try {
      const message = await xmtp.send({
        message: newMessage,
        receivers: [recipientInfo.address],
      });
      setMessages((prevMessages) => [...prevMessages, message]);
      setNewMessage("");
    } catch (error) {
      console.error("Error sending message:", error);
    }
  };
 
  return <div className={styles.chatContainer}>{/* Render chat UI */}</div>;
}
 
export default React.memo(Chat);