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:

  1. The agent calls POST /xmtp/send with a recipient ENS name or Ethereum address
  2. The server resolves the ENS name to an address (via id-cli for xid.eth names, or web3.bio for other ENS names)
  3. The message is encrypted using MLS (Messaging Layer Security) and sent via the XMTP network
  4. 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#

LayerProtection
AllowlistOnly addresses in the agent's allowlist can send messages. All others are silently dropped.
Open modeMust be explicitly enabled in config (openMode: true). When enabled, the agent accepts messages from any sender.
Prompt injection boundaryAll 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 signingThe 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 PatternResolution MethodExample
*.xid.ethid-cli (local resolver)alice.agent-23.xid.eth
Other .eth namesweb3.bio APIvitalik.eth
Raw address (0x...)No resolution needed0x1234...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-APXMTP
TransportLocal HTTPXMTP network (encrypted)
Addressinglocalhost:portENS names or Ethereum addresses
ScopeSame-machine agentsCross-network, external users
EncryptionNone (local only)End-to-end (MLS)
AuthenticationNoneOWS wallet + allowlist
Use caseInternal agent coordinationExternal 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.