This guide explains Manifold's built-in Matrix gateway for people who have never used Matrix before.
Matrix is an open chat protocol. You can think of it as a federated chat network, similar in spirit to email:
- a Matrix
homeserveris the server that hosts accounts and rooms - a Matrix
user IDidentifies an account, for example@manifold:matrix.example.com - a Matrix
roomis a shared chat space, similar to a channel or group chat - a Matrix
room IDis the internal identifier for a room, for example!abc123:matrix.example.com
Manifold's Matrix gateway lets the Manifold server act as a Matrix chat participant. When someone sends a message in a configured room, Manifold can route that message to the orchestrator or to a named specialist and send the reply back into the room.
When the gateway is enabled, Manifold:
- connects to your Matrix homeserver using a Matrix account and access token
- long-polls Matrix for new room messages
- watches only the rooms you explicitly list in
config.yaml - routes matching messages to Manifold's orchestrator or to a named specialist
- posts the final reply back into the same Matrix room
The gateway is useful when you want people to talk to Manifold from an ordinary chat room instead of from Manifold's web UI.
The current implementation is intentionally narrow. End users should know these limits up front:
- It only watches rooms listed in
matrix.rooms. - It expects the real Matrix room ID, not a room alias or room name.
- It only routes plain Matrix message events.
- It ignores messages sent by the gateway account itself.
- It does not provide a dedicated Matrix room browser in the Manifold UI yet.
- Matrix-driven conversations currently show up indirectly through Manifold's normal chat/session storage.
- End-to-end encrypted Matrix rooms are not supported by this gateway.
deviceID,systemPromptRef, andmaxConcurrentexist in config today, but are not wired into runtime behavior yet.
You need four things:
- A working Matrix homeserver URL, such as
https://matrix.example.com. - A Matrix account for Manifold, such as
@manifold:matrix.example.com. - An access token for that Matrix account.
- The internal room ID for each room Manifold should watch.
If you are new to Matrix, the most confusing part is usually the room ID. The gateway needs the room's internal ID, which usually starts with !.
Examples:
- Valid room ID:
!abc123:matrix.example.com - Not valid for this config:
#engineering:matrix.example.com - Not valid for this config:
engineering
How you get a token depends on your homeserver and client, but one common path is to log in through the Matrix client API.
Example:
curl -X POST "https://matrix.example.com/_matrix/client/v3/login" \
-H "Content-Type: application/json" \
-d '{
"type": "m.login.password",
"identifier": {
"type": "m.id.user",
"user": "manifold"
},
"password": "your-password",
"initial_device_display_name": "Manifold Gateway"
}'That response usually includes:
access_tokenuser_iddevice_id
For Manifold today, the important value is access_token.
Store the token in .env, not directly in config.yaml, for example:
MATRIX_ACCESS_TOKEN="your_real_matrix_access_token"If your Matrix client does not show the room ID directly, you can ask the homeserver which rooms the gateway account has joined.
Example:
curl -s \
-H "Authorization: Bearer $MATRIX_ACCESS_TOKEN" \
"https://matrix.example.com/_matrix/client/v3/joined_rooms"That returns a list of joined room IDs. Pick the room you want and place that exact value into matrix.rooms[].roomID.
The Matrix gateway lives under the matrix: section in config.yaml.
Minimal example:
matrix:
enabled: true
homeserverURL: https://matrix.example.com
userID: "@manifold:matrix.example.com"
accessToken: "${MATRIX_ACCESS_TOKEN}"
deviceID: manifold-agentd
syncTimeoutSeconds: 30
syncRetryDelaySeconds: 3
processBacklog: false
rooms:
- roomID: "!abc123:matrix.example.com"
defaultTarget: orchestrator
allowUnmentioned: true
mentions:
"@gpt": gpt
"@research": research
systemPromptRef: assets/prompts/matrix/default.txt
maxConcurrent: 1enabled
- Turns the gateway on or off.
- If
false, Manifold starts normally without connecting to Matrix.
homeserverURL
- The base URL of your Matrix homeserver.
- Example:
https://matrix.example.com
userID
- The Matrix account Manifold will use.
- Example:
@manifold:matrix.example.com
accessToken
- The access token for that Matrix account.
- Use an environment variable such as
${MATRIX_ACCESS_TOKEN}instead of committing a real token into source control.
deviceID
- Present in config, but not used by the current gateway implementation.
- You can leave a descriptive value such as
manifold-agentd.
syncTimeoutSeconds
- Controls how long each Matrix
/synclong-poll can wait for new events. - Higher values reduce request churn. Lower values make the connection cycle more frequently.
30is a reasonable default.
syncRetryDelaySeconds
- Delay before retrying after a sync error.
3is a reasonable default.
processBacklog
- If
false, the gateway skips old room events on the first successful sync after startup. - If
true, it processes backlog events from the first sync response. - Most users should start with
falseso Manifold does not answer old messages when you first turn it on.
Each item in matrix.rooms configures one Matrix room.
roomID
- Required.
- Must be the real Matrix room ID, such as
!abc123:matrix.example.com. - This is an exact allowlist entry. If the room is not listed here, the gateway ignores it.
defaultTarget
- Where unmatched messages go when
allowUnmentionedistrue. - Use
orchestratorfor the main Manifold orchestrator. - You can also use a specialist name if you want every unmentioned message in the room to go to one specialist.
allowUnmentioned
- If
true, messages that do not match any mention rule still go todefaultTarget. - If
false, only explicitly matched messages are routed.
mentions
- Maps a chat alias to a Manifold route target.
- In practice, these targets should be specialist names, or
orchestrator. - Example:
mentions:
"@gpt": gpt
"@small-bot": small-botsystemPromptRef
- Present in config, but not used by the current gateway implementation.
- The current runtime does not load a per-room prompt from this path yet.
maxConcurrent
- Present in config, but not used by the current gateway implementation.
The gateway supports two matching styles.
If a mention alias starts with @, it matches anywhere in the message as a standalone token.
Example config:
mentions:
"@gpt": gptExample messages that match:
@gpt summarize this threadCan @gpt review this?
If a mention alias does not start with @, it behaves like a command prefix and must appear at the start of the message.
Example config:
mentions:
"!gpt": gptExample messages that match:
!gpt summarize this thread
When a prefix match is used, the prefix is removed before the prompt is passed into Manifold.
When the gateway sends a final reply back to the room, it prefixes the reply with the selected route target name.
Example:
gpt: Here is the summary of the discussion...
If the message contains Markdown, Manifold also tries to send Matrix-formatted rich text.
There is not yet a dedicated Matrix room viewer in the Manifold UI.
Today, Matrix-triggered conversations are stored as ordinary Manifold chat sessions, keyed by room and target. That means:
- you can inspect the resulting conversation indirectly through the Chat page
- you are not seeing the full Matrix room timeline as a first-class room browser yet
If you are new to the feature, start simple:
matrix:
enabled: true
homeserverURL: https://matrix.example.com
userID: "@manifold:matrix.example.com"
accessToken: "${MATRIX_ACCESS_TOKEN}"
syncTimeoutSeconds: 30
syncRetryDelaySeconds: 3
processBacklog: false
rooms:
- roomID: "!abc123:matrix.example.com"
defaultTarget: orchestrator
allowUnmentioned: false
mentions:
"@gpt": gpt
"@research": researchWhy this is a good starting point:
- it avoids replying to old room history
- it avoids answering every message in the room
- it makes routing explicit through mentions
On a healthy startup, look for logs like:
matrix_gateway_initializedmatrix_gateway_startup_sync_complete
When a Matrix message is processed successfully, you should later see:
matrix_gateway_message_processed
Potential warning logs:
matrix_gateway_sync_failedmatrix_gateway_join_failedmatrix_gateway_handler_failed
Check these first:
matrix.enabledistrue- the gateway account is actually joined to the room
roomIDis the real Matrix room ID beginning with!- the room is listed in
matrix.rooms - the message matched either
mentionsordefaultTarget
- Set
processBacklog: false
Common causes:
- the room ID is wrong
- the room is encrypted
allowUnmentionedisfalseand the message did not match a configured mention alias
Check your mentions mapping and make sure the target names match the specialist names you configured in Manifold.
Recent versions of the gateway cancel Matrix long-poll requests during shutdown. If shutdown still hangs, collect logs after agentd shutdown requested and verify you are running a build that includes the Matrix gateway shutdown fix.
- Treat the Matrix access token like a password.
- Keep it in
.envor another secret store, not in committed YAML. - Use a dedicated Matrix account for Manifold rather than a personal account.
- Add the gateway account only to rooms where Manifold is supposed to operate.
The Matrix gateway can work with Manifold's pulse automation runtime. Pulse tasks are recurring background checks or routines associated with a Matrix room and route target.
This is an advanced feature. Most users do not need it to start using Matrix chat.
Important behavior:
- pulse execution runs inside Manifold
- pulse logs are internal by default
- room-facing pulse messages should be sent intentionally through the Matrix message tool, not assumed automatically
- interval tasks run every
interval_seconds - daily-time tasks run at
specific_timeusing the Manifold server's local timezone - one-off tasks use
specific_atand also interpretYYYY-MM-DDTHH:MMvalues in the Manifold server's local timezone - newly created pulse tasks run immediately once, then follow their configured schedule after that first successful run
Examples:
@gpt Create a pulse task for this room:
title: Hourly room check
schedule_type: interval
interval_seconds: 3600
prompt: Review the room context. If there is anything useful to report, send a short update using matrix_room_message.
@gpt Create a daily pulse task for this room:
title: Morning summary
schedule_type: daily_time
specific_time: 09:00
prompt: Summarize important project updates and post them to this Matrix room using matrix_room_message.
@gpt Create a one-off pulse task for this room:
title: Release reminder
schedule_type: once_at
specific_at: 2026-05-12T15:30
prompt: Remind the room about the release checkpoint using matrix_room_message.
If you remember only five things, remember these:
- Matrix is a chat network; Manifold can join it through one Matrix account.
- You must configure explicit room IDs under
matrix.rooms. - Use the real room ID starting with
!, not a room alias starting with#. processBacklog: falseis the safest first-run setting.deviceID,systemPromptRef, andmaxConcurrentare not active runtime knobs yet.