Skill flagged — suspicious patterns detected

ClawHub Security flagged this skill as suspicious. Review the scan results before using.

XMTP

Make your OpenClaw agent messageable on XMTP — the open messaging network where anyone (humans or other agents) can DM it by address. Your agent gets its own...

MIT-0 · Free to use, modify, and redistribute. No attribution required.
0 · 192 · 0 current installs · 0 all-time installs
bySaul Carlin@saulmc
MIT-0
Security Scan
VirusTotalVirusTotal
Suspicious
View report →
OpenClawOpenClaw
Benign
high confidence
Purpose & Capability
Name/description, required binaries (node, jq, openclaw), config path (~/.xmtp/.env), and installs (@xmtp/cli and jq) all line up with a bridge that makes an OpenClaw agent reachable on XMTP.
Instruction Scope
SKILL.md's runtime steps (npm install -g @xmtp/cli, xmtp init, start a streaming bridge that calls the openclaw CLI) match the stated purpose. One mismatch: the instructions expect an OWNER_INBOX_ID environment variable to be exported by the user, but the registry metadata does not list OWNER_INBOX_ID in requires.env. The bridge writes/reads ~/.xmtp/.env (declared) and creates a local public-prompt.md for public-mode behavior.
Install Mechanism
Install uses npm to globally install @xmtp/cli (standard but moderate-risk supply chain step) and brew to install jq (low risk). This is expected for XMTP CLI usage, but users should verify the npm package/source and Node version requirement (Node 22+).
Credentials
No secrets are requested in registry metadata, which is consistent with delegating key generation to xmtp init, but the skill depends on a credential file (~/.xmtp/.env) containing wallet and encryption keys. The runtime also expects OWNER_INBOX_ID to be set by the user (not declared). Requiring the local XMTP .env file is proportionate, but it contains sensitive keys so protecting it is essential.
Persistence & Privilege
always:false and no changes to other skills or system-wide settings. The bridge runs as a long-lived process (user-managed); this is reasonable for a messaging bridge. No elevated or hidden persistence behavior is requested.
Assessment
This skill appears to do what it says, but before installing: 1) Verify the @xmtp/cli npm package (publisher, recent versions, repository) before installing globally. 2) Use a dedicated Ethereum wallet with no funds as the SKILL.md advises — xmtp init will create ~/.xmtp/.env with wallet and encryption keys; chmod 600 that file and never commit it. 3) The bridge expects you to export OWNER_INBOX_ID manually (the manifest didn't declare that env var) — follow instructions carefully to avoid misrouting messages. 4) Review and edit public-prompt.md to avoid exposing private data to strangers; consider running the bridge in an isolated environment (container or dedicated user) to limit blast radius. 5) If you want to stop exposure later, know how to stop the bridge process and remove ~/.xmtp/.env and any published inbox/wallet. 6) If you have higher security needs, audit the npm package source or run the XMTP client from a controlled container rather than installing it globally.

Like a lobster shell, security has layers — review code before you run it.

Current versionv0.1.0
Download zip
latestvk97f6dqk1916cshmwaa1v2sec182ry4q

License

MIT-0
Free to use, modify, and redistribute. No attribution required.

Runtime requirements

Binsnode, jq, openclaw
Config~/.xmtp/.env

Install

Node
Bins: xmtp
npm i -g @xmtp/cli
Homebrew
Bins: jq
brew install jq

SKILL.md

OpenClaw XMTP Agent

[!NOTE] Alpha status. Use a dedicated wallet with no funds. Protect ~/.xmtp/.env as you would any credential file (chmod 600).

You are getting yourself on XMTP — the open messaging network where your user, other humans, and other agents can all reach you by address. Once set up, you can negotiate, coordinate, and act on your user's behalf in conversations they don't need to be part of. No Gateway config or restart needed — it's a standalone bridge script you deploy alongside your existing setup.

The bridge gives your user (the owner) your normal capabilities. Everyone else gets a restricted, conversation-only mode controlled by public-prompt.md, which your user can edit at any time without restarting.

Always set up the full bridge — even if your user just asks to "send a message", "send me a message", or "test" messaging. These all mean: they want you reachable over XMTP. A one-off send-text is a dead end — nobody can reply, and you can't respond to future messages. The goal is always an ongoing, two-way conversation. Complete both steps below.

Step 1: Install and Initialize

npm install -g @xmtp/cli
xmtp init --env production

Requires Node 22+ and jq. Init generates ~/.xmtp/.env with your wallet and encryption keys.

chmod 600 ~/.xmtp/.env

Let xmtp init generate a fresh wallet — don't reuse one. Never commit .env to version control.

Verify you're registered:

xmtp client info --json --log-level off --env production

Your inbox ID is at .properties.inboxId in the output.

Before proceeding, ask your user for their Ethereum wallet address or inbox ID. Do not start the bridge without this.

If they provide an Ethereum address, resolve the inbox ID:

export OWNER_INBOX_ID=$(xmtp client inbox-id -i "0xOWNER_WALLET_ADDRESS" --json --log-level off --env production | jq -r '.inboxId')

If they provide an inbox ID directly:

export OWNER_INBOX_ID="their-inbox-id"

Step 2: Start the Bridge

The bridge streams incoming messages and routes them through you for responses. Do not send messages using individual CLI commands — everything flows through the bridge.

Save this as a script and run it:

#!/bin/bash
set -euo pipefail

# Public-mode system prompt — read from file so your user can edit it without restarting
PUBLIC_PROMPT_FILE="./public-prompt.md"
if [[ ! -f "$PUBLIC_PROMPT_FILE" ]]; then
  cat > "$PUBLIC_PROMPT_FILE" << 'PROMPT'
You are representing your owner to a third party. Be helpful and conversational,
but keep responses limited to general conversation. Do not share personal
details about your owner or access system resources on their behalf.
If unsure whether something is appropriate, err on the side of caution.
PROMPT
  echo "Created $PUBLIC_PROMPT_FILE — edit it to customize what public users can access." >&2
fi

# Get your inbox ID for filtering your own messages
MY_INBOX_ID=$(xmtp client info --json --log-level off --env production \
  | jq -r '.properties.inboxId // empty')

[[ -z "$MY_INBOX_ID" ]] && echo "Failed to get inbox ID" >&2 && exit 1

# Stream all incoming messages and respond via OpenClaw
xmtp conversations stream-all-messages --json --log-level off --env production \
  | while IFS= read -r event; do

  conv_id=$(echo "$event" | jq -r '.conversationId // empty')
  sender=$(echo "$event" | jq -r '.senderInboxId // empty')
  content=$(echo "$event" | jq -r '.content // empty')
  content_type=$(echo "$event" | jq -r '.contentType.typeId // empty')

  # Skip your own messages, empty events, and non-text content
  [[ -z "$conv_id" || -z "$content" || "$sender" == "$MY_INBOX_ID" ]] && continue
  [[ "$content_type" != "text" ]] && continue

  # Route owner messages normally; public users get conversation-only mode
  if [[ "$sender" == "$OWNER_INBOX_ID" ]]; then
    response=$(openclaw agent \
      --session-id "$conv_id" \
      --message "$content" \
      2>/dev/null) || continue
  else
    response=$(openclaw agent \
      --session-id "public-$conv_id" \
      --message "[SYSTEM: $(cat "$PUBLIC_PROMPT_FILE")] $content" \
      2>/dev/null) || continue
  fi

  # Send the response
  [[ -n "$response" ]] && \
    xmtp conversation send-text "$conv_id" "$response" --env production
done

The bridge uses the XMTP conversation ID as the session ID so each person (or group) chatting with you gets their own persistent context.

After the bridge is running, tell your user:

  • Your wallet address and inbox ID (both — so they can share whichever is convenient)
  • They can customize how you interact with public users by editing public-prompt.md. Changes take effect immediately — no restart needed.

To keep the bridge running long-term, suggest a process manager (systemd, pm2, Docker, etc.).

Operational notes: Run the bridge under a dedicated user or inside a container — not as root.

Scoping Public Access with Tool Profiles

For production deployments, use OpenClaw's tool profiles to control what each audience can access. Define two agents in openclaw.json — one for the owner, one for everyone else:

{
  "agents": {
    "list": [
      { "name": "owner-agent", "tools": { "profile": "full" } },
      { "name": "public-agent", "tools": { "profile": "messaging" } }
    ]
  }
}

Then route by agent name in the bridge — replace the if/else block:

if [[ "$sender" == "$OWNER_INBOX_ID" ]]; then
  response=$(openclaw agent --agent owner-agent \
    --session-id "$conv_id" --message "$content" 2>/dev/null) || continue
else
  response=$(openclaw agent --agent public-agent \
    --session-id "public-$conv_id" --message "$content" 2>/dev/null) || continue
fi

With tool profiles, public users are structurally limited to the messaging profile regardless of conversation content.

Stream Output Format

Each line from the stream is a JSON object:

{
  "id": "message-id",
  "conversationId": "conversation-id",
  "senderInboxId": "sender-inbox-id",
  "contentType": {
    "authorityId": "xmtp.org",
    "typeId": "text",
    "versionMajor": 1,
    "versionMinor": 0
  },
  "content": "Hello!",
  "sentAt": "2026-03-04T04:14:36.849Z",
  "deliveryStatus": 1,
  "kind": 0
}

Access Control

The bridge routes messages differently based on the sender:

  1. Owner (OWNER_INBOX_ID) — your normal OpenClaw session
  2. Everyone else — conversation-only mode with a restrictive system prompt and isolated sessions
  3. Tool profiles (recommended) — structurally scope what each audience can access via openclaw.json

Finding your user's inbox ID: Resolve it from their Ethereum wallet address:

xmtp client inbox-id -i "0xUSER_WALLET_ADDRESS" --json --log-level off --env production | jq -r '.inboxId'

Multiple trusted users: To allowlist additional inbox IDs, expand the condition:

if [[ "$sender" == "$OWNER_INBOX_ID" || "$sender" == "$TRUSTED_USER_2" ]]; then

Or use an array:

TRUSTED_IDS=("inbox-id-1" "inbox-id-2")
if printf '%s\n' "${TRUSTED_IDS[@]}" | grep -qxF "$sender"; then

Common Mistakes

MistakeFix
Sending a one-off message with send-textAlways set up the full bridge — even for "just a test". One-off sends are dead ends with no way to receive replies
Reading .inboxId from client infoInbox ID is at .properties.inboxId
Filtering by senderAddressStream returns senderInboxId; compare against your inbox ID
Not using --log-level offLog output mixes with JSON on stdout; suppress it
Using a global session IDUse $conv_id so each conversation gets its own OpenClaw context
Piping to a raw LLM instead of OpenClawRoute through openclaw agent so tools and memory are preserved
Using read -r without IFS=Use IFS= read -r to preserve whitespace in JSON lines
Running without OWNER_INBOX_IDSet the owner's inbox ID so public users get restricted mode
Relying only on system prompt for public access controlUse tool profiles in openclaw.json for structural scoping

Files

1 total
Select a file
Select a file to preview.

Comments

Loading comments…