Message Gateway (Beta)
Security Warning — Read Before Enabling
The Message Gateway gives anyone who can message your bot the ability to create Agor sessions and interact with autonomous coding agents. This effectively grants full access to the target worktree, the host machine (to the extent the agent can access it), and any connected services (MCP servers, APIs, credentials).
This is a beta feature. Use at your own risk and with caution:
- Assume full access. Anyone who can DM your Slack bot can prompt the agent to read, write, and execute code on the worktree. Treat this as giving them SSH access to that environment.
- Sandbox everything you can. Run the daemon in a container, use restricted permission modes (
supervisedormanual), limit MCP server exposure, and avoid mounting sensitive host directories. - Restrict bot access. In Slack, limit who can DM the bot. Consider creating a private Slack workspace or restricting the bot to specific users via Slack’s app distribution settings.
- Use dedicated worktrees. Don’t point channels at worktrees containing production code or credentials. Create isolated worktrees specifically for gateway use.
- Audit regularly. Monitor sessions created by the gateway. All activity is logged in Agor’s session transcripts.
- Rotate credentials. Periodically rotate your Slack bot tokens and channel keys through the Settings UI.
- Don’t run as root. The daemon and agents should never run as root. Use Agor’s Unix isolation modes for additional sandboxing.
The Message Gateway connects external messaging platforms to Agor’s agent sessions. DM your Slack bot, and Agor spins up a fresh coding session on the right worktree with the right agent — then streams responses back into the same thread. Follow-up messages continue the conversation in the same session.
Currently Slack-only. Discord, WhatsApp, and Telegram connectors are planned.
What is a Channel?
A Channel is a portal into a worktree. It defines:
- A home worktree — the persistent worktree where every session spawned by this channel will operate. Think of it as a command center, like an
agor-openclawworktree that supervises a project. - An agentic tool configuration — which coding agent to use (Claude Code, Codex, Gemini, OpenCode), which model, permission mode, and which MCP servers to attach.
- Platform credentials — bot token, app token, and other secrets needed to send and receive messages (encrypted at rest).
You can create multiple channels for the same or different worktrees. For example:
- “Optimus Prime” — Claude Code (Opus 4.6) on your main
agor-openclawworktree, with Slack + GitHub MCP servers - “Scout” — Claude Code (Sonnet 4.5) on a lighter review worktree, trust mode, no MCP servers
- “Codex Runner” — Codex on a
superset-frontendworktree for quick JS tasks
Each channel gets a unique channel key (a UUID) that authenticates inbound messages. The Slack integration uses Socket Mode, so no public webhook URLs are needed.
How It Works
When a message arrives from a platform (e.g., a Slack DM), the gateway processes it through a simple pipeline:
- Authenticate. The gateway looks up the channel by its key and verifies it’s enabled.
- Map thread to session. If this is a new thread, create a fresh Agor session on the channel’s target worktree with the channel’s agentic tool configuration. If this thread has been seen before, route to the existing session.
- Send prompt. The message text is sent to the session via Agor’s standard prompt flow — task creation, executor spawn, the full pipeline.
- Agent runs. The coding agent (Claude Code, Codex, etc.) processes the prompt with full access to the worktree, MCP servers, and any configured tools.
- Route response back. When the agent produces a response, Agor’s outbound routing hook intercepts it and posts it back into the same platform thread. Markdown is converted to platform-native formatting (e.g., Slack mrkdwn).
- Repeat. Follow-up messages in the same thread continue the same session — the agent has full conversation history.
Slack DM Agor Daemon Agent
──────── ─────────── ─────
│ │ │
│ "Fix the login bug" │ │
├─────────────────────────────>│ │
│ │ Create session │
│ │ + thread mapping │
│ │ │
│ │ Send prompt │
│ ├──────────────────────────>│
│ │ │
│ │ Agent response │
│ │<──────────────────────────┤
│ "I've fixed the login..." │ │
│<─────────────────────────────┤ │
│ │ │
│ "Can you also add tests?" │ │
├─────────────────────────────>│ │
│ │ Route to same session │
│ ├──────────────────────────>│
│ │ │The outbound routing is a lightweight after-hook on message creation. For sessions without a gateway mapping (the vast majority), it’s a single database lookup that returns immediately — effectively a no-op.
Setting Up Slack Integration
1. Create a Slack App
- Go to api.slack.com/apps and create a new app.
- Under OAuth & Permissions, add the
chat:writebot scope. - Under App Home, enable the Messages Tab and check “Allow users to send Slash commands and messages from the messages tab.”
- Under Event Subscriptions, subscribe to the
message.imbot event. - Under Socket Mode, enable Socket Mode and generate an App-Level Token (
xapp-...). - Install the app to your workspace and note the Bot User OAuth Token (
xoxb-...).
2. Create a Channel in Agor
- Open Settings (gear icon) and navigate to the Gateway tab.
- Click Add Channel.
- Fill in:
- Name — Give it a memorable name (e.g., “Optimus Prime”).
- Channel Type — Select
slack. - Target Worktree — Pick the worktree where sessions will run.
- Bot Token — Paste the
xoxb-...token. - App Token — Paste the
xapp-...token (required for receiving DMs via Socket Mode).
- Optionally expand Agentic Tool Configuration to customize the agent, model, permission mode, and MCP servers for sessions created by this channel.
- Save. The daemon will immediately start a Socket Mode listener for this channel.
3. DM Your Bot
Open Slack, find your bot under Apps, and send it a message. You should see system debug messages confirming session creation, followed by the agent’s response — all in the same DM thread.
Agentic Tool Configuration
Each channel can define its own agent configuration, overriding the user’s defaults:
| Setting | Description | Default |
|---|---|---|
| Agent | Which coding tool to use (Claude Code, Codex, Gemini, OpenCode) | Claude Code |
| Model | Model alias or specific model ID | User’s default |
| Permission Mode | How the agent handles tool approvals (trust, auto, supervised, manual) | Agent’s default |
| MCP Servers | Which MCP servers the agent can access | None |
The gateway resolves configuration with a fallback chain: channel config > user defaults > system defaults. This means you only need to configure what you want to override.
Architecture
The gateway is built as a set of loosely coupled components:
- Gateway Service (
/gateway) — Orchestrates inbound routing (platform to session) and outbound routing (session to platform). Custom FeathersJS service. - Gateway Channels (
/gateway-channels) — CRUD for channel configurations with encrypted credential storage. - Thread-Session Map (
/thread-session-map) — Persists the mapping between platform thread IDs and Agor session IDs. - Connector Layer — Platform-agnostic
GatewayConnectorinterface with a registry pattern. Adding a new platform means implementingsendMessage()and optionallystartListening(). - Outbound Hook — FeathersJS
after.createhook on the messages service. Fire-and-forget; never blocks message creation.
Adding a New Platform
The connector interface is intentionally minimal:
interface GatewayConnector {
readonly channelType: ChannelType;
sendMessage(req: { threadId: string; text: string }): Promise<string>;
startListening?(callback: (msg: InboundMessage) => void): Promise<void>;
stopListening?(): Promise<void>;
formatMessage?(markdown: string): string;
}Implement this interface, register it in the connector registry, and the gateway service handles the rest — authentication, thread mapping, session lifecycle, and outbound routing all work automatically.
Best Practices
- Use trust or auto mode for unattended agents. If you want the agent to operate without human approval on each tool call, set the permission mode accordingly.
- Attach relevant MCP servers. A Slack-connected agent that also has GitHub MCP can create PRs, check CI status, and post updates — all from a DM.
- One worktree per concern. Create separate channels for different projects or responsibilities rather than funneling everything through one worktree.
- Name channels descriptively. When you have multiple channels, names like “Frontend Review Bot (Sonnet)” are much more useful than “Channel 1.”
- Keep credentials rotated. Bot tokens and app tokens are encrypted at rest, but rotate them periodically through the channel edit UI.
Troubleshooting
- “Sending messages to this app has been turned off.” Enable the Messages Tab in your Slack app’s App Home settings, and subscribe to the
message.imbot event. - Bot doesn’t respond to DMs. Check that Socket Mode is enabled, the
app_tokenis set in the channel config, and the daemon logs show[gateway] Socket Mode listener started for channel "...". - Agent session created but no response. Verify that the coding agent (Claude Code, etc.) is authenticated inside the daemon’s environment. Check daemon logs for executor errors.
- Response appears in logs but not in Slack. The
bot_tokenmay lack thechat:writescope, or the bot may not have permission to DM the user. Check Slack app permissions. - Infinite message loop. The connector filters out bot messages (
bot_idorsubtype: bot_message). If you see loops, check that your Slack app’s bot user is properly configured.
Related Reading
- Worktree Scheduler — Automate recurring work on worktrees (complements gateway for event-driven vs. time-driven automation).
- Agor MCP Server — Give gateway-spawned agents full self-awareness and orchestration capabilities.
- Agent SDK Comparison — Choose the right coding agent for your channel’s use case.
- Spawned Subsessions — Gateway-created sessions can themselves spawn child sessions for parallel work.