All plugins

Official

xchat

X Chat gateway platform: talk to Hermes in X's end-to-end encrypted DMs. Hermes runs as the X account you connect: chat with it in your own self-chat ("message yourself"), or give it a separate account and DM that. Messages are decrypted and encrypted locally with X's official Chat XDK, so X only routes ciphertext. Encrypted media, threaded replies, group mention gating and cron delivery.

NousResearch676Platformv0.1.0Updated 7 days ago

About

X Chat gateway platform: talk to Hermes in X's end-to-end encrypted DMs. Hermes runs as the X account you connect: chat with it in your own self-chat ("message yourself"), or give it a separate account and DM that. Messages are decrypted and encrypted locally with X's official Chat XDK, so X only routes ciphertext. Encrypted media, threaded replies, group mention gating and cron delivery.

X Chat (encrypted X DMs) for Hermes Agent

Part of the Hermes Official Plugins, Nous Research's official plugins for Hermes Agent.

X Chat is X's end-to-end encrypted direct-message system. The Hermes adapter connects your agent to one X account's DMs: message bodies are encrypted and decrypted locally with the official Chat XDK — X only ever routes ciphertext, and every message is signed so recipients can verify the sender.

Which X account does Hermes use?

Hermes runs as the X account you connect during setup. It reads that account's encrypted DMs and its replies are sent from that account. Pick one of two setups:

SetupHow you talk to HermesWho else can reach it
Your own accountOpen your chat with yourself on X ("message yourself") and type there. Hermes answers in the same chat.Anyone who DMs you and is on XCHAT_ALLOWED_USERS (or approved by pairing) gets answers from Hermes, sent as you.
A separate account for the agent (e.g. @yourname_hermes)DM that account from your personal account, like any other chat.Whoever you allow. Your personal DMs are never touched.

The self-chat works out of the box: hermes xchat setup adds the connected account to XCHAT_ALLOWED_USERS, and Hermes never answers its own replies. Turn it off with XCHAT_ALLOW_SELF_CHAT=false. Messages you send from your own account in your other conversations are never treated as prompts.

Setup registers a new encryption key on the connected account, so try it on a separate account first if you are unsure.

Install

From the Hermes plugin catalog:

hermes plugins install xchat

Or straight from this repo (tracks main):

hermes plugins install NousResearch/hermes-official-plugins#xchat

The install asks once before preparing the plugin's Python dependencies: chatxdk (X's official
Chat XDK, a native E2EE binding, pinned to 0.4.1) and httpx. Needs Hermes Agent 0.21.5 or newer.

Then run hermes xchat setup for a guided walk-through, or pick X Chat in hermes gateway setup.

Prerequisites

  • An X developer account with an app configured for OAuth 2.0 user context (Developer Console). X Chat endpoints require API access on your developer plan.
  • A user access token for the account Hermes should run as (see above) with scopes: dm.read, dm.write, users.read, tweet.read (add offline.access to receive a refresh token so Hermes can auto-renew the ~2-hour access token, and media.write to send encrypted file/image attachments).

Setup

hermes xchat setup

The wizard:

  1. Stores the OAuth2 access token (and optional refresh token + client id) in ~/.hermes/.env.
  2. Shows which account Hermes will run as (@handle (id), from GET /2/users/me) and adds that account to XCHAT_ALLOWED_USERS so your self-chat works.
  3. Generates the E2EE identity + signing keypairs with the Chat XDK, saves the private-key blob to ~/.hermes/xchat/private_keys.b64 (mode 600), and registers the public keys with the X API.

Key registration is rate limited to a few writes per 24 hours per account. The setup is resume-safe: the key blob and registration payload are persisted before any network call, so an interrupted or rate-limited run resumes the same identity instead of minting a new one.

Check state anytime:

hermes xchat status

Environment variables

VariableRequiredDescription
XCHAT_ACCESS_TOKENYesOAuth2 user access token for the account Hermes runs as (dm.read, dm.write, users.read, tweet.read)
XCHAT_REFRESH_TOKENRecommendedRefresh token — enables automatic access-token renewal (rotated on every refresh and re-persisted)
XCHAT_CLIENT_IDWith refreshX app OAuth2 client id (required for token refresh)
XCHAT_CLIENT_SECRETOptionalOnly for confidential clients
XCHAT_USER_IDAutoNumeric id of the account Hermes runs as (derived by setup)
XCHAT_SIGNING_KEY_VERSIONAutoRegistered public-key version (written by setup)
XCHAT_PRIVATE_KEYS_B64OptionalKey blob override — takes precedence over the blob file. ⚠️ This is the account's entire E2EE identity for Hermes: there is no forward secrecy in this protocol, so anyone holding the blob can decrypt all past and future messages. Keep it in ~/.hermes/.env (never commit it, never pass it on a command line)
XCHAT_ALLOW_SELF_CHATOptionalfalse stops Hermes answering your self-chat (default true)
XCHAT_ALLOWED_USERSRecommendedComma-separated numeric X user ids allowed to talk to Hermes; setup adds the connected account itself
XCHAT_ALLOW_ALL_USERSOptionaltrue allows every sender (dev only)
XCHAT_CONVERSATION_IDSOptionalPin specific conversation ids to poll; omit to auto-discover
XCHAT_POLL_INTERVALOptionalSeconds between event polls (default 10, floor 2)
XCHAT_SEND_READ_RECEIPTSOptionaltrue sends read receipts for processed messages (default false)
XCHAT_REQUIRE_MENTIONOptionalIn group conversations, only respond when a wake word matches (default false)
XCHAT_MENTION_PATTERNSOptionalCustom wake-word regexes (JSON list or comma-separated)
XCHAT_HOME_CHANNELOptionalDefault conversation/user id for cron delivery
XCHAT_HOME_CHANNEL_NAMEOptionalHuman label for the home channel

How it works

  • Inbound — each poll cycle lists the bot's conversations once (with each one's newest event id) and reads the events endpoint only for conversations that have something newer than the bot's persistent per-conversation cursor (~/.hermes/xchat/cursors.json), so idle conversations cost nothing. For a conversation that already existed when the gateway first started, it batch-decrypts the backlog (decrypt_events) to seed the SDK's verified conversation-key cache without replying to old messages; a conversation someone opens while the gateway is running is answered from its first message; after that every new event (including bursts larger than one page, and messages that arrived while the gateway was down) is processed exactly once. KeyChange events (conversation-key rotations) are verified and folded into the key cache automatically; message edits are treated as new messages.
  • Outbound — replies are encrypted and signed locally (encrypt_message with the session identity), then POSTed as ciphertext. Replies to a specific message use the native threaded-reply event when the target is in the adapter's recent-event cache; inbound threaded replies reach the agent with the quoted message.
  • Media — both directions are fully encrypted. Inbound attachments are downloaded, decrypted with the conversation key for the event's key version, and cached locally so vision/file tools can read them. The download is read under the gateway's inbound-media cap (gateway.max_inbound_media_bytes) as it streams, so an oversized attachment is refused while it is read rather than after it has been held in memory. Outbound MEDIA:<path> files (images, voice notes, videos, documents) are encrypted with the latest conversation key, uploaded through the 3-step chat-media flow, and attached by media_hash_key (requires the media.write scope).
  • Senders — each new sender's public keys are fetched once and pushed into the XDK's signing-key store so their message signatures verify.
  • New conversations — standalone sends (hermes send xchat:<user-id>, cron delivery) to a bare numeric user id perform the conversation-key handshake automatically: both parties' key bindings are verified, a fresh conversation key is wrapped for each participant, and the key change is POSTed before the first message.
  • Identity — user ids are numeric X user ids; conversation ids look like 123-456 (1:1) or g123… (group).

Authorization

By default only the connected account itself (its self-chat) is allowed; every other sender is denied. To let others in, either:

  1. Add their numeric X user ids to XCHAT_ALLOWED_USERS (comma-separated), or
  2. Use DM pairing — an unknown sender gets a pairing code; approve with hermes pairing approve xchat <CODE>.

An unknown sender gets a pairing code by default. On your personal account that code is sent from you, so to ignore strangers silently instead, set it in config.yaml:

platforms:
  xchat:
    unauthorized_dm_behavior: ignore

Group conversations

Group chats (g… conversation ids) work out of the box. To keep the bot quiet unless addressed:

XCHAT_REQUIRE_MENTION=true

The default wake words are hermes / hermes agent; override with XCHAT_MENTION_PATTERNS. DMs are never gated.

Using X Chat with cron jobs

cronjob(
    action="create",
    schedule="every 1h",
    deliver="xchat",            # uses XCHAT_HOME_CHANNEL
    prompt="Check for alerts and summarise."
)

Or target a conversation directly:

hermes send xchat:<conversation-id> "Done!"

Standalone delivery opens an ephemeral E2EE session, seeds the conversation key from the conversation's backlog, encrypts, and sends — no running gateway required.

Limitations

  • Polling latency. Inbound uses REST polling (default 10s). Webhook / activity-stream delivery may come later.
  • Group creation. The bot participates in existing group conversations but does not create new groups or manage membership.
  • Access tier. X Chat API availability depends on your X developer plan; media upload additionally requires the media.write scope.

README from the reviewed commit e7a218f; it updates when the author re-pins.

More by NousResearch

29 plugins by NousResearch in the catalog.

NousResearchaccent-pickerOKLCH accent colour picker for the Hermes Desktop status bar: drag it and the whole theme retints live, with palette commands to reset to the theme default or copy the current colour. A theme-authoring tool; the colour is scratch state and is never persisted. Desktop-only (desktop/plugin.js inside the plugin SDK surface); no agent tools, hooks, middleware or env vars.OfficialDesktop app676NousResearchadvanced-delegationTwo extra delegation modes for delegate_task, next to the built-in default. Model routes (delegation.backend: model_map): the agent picks a named model route per subagent from a list you define (e.g. fast, deep, local). Profile routes (delegation.backend: profile_routes): the agent runs a subagent as one of your Hermes profiles, with that profile's own model, credentials, skills and SOUL.md. Only route names and their one-line descriptions reach the model. Adds hermes delegation config (pick the mode and edit its routes; --show prints the setup) and the /delegation slash command (save a mode for the next session, or --now to apply it to the current chat). Needs Hermes 0.22.5 or newer, the first release with pluggable delegation providers; Hermes 0.22.0 to 0.22.4 refuses to install it, and on a core older than 0.22.0 it installs, logs one warning and does nothing. A model route can set daybreak: true to run its model with OpenAI's Daybreak trusted-access program; it is sent only from your own CLI, TUI or Desktop chats, on openai-codex, for models your Codex catalog lists, and a route that breaks a rule is refused. Disclosure — no Python dependencies and no network or model calls of its own, except that a Daybreak route checks the Codex catalog through core's cached lookup with your existing Codex login; subagents run through core's delegate_task on the routes you configured. Writes only delegation.backend and delegation.backends.* (model_map / profile_routes) in the active profile's config.yaml, through core's config writer. A model route's optional api_key is stored in config.yaml in plain text (provider: keeps keys in .env / auth.json). Listing a profile under profile routes lets this profile's subagents use that profile's credentials, SOUL.md and terminal policy.Officialtools676NousResearchagent-importershermes import-agent: import an OpenClaw (~/.openclaw, or legacy ~/.clawdbot / ~/.moltbot), Claude Code (~/.claude), Codex CLI (~/.codex), Cursor (~/.cursor), Grok Bot (a self-export the Bot writes from a prompt the importer prints) or OpenBot desktop (its openbot.db) setup into Hermes in one command; always previews first and asks before applying. Claude Code / Codex / Cursor: instructions, Cursor rules and memories become memory entries, Bash permission rules become command allow/deny lists, MCP servers land in config.yaml, skills are copied, and --sync re-imports sources that changed. OpenClaw (core's former hermes claw migrate; the plugin keeps that spelling, hermes claw cleanup and the openclaw-migration skill): persona, memories, skills, model and messaging settings, MCP servers and agent behaviour. Formerly built into Hermes core; hermes setup offers to install it when it finds one of these agents, and profiles with a registered sync source are migrated automatically. Disclosure — no Python dependencies, no network calls and no model calls. Claude Code / Codex / Cursor: reads those agents' local config trees (never their credential files; secret-looking MCP env vars and headers are stripped and listed) and writes only into the active profile (memories/MEMORY.md with a backup, config.yaml, skills/ and import-sync.json). OpenClaw: reads the OpenClaw dir (openclaw.json, its .env, workspace files such as SOUL.md / MEMORY.md / USER.md, skills); API keys and bot tokens come over only with --migrate-secrets (allowlisted names, shown in the preview). It writes into the active profile (config.yaml, .env, SOUL.md, memories/, skills/openclaw-imports/, a migration/openclaw/<time>/ report with item backups, and a backups/pre-migration-*.zip restore point), plus a workspace dir only when --workspace-target names one. It runs local pgrep / systemctl / tasklist checks to warn when OpenClaw or a Hermes gateway is still running, and hermes claw cleanup renames the OpenClaw dir to *.pre-migration after a confirmation. Grok Bot / OpenBot: instructions become SOUL.md, memories and user facts become memory entries, skills land in skills/<source>-imports/, scheduled routines become paused cron jobs (--activate-routines to start them) and event routines a notes file; credential-looking export keys refuse the file, and secret MCP env vars and headers are stripped and listed. OpenBot's database is opened read-only (or read from a scratch copy that is deleted after the read).Officialtools676NousResearchblenderOfficial Blender Lab MCP server over stdio, pinned to upstream v1.0.3, with a Blender workflow skill. Needs Git and uv; Blender 5.1+ is not needed at install. The skill opens Blender itself when a task needs it and, after you agree, installs Blender and Blender Lab's MCP add-on. First startup downloads Python dependencies; prepare the uv environment before connecting. Disclosure — tools execute arbitrary Python in Blender with your privileges; the skill starts Blender with online access for that session only (your saved preference is unchanged), and the add-on's local server on port 9876 has no password, so any program on this computer can use it while Blender is open; only after you agree does the skill download Blender from download.blender.org (about 400 MB, about 1.5 GB unpacked), check it against blender.org's sha256 list and install it in your own folder without administrator rights (Linux ~/.local/opt, macOS ~/Applications, Windows %LOCALAPPDATA%\Programs\Blender Foundation; never over an existing Blender), and download the v1.0.3 add-on, check its hash and install or enable it; first start downloads the pinned Blender Lab MCP server and its Python dependencies with uv. Server discovery does not prove scene readiness.Officialtools676

Developer Mode

Explore built-in Hermes toolsets and actions.

HermesAgent

Developer?

Create a Hermes extension for Desktop or agent workflows.

Developer Section