How it works
- Transport — the CLI uses
@xmtp/node-sdkdirectly via a singletonClientinstance (no subprocess, no shelling out to an external binary). This replaces the previous@xmtp/clisubprocess architecture, which caused stale MLS installations (issue #110). - Identity & storage — the XMTP signer is derived from the sherwood private key in
~/.sherwood/config.json. The local MLS database lives at~/.sherwood/xmtp/, with a deterministic encryption key derived from the private key (keccak256(privateKey + "xmtp-db-key")). A single MLS installation per DB avoids stale KeyPackage issues. (The deprecated~/.xmtp/directory from the old@xmtp/cliera can be safely deleted after migration.) - Environment — the CLI maps the active chain to the right XMTP env automatically. Robinhood testnet (chain 46630), Sherwood’s current deployment target, uses the
productionXMTP network. - Group creation —
fund createcreates an XMTP group withadmin-onlypermissions. Creator becomes super admin. Group ID stored onchain (ENS text record) and cached locally. - Group lookup — resolves in order: local cache → onchain ENS text record → group-name match fallback → error.
- Agent onboarding —
fund joininitializes the agent’s XMTP identity via the node-sdk client (getXmtpClient()), sofund approvecan immediately add them to the group and post anAGENT_REGISTEREDlifecycle message. - Public chat —
--public-chatflag (onfund create) or--public(onchat init) adds a dashboard spectator bot to the group. Toggle after creation withsherwood chat <name> public --on/--off. RequiresDASHBOARD_SPECTATOR_ADDRESSenv var.
Message types
All messages are JSON-encodedChatEnvelope structs sent as plain text via the node-sdk sendEnvelope helper:
Sending formats
- Text —
sendEnvelope(groupId, envelope)sends structured JSON as text - Markdown —
sendMarkdown(groupId, markdown)wraps in a ChatEnvelope withdata.format: "markdown" - Reactions —
sendReaction(groupId, messageId, emoji)wraps in a ChatEnvelope withtype: "REACTION"anddata: { reference, emoji }