Skip to content
This repository was archived by the owner on Feb 27, 2026. It is now read-only.

feat(xmtp): add XMTP extension with type fixes for current API#23

Draft
neekolas wants to merge 12 commits into02-15-update_to_upstream_stagingfrom
02-15-add_xmtp_extension
Draft

feat(xmtp): add XMTP extension with type fixes for current API#23
neekolas wants to merge 12 commits into02-15-update_to_upstream_stagingfrom
02-15-add_xmtp_extension

Conversation

@neekolas
Copy link
Copy Markdown

@neekolas neekolas commented Feb 16, 2026

Summary

  • Problem: OpenClaw lacks support for the XMTP decentralized messaging protocol
  • Why it matters: XMTP provides end-to-end encrypted messaging with wallet-based identity, expanding OpenClaw's communication options
  • What changed: Added a new XMTP extension with Agent SDK integration, DM/group policy controls, and remote attachment support
  • What did NOT change: No modifications to existing channels or core gateway functionality

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

  • Closes #

User-visible / Behavior Changes

  • New XMTP channel available for configuration
  • Agents can send/receive messages via XMTP protocol using wallet addresses
  • Support for direct messages and group conversations
  • Pairing flow for DM access control
  • /address command to display the agent's public XMTP address

Security Impact (required)

  • New permissions/capabilities? (Yes)
  • Secrets/tokens handling changed? (Yes)
  • New/changed network calls? (Yes)
  • Command/tool execution surface changed? (No)
  • Data access scope changed? (No)
  • If any Yes, explain risk + mitigation:
    • The extension stores wallet private keys and encryption keys in config
    • Keys are used only for XMTP protocol communication
    • DM policy controls (pairing/allowlist/open/disabled) limit who can message the agent
    • Group policy controls limit which group conversations the agent participates in

Repro + Verification

Environment

  • OS: Linux/macOS
  • Runtime/container: Node.js
  • Integration/channel: XMTP

Steps

  1. Configure XMTP channel with wallet key and encryption key
  2. Start the gateway
  3. Send a message to the agent's XMTP address from another XMTP client
  4. Verify the agent receives and responds to the message

Evidence

  • Comprehensive unit tests for all components
  • E2E test helpers for integration testing

Human Verification (required)

What you personally verified (not just CI), and how:

  • Verified scenarios: Message sending/receiving, DM policy enforcement, group messaging
  • Edge cases checked: Error handling, network failures, invalid configurations
  • What you did not verify: Long-term stability with high message volume

Compatibility / Migration

  • Backward compatible? (Yes)
  • Config/env changes? (Yes)
  • Migration needed? (No)
  • If yes, exact upgrade steps: N/A

Failure Recovery (if this breaks)

  • How to disable/revert this change quickly: Set channels.xmtp.enabled: false in config
  • Files/config to restore: None, extension is self-contained
  • Known bad symptoms reviewers should watch for: XMTP connection errors in logs

Risks and Mitigations

  • Risk: Wallet key exposure
    • Mitigation: Keys stored in encrypted config, used only for XMTP protocol
  • Risk: Unwanted messages from XMTP network
    • Mitigation: Configurable DM policy with pairing/allowlist options

neekolas and others added 12 commits February 15, 2026 21:48
Cherry-pick extensions/xmtp from extensions/xmtp branch and fix type
errors against the current plugin SDK: string error codes, ChatType
"direct", MarkdownTableMode values, positional resolveTextChunkLimit
args, approveHint on DmPolicy, ChannelAccountSnapshot without env,
and remove defunct verifyClient/createNewIdentity.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…erface

Delete types.ts and replace all XmtpAgentRuntime references with the real
Agent class from @xmtp/agent-sdk, removing unsafe casts and improving type safety.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… definition

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ionId===sender check

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ating timestamps

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…t found

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When Pinata API credentials are configured, sendMedia now downloads the
media, encrypts it using the XMTP SDK, uploads the encrypted payload to
IPFS via Pinata, and sends a native RemoteAttachment content type that
renders properly in XMTP clients. Falls back to sending the URL as
plain text when credentials are missing or the download fails.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Author

neekolas commented Feb 16, 2026

@macroscopeapp
Copy link
Copy Markdown

macroscopeapp Bot commented Feb 16, 2026

Add XMTP channel plugin with outbound adapter, inbound pipeline, onboarding/setup flows, DM/group policy, and a 25MB IPFS remote-attachment path using Pinata

Introduce the XMTP channel with account resolution, runtime management, agent lifecycle, outbound text/media (including Pinata upload and 4000‑char chunking), inbound routing/dispatch, DM and group policy enforcement with pairing, onboarding and setup HTTP/gateway methods, command registration, config schema/types, and extensive unit/e2e tests.

📍Where to Start

Start with the XMTP channel plugin entry in xmtpPlugin in channel.ts, then follow outbound in outbound.ts and lifecycle in gateway-lifecycle.ts.


📊 Macroscope summarized 069af2b. 22 files reviewed, 38 issues evaluated, 0 issues filtered, 4 comments posted. View details

@github-actions
Copy link
Copy Markdown

⚠️ Formal models conformance drift detected

The formal models extracted constants (generated/*) do not match this openclaw PR.

This check is informational (not blocking merges yet).
See the formal-models-conformance-drift artifact for the diff.

If this change is intentional, follow up by updating the formal models repo or regenerating the extracted artifacts there.

Comment on lines +12 to +19
const account = resolveXmtpAccount({
cfg,
accountId: resolveDefaultXmtpAccountId(cfg),
});
if (!account.configured) {
return { text: "XMTP is not configured. Run openclaw configure and set up XMTP." };
}
return { text: `This is your XMTP public address: ${account.publicAddress}` };
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium

src/xmtp-commands.ts:12 resolveXmtpAccount can throw if walletKey is malformed. Consider wrapping the call in try-catch to return a user-friendly error instead of crashing.

Suggested change
const account = resolveXmtpAccount({
cfg,
accountId: resolveDefaultXmtpAccountId(cfg),
});
if (!account.configured) {
return { text: "XMTP is not configured. Run openclaw configure and set up XMTP." };
}
return { text: `This is your XMTP public address: ${account.publicAddress}` };
try {
const account = resolveXmtpAccount({
cfg,
accountId: resolveDefaultXmtpAccountId(cfg),
});
if (!account.configured) {
return { text: "XMTP is not configured. Run openclaw configure and set up XMTP." };
}
return { text: `This is your XMTP public address: ${account.publicAddress}` };
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
return { text: `Failed to resolve XMTP account: ${message}` };
}

🚀 Want me to fix this? Reply ex: "fix it for me".

🤖 Prompt for AI
In file extensions/xmtp/src/xmtp-commands.ts around lines 12-19:

`resolveXmtpAccount` can throw if `walletKey` is malformed. Consider wrapping the call in try-catch to return a user-friendly error instead of crashing.

},
};
}
return updateXmtpSection(cfg, { publicAddress });
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Low

src/accounts.ts:95 When accountId isn't found in accounts, the fallback writes publicAddress to the root-level XMTP config. Since getAccountBase merges root config into all accounts, this causes other accounts without explicit publicAddress to inherit the wrong value. Consider returning unchanged config or throwing when the account doesn't exist.

Suggested change
return updateXmtpSection(cfg, { publicAddress });
return cfg;

🚀 Want me to fix this? Reply ex: "fix it for me".

🤖 Prompt for AI
In file extensions/xmtp/src/accounts.ts around line 95:

When `accountId` isn't found in `accounts`, the fallback writes `publicAddress` to the root-level XMTP config. Since `getAccountBase` merges root config into all accounts, this causes other accounts without explicit `publicAddress` to inherit the wrong value. Consider returning unchanged config or throwing when the account doesn't exist.

Comment on lines +139 to +141
walletKey = walletKey.trim();
dbEncryptionKey = dbEncryptionKey.trim();
publicAddress = walletAddressFromPrivateKey(walletKey);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium

src/onboarding.ts:139 Consider wrapping the walletAddressFromPrivateKey call (line 141) in a try/catch or moving it inside the existing try block (line 154), since privateKeyToAccount throws on invalid hex input.

-      walletKey = walletKey.trim();
-      dbEncryptionKey = dbEncryptionKey.trim();
-      publicAddress = walletAddressFromPrivateKey(walletKey);
+      walletKey = walletKey.trim();
+      dbEncryptionKey = dbEncryptionKey.trim();
+      try {
+        publicAddress = walletAddressFromPrivateKey(walletKey);
+      } catch {
+        await prompter.note("Invalid wallet key format. Please provide a valid hex private key.", "XMTP");
+        return { cfg: next };
+      }

🚀 Want me to fix this? Reply ex: "fix it for me".

🤖 Prompt for AI
In file extensions/xmtp/src/onboarding.ts around lines 139-141:

Consider wrapping the `walletAddressFromPrivateKey` call (line 141) in a try/catch or moving it inside the existing try block (line 154), since `privateKeyToAccount` throws on invalid hex input.

const mimeType = (response.headers.get("content-type") ?? "application/octet-stream")
.split(";")[0]
.trim();
const buffer = await response.arrayBuffer();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 High

src/outbound.ts:105 Suggest streaming the media download with a running byte count instead of relying on Content-Length and response.arrayBuffer(). Abort once MAX_MEDIA_BYTES is exceeded to prevent OOM and size spoofing.

🚀 Want me to fix this? Reply ex: "fix it for me".

🤖 Prompt for AI
In file extensions/xmtp/src/outbound.ts around line 105:

Suggest streaming the media download with a running byte count instead of relying on `Content-Length` and `response.arrayBuffer()`. Abort once `MAX_MEDIA_BYTES` is exceeded to prevent OOM and size spoofing.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant