XMTP Messaging
Encrypted agent-to-agent and agent-to-user messaging via ENS names
XMTP Messaging#
XMTP provides encrypted, off-chain messaging between agents and users. Messages are addressed to ENS names (e.g., alice.agent-23.xid.eth) and encrypted end-to-end using the MLS protocol. Unlike REST-AP, which operates over local HTTP, XMTP enables communication across networks and with external users who have XMTP-compatible wallets.
How It Works#
Each agent gets its own XMTP identity through its OWS wallet. When an agent sends a message, the flow is:
- The agent calls
POST /xmtp/sendwith a recipient ENS name or Ethereum address - The server resolves the ENS name to an address (via
id-cliforxid.ethnames, orweb3.biofor other ENS names) - The message is encrypted using MLS (Messaging Layer Security) and sent via the XMTP network
- The recipient's XMTP client decrypts and displays the message
Messages are fully encrypted -- only the sender and recipient can read them. The XMTP network relays ciphertext without access to plaintext content.
Security Model#
XMTP messaging is closed by default. Agents only accept messages from allowlisted senders.
Inbound Message Security#
| Layer | Protection |
|---|---|
| Allowlist | Only addresses in the agent's allowlist can send messages. All others are silently dropped. |
| Open mode | Must be explicitly enabled in config (openMode: true). When enabled, the agent accepts messages from any sender. |
| Prompt injection boundary | All inbound XMTP messages are treated as untrusted external input. The agent's system prompt includes a boundary marker so the LLM distinguishes XMTP content from trusted instructions. |
| OWS signing | The agent's private key never leaves the OWS vault. All signing operations happen inside the vault -- no key material is exposed to the agent process or stored on disk. |
Closed vs Open Mode#
# Closed mode (default) -- only allowlisted senders
defaults:
skills:
- xmtp
# Open mode -- accepts messages from anyone
defaults:
skills:
- xmtp
xmtp:
openMode: true
Use closed mode for production agents. Open mode is useful for public-facing agents that need to accept messages from unknown users.
Data Storage#
XMTP state is stored per-address under ~/.xmtp/, completely outside the project repository:
~/.xmtp/{address}/
├── xmtp.db # Local message database
├── encryption.key # XMTP DB encryption key
└── allowlist.yaml # Allowed sender addresses
Nothing is stored in the agent's working directory or project repo. This means XMTP state persists across redeploys and is never committed to version control.
Allowlist#
The allowlist controls which addresses can send messages to an agent. It is persisted as a YAML file at ~/.xmtp/{address}/allowlist.yaml.
Format#
- address: "0x1234...abcd"
name: "alice.agent-23.xid.eth"
- address: "0x5678...ef01"
name: "bob.myteam.xid.eth"
Each entry contains the sender's Ethereum address and their ENS name for readability. The allowlist is checked on every inbound message -- if the sender's address is not present, the message is dropped.
When openMode: true is set, the allowlist is bypassed and all senders are accepted.
Configuration#
Enable XMTP#
Add the xmtp skill to your agent's skills list and set the OWS_WALLET environment variable:
version: "1.0"
team: my-team
defaults:
skills:
- identity
- inter-agent
- catalog
- wallet
- xmtp
agents:
- name: support
description: "Customer support agent with XMTP messaging"
# .env
OWS_WALLET=my-agent-wallet
The OWS_WALLET environment variable tells the agent which OWS wallet to use for its XMTP identity. Without it, the XMTP skill is skipped at deploy time (similar to the wallet skill).
Per-Agent XMTP Settings#
agents:
- name: public-bot
description: "Public-facing agent"
skills: [xmtp]
xmtp:
openMode: true
- name: internal-agent
description: "Internal team agent"
skills: [xmtp]
# openMode defaults to false -- closed mode
Endpoints#
Send Message: POST /xmtp/send#
Send an XMTP message to an ENS name or Ethereum address.
# Send by ENS name (xid.eth -- resolved via id-cli)
curl -X POST http://localhost:4101/xmtp/send \
-H "Content-Type: application/json" \
-d '{
"to": "alice.agent-23.xid.eth",
"message": "Hello from my agent"
}'
# Send by ENS name (non-xid.eth -- resolved via web3.bio)
curl -X POST http://localhost:4101/xmtp/send \
-H "Content-Type: application/json" \
-d '{
"to": "vitalik.eth",
"message": "Hello from my agent"
}'
# Send by Ethereum address
curl -X POST http://localhost:4101/xmtp/send \
-H "Content-Type: application/json" \
-d '{
"to": "0x1234...abcd",
"message": "Hello from my agent"
}'
Response:
{
"ok": true,
"message": "Message sent"
}
ENS Name Resolution#
The /xmtp/send endpoint resolves recipient names differently depending on the ENS suffix:
| Name Pattern | Resolution Method | Example |
|---|---|---|
*.xid.eth | id-cli (local resolver) | alice.agent-23.xid.eth |
Other .eth names | web3.bio API | vitalik.eth |
Raw address (0x...) | No resolution needed | 0x1234...abcd |
Status: GET /xmtp/status#
Check whether the agent's XMTP client is connected and ready.
curl http://localhost:4101/xmtp/status
Response:
{
"connected": true,
"address": "0x1234...abcd"
}
XMTP vs REST-AP#
XMTP and REST-AP serve different communication needs:
| REST-AP | XMTP | |
|---|---|---|
| Transport | Local HTTP | XMTP network (encrypted) |
| Addressing | localhost:port | ENS names or Ethereum addresses |
| Scope | Same-machine agents | Cross-network, external users |
| Encryption | None (local only) | End-to-end (MLS) |
| Authentication | None | OWS wallet + allowlist |
| Use case | Internal agent coordination | External messaging, cross-team communication |
For agents on the same machine, REST-AP is simpler and faster. Use XMTP when agents need to communicate across networks or with external users and wallets.