
Company News
Socket Joins New OpenJS Program to Fund Node.js Security Work
Socket is joining the OpenJS Security Stewardship Program to fund Node.js vulnerability research, maintainer remediation, and security releases.
@markmnl/openclaw-fmsg
Advanced tools
OpenClaw channel plugin for the federated messaging protocol fmsg
Connect OpenClaw to fmsg, the federated messaging protocol. The plugin receives messages over WebSocket, catches up after reconnects, preserves fmsg message trees as native OpenClaw sessions, and carries attachments in both directions.
no_reply enforcement, loop protection, and outbound credential redaction.message tool, with reconnect synchronization.fmsg_send tool can continue a one-to-one thread or start a new root.| Component | Supported |
|---|---|
| OpenClaw | 2026.8.1 or newer; CI-verified through 2026.8.2 |
| fmsg Web API | FMSG-003 v0.2.0 or newer for reactions |
| Node.js 22 | 22.22.3 or newer |
| Node.js 24 | 24.15.0 or newer |
| Node.js 25 | 25.9.0 or newer |
| Package format | TypeScript source plus prebuilt ESM JavaScript |
openclaw plugins install npm:@markmnl/openclaw-fmsg
openclaw channels add --channel fmsg
The published package contains TypeScript source for linked development and prebuilt JavaScript for managed installs. It has no install-time build hook and is compatible with OpenClaw/npm dependency installation using --ignore-scripts.
The setup flow prompts for your fmsg Web API URL, home fmsg address, and API key. No hosted API URL is assumed. For the credential, setup can store a plaintext key, use FMSG_API_KEY, or write an OpenClaw SecretRef.
For environment-based configuration, expose all values to the Gateway process:
export FMSG_API_URL='https://fmsg-api.example.com'
export FMSG_API_KEY='fmsgk_...'
export FMSG_HOME_CHANNEL='@owner@example.com'
Alternatively, configure the channel explicitly:
{
"channels": {
"fmsg": {
"enabled": true,
"apiUrl": "https://fmsg-api.example.com",
"apiKey": {
"source": "env",
"provider": "default",
"id": "FMSG_API_KEY"
},
"homeChannel": "@owner@example.com",
"allowedUsers": ["@owner@example.com"],
"allowAllUsers": false,
"maxAgentTurnsPerThread": 8,
"maxAgentTurnsPerRoot": 20,
"maxAgentTurnsPerSender": 20,
"agentTurnWindowMs": 60000,
"actions": { "reactions": true },
"reactionNotifications": "own"
}
}
}
Restart the gateway. A successful WebSocket connection identifies the address obtained from the JWT:
fmsg connected as @agent@example.com
The sender address is always read from the exchanged JWT's sub claim. homeChannel is not a from-address.
channels.fmsg.apiKey accepts OpenClaw env, file, exec, and store SecretRefs. OpenClaw keeps the reference in openclaw.json, resolves it into the active in-memory runtime snapshot, and includes this path in openclaw secrets audit, configure, and apply.
An env SecretRef still requires the named variable in the Gateway environment. file, exec, and store references use the corresponding provider under OpenClaw's secrets.providers configuration and do not require FMSG_API_KEY to be injected into the Gateway environment.
{
"channels": {
"fmsg": {
"apiUrl": "https://fmsg-api.example.com",
"apiKey": {
"source": "file",
"provider": "mounted-json",
"id": "/fmsg/apiKey"
},
"homeChannel": "@owner@example.com"
}
}
}
When the legacy FMSG_API_KEY environment variable is present, it remains authoritative and a configured apiKey SecretRef is treated as inactive. Setup, Gateway logs, and the secrets audit warn about this shadowing. Remove the obsolete environment entry after migrating; the plugin never deletes credentials automatically.
Environment values take precedence over channels.fmsg. apiUrl is required: the plugin does not assume a public or hosted fmsg deployment.
| Configuration | Environment | Default | Purpose |
|---|---|---|---|
apiUrl | FMSG_API_URL | required | Explicit fmsg Web API base URL |
apiKey | FMSG_API_KEY | required | fmsgk_... credential or OpenClaw SecretRef |
homeChannel | FMSG_HOME_CHANNEL | prompted by setup | Default operator destination and fallback sole allowlist entry |
allowedUsers | FMSG_ALLOWED_USERS | [] | Allowed inbound senders; environment form is comma-separated |
allowAllUsers | FMSG_ALLOW_ALL_USERS | false | Explicitly allow every valid fmsg sender |
maxAgentTurnsPerThread | FMSG_MAX_AGENT_TURNS_PER_THREAD | 8 | Automatic OpenClaw turns allowed per branch/window; 0 disables |
maxAgentTurnsPerRoot | FMSG_MAX_AGENT_TURNS_PER_ROOT | 20 | Automatic OpenClaw turns across every branch of one root/window; 0 disables |
maxAgentTurnsPerSender | FMSG_MAX_AGENT_TURNS_PER_SENDER | 20 | Automatic OpenClaw turns from one normalized sender across all roots/window; 0 disables |
agentTurnWindowMs | FMSG_AGENT_TURN_WINDOW_MS | 60000 | Sliding window shared by all three circuit breakers |
mediaMaxBytes | — | 10485760 | Maximum bytes per inbound/outbound attachment |
actions.reactions | — | true | Expose react and reactions on OpenClaw's shared message tool |
reactionNotifications | — | own | off, own, or all; controls which inbound reaction changes become system events |
Access is default-deny:
allowedUsers list is enforced case-insensitively.homeChannel configured uses that address as the effective sole allowlist and logs a warning.allowAllUsers: true is the only open-access setting.These settings deliberately have different jobs:
| Setting | Purpose |
|---|---|
channels.fmsg.allowedUsers | Who may send ordinary inbound messages |
channels.fmsg.homeChannel | Default operator destination and fallback inbound allowlist entry |
commands.ownerAllowFrom | Who may use privileged OpenClaw owner commands |
agents.defaults.heartbeat.target | Heartbeat route mode: owner, last, none, or a channel such as fmsg |
agents.defaults.heartbeat.to | Explicit destination when a channel target is selected |
homeChannel does not grant owner privileges. Interactive setup offers that privilege separately and defaults to no. To configure it manually:
{
"commands": {
"ownerAllowFrom": ["fmsg:@owner@example.com"]
}
}
A concrete effective fmsg allowlist can supply OpenClaw's default owner heartbeat route. An explicit route looks like:
{
"agents": {
"defaults": {
"heartbeat": {
"target": "fmsg",
"to": "@owner@example.com"
}
}
}
}
Heartbeat routing is independent of proactive messaging: fmsg_send can initiate a message without granting its recipient owner authority.
Run openclaw status --deep after setup. The fmsg account reports its JWT-derived identity, API URL, credential source, access policy, effective allowlist, connection state, and recent delivery errors without exposing the credential.
Do not treat a session transcript as proof of delivery. Confirm the reply arrived in the peer's fmsg inbox. For a fuller smoke test, verify a root reply, a later sibling branch, reply-all, an attachment, no_reply, and restart catch-up.
fmsg is a message tree. OpenClaw is given the fmsg branch ID as native threadId and the direct parent message as replyToId.
| fmsg event | OpenClaw branch/thread ID | Result |
|---|---|---|
Root R from counterparty C | R | New counterparty session |
First child on the line from R | inherited R | Continues that session |
Later sibling M under root R | R:br:M | New session with direct ancestry context |
Descendant of M | inherited R:br:M | Continues the branch session |
A native key has this shape:
agent:<agentId>:fmsg:direct:<counterparty>:thread:<root-or-branch>
The first discovered child continues its parent's session; later discovered siblings fork. Catch-up is processed oldest-first, making this chronological during normal delivery. Fork context contains only the bounded root-to-parent pid chain—never sibling history—and is explicitly labelled as untrusted content.
Outbound replies:
from, to, every add_to_from, and every add_to.to on that parent, excluding the JWT sender.int64 numbers at the Web API boundary, including values above JavaScript's safe-integer range.The plugin registers fmsg_send:
{
"to": "@alice@example.net",
"text": "Status update",
"fmsg_new_thread": false,
"topic": "Optional topic for a new root"
}
By default it continues the latest strict one-to-one thread. Set fmsg_new_thread to force a new root.
Reactions require an fmsg Web API implementing FMSG-003 v0.2.0 and FMSG-005. They use OpenClaw's shared message tool rather than a plugin-specific tool:
{ "action": "react", "messageId": "3528", "emoji": "👍" }
{ "action": "react", "messageId": "3528", "emoji": "👍", "remove": true }
{ "action": "react", "messageId": "3528", "emoji": "" }
{ "action": "reactions", "messageId": "3528" }
Each fmsg participant has one effective reaction per message. Adding another emoji changes it; an empty emoji clears it. A specific removal only clears the reaction when the agent currently has that emoji, so OpenClaw's removal semantics remain idempotent.
Inbound reaction events update persisted state and become low-priority system events in the subject's existing root or branch session. They do not create sessions, mark messages read, consume automatic-turn budgets, or trigger an immediate agent reply. reactionNotifications: "own" reports reactions to agent-authored messages; all also reports reactions to other known messages; off synchronizes state silently. The sender access policy is still enforced.
Because reaction WebSocket events are not durable, reconnect catch-up synchronizes effective reactions from both the inbox and sent-message lists. The first run after upgrading seeds existing reactions silently instead of replaying historical notifications.
no_reply inbound messages are marked read without ancestry fetching or an automatic agent turn.important is exposed as FmsgImportant in OpenClaw's inbound context. no_reply is hard-suppressed before model dispatch.no_reply; further turns are suppressed until timestamps leave the shared sliding window.maxAgentTurnsPerThread, maxAgentTurnsPerRoot, or maxAgentTurnsPerSender to 0 disables that scope's breaker.fmsgk_... and compact JWT-shaped strings are redacted immediately before any outbound text is drafted and from plugin error logs.senderIsOwner is derived from commands.ownerAllowFrom, not ordinary channel access. senderKind remains unknown unless fmsg eventually supplies authenticated human/agent metadata; the plugin does not guess from addresses.Runtime routing state is stored under OpenClaw's state directory in fmsg/<account>.json, with bounded message and deduplication history.
Final replies use OpenClaw's durable outbound path. An inbound message is acknowledged only after delivery settles or the plugin intentionally suppresses a reply. Definite pre-send failures remain eligible for inbox catch-up after reconnect. fmsg currently exposes no verified idempotency key or send-reconciliation contract to this plugin, so an interrupted request after platform dispatch is treated as an ambiguous outcome rather than retried blindly.
Restarting the whole Gateway can interrupt a model turn. The plugin persists routing state before dispatch, drains its in-process receive queue during graceful shutdown, and catches up unacknowledged inbox messages after reconnect; it cannot preserve an executing model process across termination.
npm install --ignore-scripts
npm run typecheck
npm test
npm run build
npm pack --dry-run
Stable releases are published to npm by .github/workflows/publish.yml when a GitHub Release is published. Use a stable semantic-version tag such as v0.1.1; the workflow derives the npm version from the tag and updates both package.json and package-lock.json in the release checkout before publishing. Build and commit dist/ whenever source code changes before creating the release.
The npm package must configure a trusted GitHub Actions publisher for repository markmnl/openclaw-fmsg, workflow publish.yml, with npm publish allowed. The workflow uses short-lived OIDC credentials and does not require an npm token secret.
The unit suite runs a strict in-memory HTTP and WebSocket implementation of the fmsg Web API. It enforces numeric pid input and covers full-range int64 preservation, JWT exchange/refresh, drafts and attachments, WebSocket plus inbox/reaction catch-up, reaction actions and snapshot migration, terminal messages, access control, branch mapping, native session routing, reply-all, output chaining, proactive one-to-one continuation, flags, secret redaction, persistence, delivery failure acknowledgement, and the circuit breaker.
The opt-in gateway test launches the installed OpenClaw executable with a deterministic local model and the in-memory fmsg Web API. It verifies the ready connection, root and branch session rows in OpenClaw's SQLite session store, first-child continuation, and reply-all:
npm run test:gateway
Set OPENCLAW_E2E_ROOT to an OpenClaw package directory when it is not available at node_modules/openclaw. Set OPENCLAW_E2E_PLUGIN_ROOT to exercise a particular installed copy of this package instead of the working tree.
The e2e test expects two provisioned identities in an already-running fmsg-docker environment:
FMSG_E2E=1 \
FMSG_E2E_AGENT_API_URL=http://localhost:8181 \
FMSG_E2E_AGENT_API_KEY='fmsgk_...' \
FMSG_E2E_PEER_API_URL=http://localhost:8182 \
FMSG_E2E_PEER_API_KEY='fmsgk_...' \
npm run test:e2e
The addresses are derived from the JWTs. The test sends a root with an attachment, observes WebSocket delivery, downloads the attachment, adds/changes/clears a reaction, replies through the second deployment, and verifies the reply's pid.
Pull requests and npm releases also run .github/scripts/run-fmsg-docker-e2e.sh, which provisions isolated real stacks at a pinned fmsg-docker revision before running this acceptance.
MIT
FAQs
OpenClaw channel plugin for the federated messaging protocol fmsg
The npm package @markmnl/openclaw-fmsg receives a total of 1 weekly downloads. As such, @markmnl/openclaw-fmsg popularity was classified as not popular.
We found that @markmnl/openclaw-fmsg demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Company News
Socket is joining the OpenJS Security Stewardship Program to fund Node.js vulnerability research, maintainer remediation, and security releases.

Security News
Two compromised GitHub Actions were re-enabled with malicious tags intact, exposing thousands of downstream repositories to Mini Shai-Hulud.

Research
/Security News
A malicious Firefox extension fetches its payload after installation to evade detection, steal Google session cookies, and automate account takeover.