Skip to main content

fmsg-platform

❖ Community★ 2

fmsg platform adapter for Hermes Agent. Real-time inbound over the fmsg-webapi WebSocket (/fmsg/ws), outbound via its JSON REST routes. Authenticates with an fmsg API key (fmsgk_...) exchanged for a short-lived JWT — run the agent as a derived sub-account such as @alice_hermes@example.com. Only http

Open in Hermes Desktop
hermes plugins install fmsg-platform

What it adds

Environment variables it needs 1

name

README

From the reviewed commit 562f45a ↗; it updates when the author re-pins.

HERMES FMSG

fmsg platform plugin for Hermes Agent — give your Hermes agent its own address and durable, threaded conversations.

An fmsg thread is also the agent's conversation context: roots map to Hermes sessions, linear replies continue them, and later branches get their own session with direct ancestry restored as context.

hermes plugins install markmnl/hermes-fmsg --enable

Why fmsg for an agent?

  • A durable agent identity: people and other agents can contact a stable @agent@domain address without sharing a human login.
  • Context-native threads: fmsg's message tree maps to Hermes sessions and branch context instead of flattening every conversation into one chat.
  • Offline-safe delivery: WebSocket push is backed by inbox catch-up after a restart or network interruption.
  • Revocable access: an agent uses its own expiring API key, exchanged for short-lived JWTs, rather than a human password or persistent user token.
  • Federated reach: addresses on independently operated fmsg hosts can communicate without joining the same messaging silo.
  • Self Hostable: self-host an fmsg host on your domain and choose storage, quotas, retention, and policy without per-message platform metering. Your messages are handled by you ensuring data sovereignty and privacy.
  • Rich workflows: text, attachments, reply-all, cron delivery, and agent-to-agent messaging use the same address.

Status

v0.2.3 The core message, attachment, reconnect, auth, threading, and branching paths are implemented and unit tested. Install now prompts for home channel + allowlist, and seeds the allowlist from FMSG_HOME_CHANNEL when the allowlist is left empty. The plugin is a standalone community integration, not bundled with Hermes Agent.

Supported and continuously tested with Hermes Agent v0.19 through v0.21. Please report compatibility problems with your Hermes version and operating system.

Quickstart

1. Get an agent address and API key

Choose either route:

  • Hosted: use an fmsg service provider, create an agent/sub-account under your user address, and copy its one-time API key. You also need the provider's fmsg Web API URL.

fmsg.io provides accounts with up to 5 sub-accounts for free.

  • Self-hosted: deploy the open-source fmsg-docker stack, then create a derived sub-account and API key through its Web API or CLI.

Use a dedicated agent identity such as @alice_hermes@example.com. Do not give the plugin a human password or a user's identity-provider token.

2. Install with Hermes

hermes plugins install markmnl/hermes-fmsg --enable

The Hermes installer prompts for:

  • FMSG_API_URL — the base URL of the host's fmsg Web API; the plugin uses https://api.fmsg.io when it is left unset.
  • FMSG_API_KEY — the agent's fmsgk_... API key.
  • FMSG_HOME_CHANNEL — your owner fmsg address (cron / notifications; also used as the default allowed sender if the allowlist is empty).
  • FMSG_ALLOWED_USERS — comma-separated addresses allowed to message the agent (use your address at minimum).

Hermes only prompts variables listed under requires_env. Empty answers are still skipped, so the plugin also seeds FMSG_ALLOWED_USERS from FMSG_HOME_CHANNEL at gateway start when the allowlist is unset (see below). Hermes skips an install prompt when the variable is already present in either the current process environment or ~/.hermes/.env.

The repository root is a native Hermes plugin package; cloning or copying files manually is not required.

3. Configure the environment

Here are the FMSG_ env vars this plugin can use:

# Required (prompted at install)
FMSG_API_KEY=fmsgk_<key_id>_<secret>
FMSG_API_URL=https://api.fmsg.io
FMSG_HOME_CHANNEL=@alice@example.com
FMSG_ALLOWED_USERS=@alice@example.com

# Optional
FMSG_ALLOW_ALL_USERS=false
FMSG_HOME_CHANNEL_NAME=Alice
FMSG_DEFAULT_TOPIC=Hermes

Access control defaults to deny. At least one trusted address is required for inbound replies:

  • Prefer setting both FMSG_HOME_CHANNEL and FMSG_ALLOWED_USERS at install.
  • If FMSG_ALLOWED_USERS is empty, FMSG_ALLOW_ALL_USERS is off, and FMSG_HOME_CHANNEL is set, the plugin copies the home channel into the allowlist for the gateway process and logs a warning.
  • If none of those are set, every inbound sender is rejected (Unauthorized user: ... on fmsg in the gateway log).
  • FMSG_ALLOW_ALL_USERS=true bypasses the allowlist and is intended only for isolated development.

FMSG_HOME_CHANNEL should normally be the plugin owner's own fmsg address. Environment variables take precedence over platforms.fmsg.extra in ~/.hermes/config.yaml.

4. Start or restart the gateway

hermes gateway restart
hermes gateway status
hermes plugins list --plain --no-bundled

The plugin list should show fmsg-platform enabled and the gateway log should show fmsg connected — streaming from …. Send a message to the agent's address to open its first Hermes session.

Sending messages from Hermes

The sending identity is the fmsg sub-account bound to FMSG_API_KEY. The plugin obtains that address from the exchanged JWT and supplies it as the message's from address; do not configure a separate sender address.

With FMSG_HOME_CHANNEL set, an agent or shell command can send to that address using the platform-only target:

hermes send --to fmsg "whats up"

Replies in an existing fmsg conversation automatically use that conversation's address and thread. Code calling the plugin sender directly must pass the recipient's complete fmsg address, such as @alice@example.com, as chat_id without an fmsg: prefix.

Use these integration paths:

  • Current conversation: reply normally; the live adapter preserves the fmsg parent, recipients, and Hermes session.
  • Configured home channel: use hermes send --to fmsg "message" or the Hermes send_message tool with target fmsg.
  • Cron delivery: configure FMSG_HOME_CHANNEL; Hermes invokes the plugin's registered standalone sender when it is outside the gateway process.
  • Application code inside Hermes: call the platform adapter's send method with the complete address as chat_id.

In all of these paths the plugin owns API-key exchange, sender identity, threading, attachments, and error handling. The Hermes agent should not read FMSG_API_KEY, manage JWTs, or construct fmsg drafts itself.

How conversations map to Hermes

fmsg Hermes
counterparty address @user@domain chat and authenticated user identity
root message new session keyed by root message ID
first child path continuation of the root session
later sibling branch new {root}:br:{message} session with direct ancestry context
short_text or message data inbound prompt text
attachments cached inbound media or outbound fmsg attachments
important / no_reply event metadata and model context
parent participants reply-all recipients for that parent message
read route receipt after Hermes dispatches the message

Multiple Hermes replies in one turn form a chain: the first replies to the latest inbound message, and each subsequent message replies to the previous outbound message. A later inbound message resets that chain.

Agent-initiated messages without an explicit thread continue the most recent one-to-one conversation with that address. Set metadata fmsg_new_thread=true to force a new root, with an optional topic override. Multi-party conversations are never selected for automatic continuation.

Reply-all behavior

A reply includes every participant on the specific parent message (from, to, and add_to), excluding the agent. Exceptional callers can select a subset with fmsg_to / recipients, or set fmsg_reply_all=false for the counterparty only.

Security notes

  • Review any Hermes plugin before installation; plugins execute inside the Hermes process.
  • Keep the fmsg API key in ~/.hermes/.env, never in a repository or issue.
  • Restrict the key to expected CIDRs where the host supports it.
  • Prefer short expiry periods and rotate or revoke a key immediately if it is exposed.
  • Keep FMSG_ALLOWED_USERS narrow. Do not enable FMSG_ALLOW_ALL_USERS on an Internet-connected agent.

Please report suspected vulnerabilities privately as described in SECURITY.md.

Troubleshooting

hermes --version
hermes plugins list --plain --no-bundled
hermes gateway status
hermes logs --help

Common checks:

  • Plugin is disabled: rerun the install with --enable, or use hermes plugins enable fmsg-platform.
  • Missing dependency: install httpx and websockets in the Python environment used by Hermes.
  • API key rejected: confirm it is unexpired, unrevoked, allowed from the gateway's IP, and belongs to the intended agent address.
  • Messages ignored / Unauthorized user in gateway log: add the sender to FMSG_ALLOWED_USERS (or set FMSG_HOME_CHANNEL so the plugin can seed the allowlist) and restart the gateway. Empty allowlist is default-deny.
  • Outbound works, inbound does not: hermes send uses the standalone sender and does not need a running gateway. Inbound replies need hermes gateway with fmsg connected in the log.
  • Connection drops: the adapter reconnects with backoff and catches up from the inbox. Include sanitized fmsg lines from the gateway log in a bug report.

Never post API keys, JWTs, private addresses, or unredacted URLs containing credentials.

Updating and uninstalling

hermes plugins update fmsg-platform
hermes plugins disable fmsg-platform
hermes plugins remove fmsg-platform

Consult hermes plugins --help if command names differ on an older Hermes release.

Development

python3 -m venv .venv
.venv/bin/pip install -e ".[dev]"
.venv/bin/python -m pytest tests/ -v

Unit tests use an in-memory fake fmsg Web API and stub only the Hermes gateway surface needed by the adapter.

The legacy developer copy target remains available:

make install

End-to-end test

The opt-in test exchanges a threaded round trip between two real API-key identities:

FMSG_E2E=1 \
FMSG_E2E_API_URL=http://127.0.0.1:8000 \
FMSG_E2E_API_KEY=fmsgk_... \
FMSG_E2E_PEER_KEY=fmsgk_... \
.venv/bin/python -m pytest tests/e2e/ -v

It requires a running fmsg Web API with the fmsgd base schema and the Web API schema applied. The fmsg-docker stack provides the full integration environment.

← Back to the catalog · catalog built Oct 8, 2026