DEV Community

Adnan Sattar
Adnan Sattar

Posted on • Originally published at Medium on

NemoClaw for the Enterprise: Matrix as the Communication Channel (Part 3)

The agent is alive in its cell. Now we give it a phone one that’s encrypted, allowlisted, and answers only to you.

In Part 2 we installed NemoClaw and bootstrapped the four-layer sandbox. The agent is reachable from the OpenClaw dashboard over Tailscale, gated behind mTLS, and isolated from the host. That’s enough for development. It’s not enough for the way most people will actually want to use this agent day-to-day: from their phone, from their laptop, from wherever they happen to be.

We need a chat channel.

The default candidates Telegram, Discord, WhatsApp, Slack share one disqualifying property i.e. the platform operator can read your messages. For a channel that’s about to carry high-privilege instructions to an autonomous agent (“delete the staging environment”, “transfer this file”, “post on my behalf”), that’s the wrong threat model. Matrix is the channel where the messages are end-to-end encrypted by default, the protocol is open, you can self-host if you want, and an enterprise audit trail is possible without trusting a third party.

This article gets a Matrix-controlled NemoClaw bot from zero to “type a message, get an answer” in about thirty minutes. By the end you’ll have:

  • A dedicated Matrix account for your bot, registered on matrix.org (or your own homeserver)
  • An access token authorising OpenClaw to act as that account
  • A network policy granting the sandbox exactly the access it needs to talk to Matrix and nothing more
  • An allowlist so only your Matrix ID can DM the bot
  • Verified end-to-end encryption between your client and the agent

The clean path is straightforward. The sharp edges are real but they’re in sidebars, not in your way.

What You’re Building

Three actors, one channel:

┌──────────────┐ E2EE ┌──────────────┐ ┌─────────────────┐
│ You │ ┌────────┐ │ matrix.org │ │ Your VPS │
│ Element on │──┤ Matrix │──│ homeserver │───┤ NemoClaw │
│ phone/laptop │ │ room │ │ (relays only │ │ sandbox │
└──────────────┘ └────────┘ │ ciphertext) │ │ (decrypts here) │
                              └──────────────┘ └─────────────────┘
Enter fullscreen mode Exit fullscreen mode

You and the bot share an end-to-end-encrypted room. Your homeserver and the bot’s homeserver — they can be the same (matrix.org) or different (matrix.org ↔ self-hosted Synapse) — relay encrypted bytes between you. Neither homeserver can read the content. The bot's plaintext only ever exists inside two places: your Element client and the NemoClaw sandbox itself.

If that property doesn’t matter to your threat model, stop reading and use Telegram. If it does, keep going.


e2ee secure communication chain

Step 1: Create the Bot’s Matrix Account

You can either register a fresh account on matrix.org (free, easy) or on your own self-hosted Synapse. The setup is identical from here on; pick whichever matches your existing posture.

For matrix.org:

  1. Open https://account.matrix.org in a browser
  2. Click Create account
  3. Choose a username for the bot — something descriptive likeagent-orion-bot, nightowl-bot. This becomes the bot's Matrix ID: @nightowl-bot:matrix.org
  4. Use a real email you can verify — matrix.org requires it
  5. Set a strong password, save it to a password manager, then forget you ever typed it (we’ll authenticate via access token from here on)

Sidebar** matrix.org migrated to OIDC.** As of late 2025, account registration and login on matrix.org runs through OpenID Connect (MSC3861). You'll be bounced through a federated login flow rather than the old "username + password" page. This is fine for humans. It matters when you start scripting against the API see Step 2.

Once registered, open Element Web (https://app.element.io) and log in as the bot. Set a display name and avatar so messages from the bot look like messages, not like infrastructure. This is a one-time human-friendliness step.


Create the Bot’s Matrix Account

Step 2: Get an Access Token

OpenClaw authenticates against Matrix using an access token rather than a password. The token represents a single device session if it leaks, you revoke that device and the rest of your account is unaffected.

In Element Web, logged in as the bot:

  1. SettingsHelp & About
  2. Scroll to Advanced
  3. Click the disclosure triangle next to Access Token
  4. Copy the long string starting with syt_…

Keep it on the clipboard. Treat it like an SSH key.

Sidebar: Why not generate the token programmatically? On a pre-OIDC homeserver, you’d POST /_matrix/client/v3/login with a username and password and get a token back. On matrix.org post-MSC3861, that endpoint returns errors for accounts created through the OIDC flow. Element's settings panel sidesteps the issue by exposing the token already minted by your interactive login. For a single bot, that's plenty. If you're at the scale of "many bots, automated provisioning", run your own Synapse where the legacy login API still works the way you'd expect.


Matrix Get an Access Token

Step 3: Allow the Sandbox to Reach Matrix

By default, the NemoClaw policy denies the sandbox almost all outbound traffic — including matrix.org. The bot will silently fail to connect until you write a policy that allows it.

On the host:

NEMOCLAW_POLICIES="$(npm root -g)/nemoclaw/nemoclaw-blueprint/policies"
nano $NEMOCLAW_POLICIES/openclaw-sandbox.yaml
Enter fullscreen mode Exit fullscreen mode

Add the following entry under the existing network_policies: block (don't replace the file — append):

matrix:
    name: matrix
    endpoints:
      - host: matrix-client.matrix.org
        port: 443
        protocol: rest
        tls: terminate
        enforcement: enforce
        rules:
          - allow: { method: GET, path: "/**" }
          - allow: { method: POST, path: "/**" }
          - allow: { method: PUT, path: "/**" }
      - host: matrix.org
        port: 443
    binaries:
      - { path: /usr/bin/node }
Enter fullscreen mode Exit fullscreen mode

Apply the updated policy:

openshell policy set --policy $NEMOCLAW_POLICIES/openclaw-sandbox.yaml nemoclaw-sandbox
Enter fullscreen mode Exit fullscreen mode

Verify it landed:

nemoclaw nemoclaw-sandbox policy-list
Enter fullscreen mode Exit fullscreen mode

You should see matrix in the list. If you're self-hosting Synapse, swap matrix-client.matrix.org and matrix.org for your own homeserver hostnames — the policy structure is identical.

Sidebar:** openshell policy set replaces the entire policy.** This is not a merge command. Always edit the full policy file and reapply it. If you cat only a partial policy and apply that, you've just removed every other endpoint your agent depended on. Edit; don't shard.


Sandbox Trafic Reach Matrix

Step 4: Configure OpenClaw

Open the OpenClaw dashboard at http://:18789 and navigate to SettingsConfig.

Find the channels block (or add one if it doesn't exist) and configure matrix:

channels: {
  matrix: {
    enabled: true,
    homeserverUrl: 'https://matrix-client.matrix.org',
    userId: '@chip1-bot:matrix.org',
    accessToken: 'syt_xxxxxxxxxxxxxxxxxxxxxxxx',
    e2ee: true,
    dmPolicy: 'allowlist',
    allowFrom: [
      '@you:matrix.org',
    ],
    streaming: 'partial',
  },
},
Enter fullscreen mode Exit fullscreen mode

Replace the four placeholders: homeserverUrl (your homeserver — leave as-is for matrix.org), userId (the bot's full Matrix ID), accessToken (the syt_… string from Step 2), and allowFrom (your own Matrix ID — this is who's allowed to DM the bot).

Save the config.

e2ee: true is the only setting that matters for security. Don't ever flip it to false "just to test" — the bot's device keys get generated on first run, and switching encryption modes later forces a device-key rotation that you do not want to debug. Set it once, on.

streaming: 'partial' makes the bot post incremental responses as the agent generates them, which feels conversational rather than "send message → wait 30 seconds → wall of text". 'full' waits for completion and posts once.

Restart NemoClaw’s auxiliary services so the Matrix bridge picks up the new config:

nemoclaw stop
nemoclaw start
Enter fullscreen mode Exit fullscreen mode


OpenClaw e2ee Communication Channel Configure

Step 5: Verify the Bot Is Online

From your own Matrix account (Element on phone or web), start a new direct message to the bot’s Matrix ID. Send hello.

Within a few seconds you should see:

  • A reply from the bot
  • A shield icon on the room indicating end-to-end encryption is active
  • A device-verification prompt (one-time, see below)

If nothing happens within thirty seconds, check the logs:

nemoclaw nemoclaw-sandbox logs --follow | grep -i matrix
Enter fullscreen mode Exit fullscreen mode

Common failure modes and what they mean:


Common failure modes


Verify the e2ee Bot Is Online

Step 6: Verify End-to-End Encryption

E2EE on Matrix only protects you if you actually verify the other device’s keys. Without verification, the room is encrypted but vulnerable to a homeserver-side key swap that you wouldn’t notice.

In Element, in the room with the bot:

  1. Click the room name → People
  2. Click the bot’s name
  3. Click Verify
  4. Compare the emoji sequence shown in Element with the emoji sequence printed by the bot in the room
  5. If they match, confirm

The shield icon should now turn from grey (“encrypted, unverified”) to green (“encrypted, verified”). This is a one-time step per device pair.

If verification fails or the bot never sends the emoji message, the device is in a confused state — see the next sidebar.

A Known Wart: Stale Devices

If you’ve logged into the bot account before — to test, to check something, to set the display name — every login created a device (a session key). Matrix tracks these per-account, and if the bot tries to send an encrypted message while another stale device holds conflicting crypto keys, E2EE breaks.

You will see one of:

  • The bot replies but messages are flagged “unable to decrypt” on your end
  • The bot logs an error like OlmSessionError: no matching session
  • Device verification (Step 6) loops forever

Recovery is mechanical but slightly painful, because matrix.org has neither a "delete all devices" button nor a working bulk-delete API. The endpoints DELETE /_matrix/client/v3/devices/{deviceId} and the older /logout/all both return M_UNRECOGNIZED for OIDC-managed accounts.

The path that actually works:

  1. Go to https://account.matrix.org/account/
  2. Sign in as the bot
  3. Sessions → review every active session
  4. For each one that isn’t the current OpenClaw bot session: click the session, then Sign out
  5. If you can’t tell which one is OpenClaw’s, sign out of all of them, restart nemoclaw stop && nemoclaw start, let the bot create a fresh device, and then verify that single device with Element

If the dashboard refuses to let you delete a specific stale device (it sometimes does, depending on which device created the others), the working trick is to log in as that stale device from a fresh Element session — using the bot’s password — and then Sign out from within. The device gets purged server-side because you’re inside its own session.

This is the one place in Part 3 where the path is genuinely uglier than Matrix’s marketing implies. Do the cleanup once, verify once, and you won’t revisit it.


Matrix Stale Devices

Verification Checklist

Before moving on to Part 4:

  • The bot account exists at @yourbot:matrix.org (or your homeserver) and is reachable in Element
  • matrix-client.matrix.org appears in the sandbox's network policy
  • nemoclaw nemoclaw-sandbox policy-list shows matrix as applied
  • From your own Matrix account, DM’ing the bot returns a response within 5 seconds
  • The room shows a green shield icon (encrypted and verified)
  • DMs from any Matrix ID not on the allowFrom list are silently ignored — test by asking a friend to message the bot and confirm nothing happens
  • nemoclaw nemoclaw-sandbox logs shows no recurring M_FORBIDDEN or device-key errors

Where You Are Now

You have an autonomous AI agent reachable from any Matrix client, anywhere, over an end-to-end encrypted channel that you’ve verified. Only your Matrix ID can talk to it. The sandbox can reach matrix.org and nothing else relevant on the public internet. The host has no public ports open.

This is the configuration most consumer agents simply don’t offer. ChatGPT-on-iOS reads your messages; so does any LLM-backed Discord bot, Slack bot, or Telegram bot. The plaintext lives somewhere outside your control. With this setup, plaintext lives in exactly two places your phone and your sandbox and the path between them is bytes you’ve cryptographically verified.

The threat model that remains: anyone who compromises your Matrix account can talk to your agent. Hardware-level account security (FIDO2 on Element, account-recovery key offline) is now load-bearing in a way it wasn’t before. This is the right place for security to live, because it’s the same security perimeter you already protect for everything else important in your digital life.


Encrypted AI communication

What’s Next

Part 4. Policy Engineering. Your agent can now receive instructions over an encrypted channel. The next question is what the agent is actually allowed to do once it receives them. OpenShell’s policy engine is the reason NemoClaw exists rather than just running OpenClaw directly — we’ll write per-domain network policies, set up filesystem allowlists, walk through live policy updates with openshell policy set --wait, and look at how policy revisions work as an audit trail.

Part 5. Skills, Plugins, and Model Switching. The agent currently has the empty-shell capabilities OpenClaw ships with. We’ll install skills from ClawHub safely (the docker-cp-then-kubectl-cp pattern), enable plugins from inside the sandbox, and swap between Nemotron and Claude Sonnet without a restart.


Next AI agent with policy boundaries

I’m collecting Matrix deployment stories for the Part 4 appendix. If you hit a homeserver-specific quirk, an Element verification edge case, or a federation issue I didn’t cover — drop the details in the comments. Every reader who shares makes the next article sharper.

Top comments (0)