GuideMessage Gateway

Message Gateway

Security — Think Before Enabling

A Message Gateway channel opens an external pathway (Slack DM, GitHub @mention) that can prompt agents holding your worktree’s full surface area: code, MCP servers, env vars, and any credentials in scope. Worth thinking through before you flip it on, but not categorically dangerous.

Things working in your favor:

  • Email-based user matching. Inbound Slack messages are aligned to Agor users via the users:read.email scope; GitHub mentions are aligned via user_map (login → email) or the sender’s public GitHub email. When a match exists, the spawned session is attributed to that real Agor user — so prompts run under their identity and audit trail, not as a generic bot. Unmatched senders are rejected (or surfaced to admins).
  • Encrypted credentials at rest. Bot tokens, app tokens, and private keys are stored encrypted in the daemon database.
  • Outbound-only transport. Slack uses Socket Mode and GitHub uses API polling — no public webhook URLs to defend.
  • Per-channel agent config. Permission modes, model, and MCP server attachments are scoped to the channel, not global.

Things that still warrant caution:

  • Channels open external pathways into agent context. Anyone who can DM the bot (or @mention it on a watched repo) can ask the agent to read, write, or execute code on the target worktree. If the channel runs in trust mode with broad MCP access, that pathway is effectively a remote prompt seat — treat it as such.
  • Restrict who can reach the bot. Limit Slack DM scope (private workspace or app distribution settings); narrow GitHub watch_repos to repos where mentions are intended.
  • Use dedicated worktrees. Don’t point channels at worktrees holding production secrets. Create isolated worktrees scoped to gateway use.
  • Pick the right permission mode. supervised or manual modes preserve human-in-the-loop on tool calls; trust is fine for bounded, well-understood automations but removes that gate.
  • Mind the Unix isolation mode. In simple mode the agent runs as the daemon user; in strict mode it runs as the matched Agor user’s Unix identity, which is what you want when channels span teams.
  • Rotate tokens. Bot/app tokens and channel keys should be rotated like any other secret.

The mental model: a gateway channel is a remote terminal into an agent that holds the same authority as a human prompting that worktree. Configure who can reach it, what it can do, and which identity it runs as — and the rest is normal Agor surface area.

The Message Gateway connects external messaging platforms to Agor’s agent sessions. Message your Slack bot or mention @agor on a GitHub PR, and Agor spins up a coding session on the right worktree with the right agent — then routes responses back to the platform. Follow-up messages continue the conversation in the same session.

Currently supports Slack and GitHub. 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 assistant worktree 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 assistant worktree, 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-frontend worktree for quick JS tasks

Each channel gets a unique channel key (a UUID) that authenticates inbound messages. The Slack integration uses Socket Mode and the GitHub integration uses API polling — both are outbound-only, so no public webhook URLs are needed.

How It Works

When a message arrives from a platform (e.g., a Slack DM or a GitHub @agor mention), the gateway processes it through a simple pipeline:

  1. Authenticate. The gateway looks up the channel by its key and verifies it’s enabled.
  2. 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.
  3. Send prompt. The message text is sent to the session via Agor’s standard prompt flow — task creation, executor spawn, the full pipeline.
  4. Agent runs. The coding agent (Claude Code, Codex, etc.) processes the prompt with full access to the worktree, MCP servers, and any configured tools.
  5. 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. Formatting is adapted per platform (Slack mrkdwn conversion, GitHub markdown pass-through).
  6. 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

  1. Go to api.slack.com/apps and create a new app.
  2. Under OAuth & Permissions, add the chat:write bot scope.
  3. Under App Home, enable the Messages Tab and check “Allow users to send Slash commands and messages from the messages tab.”
  4. Under Event Subscriptions, subscribe to the message.im bot event.
  5. Under Socket Mode, enable Socket Mode and generate an App-Level Token (xapp-...).
  6. Install the app to your workspace and note the Bot User OAuth Token (xoxb-...).

2. Create a Channel in Agor

  1. Open Settings (gear icon) and navigate to the Gateway tab.
  2. Click Add Channel.
  3. 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).
  4. Optionally expand Agentic Tool Configuration to customize the agent, model, permission mode, and MCP servers for sessions created by this channel.
  5. 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.

Setting Up GitHub Integration

How It Works

When someone mentions @agor (or your app’s name) in a PR or issue comment, Agor picks it up via API polling and creates a session. The session runs on the target worktree, and the agent’s final response is posted back as a PR/issue comment under the app’s bot identity (e.g., agor[bot]).

Key differences from Slack:

  • Polling, not WebSocket — Agor polls the GitHub API every 15 seconds (configurable). No public endpoint needed.
  • Per-PR/issue sessions — Each PR or issue gets its own session. All @agor mentions within the same PR route to the same session.
  • Last-message-only responses — Unlike Slack (which streams every message), only the agent’s final response is posted to GitHub. Intermediate progress is visible in the Agor UI.
  • Bot identity — Responses are posted as the GitHub App’s bot user, not as any human user.

1. Create a GitHub App

You can create the app through Agor’s setup wizard or manually:

Via Agor UI (recommended):

  1. Open SettingsGateway tab → Add Channel.
  2. Select github as the channel type.
  3. Click “Create GitHub App” — this opens GitHub’s App Manifest flow.
  4. Name your app (default: “Agor”) and click Create.
  5. Agor captures the credentials automatically.

Manually:

  1. Go to your GitHub org’s SettingsDeveloper settingsGitHub AppsNew GitHub App.
  2. Set permissions: Contents: Read, Issues: Read & Write, Pull requests: Read & Write, Metadata: Read.
  3. Subscribe to events: issue_comment, issues, pull_request, pull_request_review, pull_request_review_comment.
  4. Disable webhooks (set Active to false) — Agor uses polling.
  5. After creating, generate a private key and note the App ID.

2. Install the App

  1. Go to your GitHub App’s page → Install App.
  2. Select your organization and choose which repositories Agor should access.
  3. Click Install.

3. Create a Channel in Agor

  1. Open SettingsGateway tab → Add Channel.
  2. Fill in:
    • Name — e.g., “GitHub: preset-io”
    • Channel Type — Select github.
    • Target Worktree — Pick the worktree where sessions will run. Ideally, this is an assistant worktree configured for GitHub work.
    • App ID — Your GitHub App’s ID.
    • Private Key — The PEM private key (encrypted at rest).
    • Installation ID — Found via the setup wizard or GitHub’s API.
  3. Optionally configure:
    • Watch Repos — Specific repos to monitor (empty = all repos in the installation).
    • Poll Interval — How often to check for new mentions (default: 15 seconds).
    • Mention Name — The keyword to trigger on (default: agor, matches @agor).
  4. Save. Polling starts immediately.

4. Mention @agor on a PR

Comment @agor review this PR on any PR in a watched repo. Within 15 seconds:

  1. The comment gets a 👀 reaction (instant feedback).
  2. A “Processing…” comment appears with a link to the Agor session.
  3. The agent reviews the PR.
  4. The “Processing…” comment is replaced with the agent’s response.

Follow-up @agor mentions on the same PR continue the same session — the agent has full conversation history.

Setting Up the Assistant

The gateway routes messages to a session on the target worktree. What the agent does with those messages is up to you — configured via the assistant’s instructions on the target worktree.

A minimal assistant prompt might be:

You handle GitHub mentions for preset-io/agor.

When mentioned on a PR:
- Create a worktree for the PR branch
- Spawn a child agent to review the changes
- The child should comment on the PR when done
- Include links to Agor sessions in your replies

When mentioned on an issue:
- Read the issue and all comments
- Respond with analysis or next steps

With a capable model (like Claude Opus) and Agor MCP tools available, even a minimal prompt produces good results — the agent figures out how to use worktrees, spawn sessions, and interact with GitHub. Over time, the assistant learns the codebase, team conventions, and review patterns across PRs.

Agentic Tool Configuration

Each channel can define its own agent configuration, overriding the user’s defaults:

SettingDescriptionDefault
AgentWhich coding tool to use (Claude Code, Codex, Gemini, OpenCode)Claude Code
ModelModel alias or specific model IDUser’s default
Permission ModeHow the agent handles tool approvals (trust, auto, supervised, manual)Agent’s default
MCP ServersWhich MCP servers the agent can accessNone

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 GatewayConnector interface with a registry pattern. Currently: SlackConnector (Socket Mode) and GitHubConnector (API polling). Adding a new platform means implementing sendMessage() and optionally startListening().
  • Outbound Hook — FeathersJS after.create hook 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

Slack

  • “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.im bot event.
  • Bot doesn’t respond to DMs. Check that Socket Mode is enabled, the app_token is set in the channel config, and the daemon logs show [gateway] Socket Mode listener started for channel "...".
  • Response appears in logs but not in Slack. The bot_token may lack the chat:write scope, 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_id or subtype: bot_message). If you see loops, check that your Slack app’s bot user is properly configured.

GitHub

  • Bot doesn’t respond to @agor mentions. Check daemon logs for [github] Watching N repos and [github] Starting poll loop. Verify the App ID, private key, and installation ID are correct. Ensure the app is installed on the org with the right repos selected.
  • “GitHub App authentication failed.” The private key or app ID may be wrong. Re-check credentials. If you regenerated the private key on GitHub, update it in the channel config.
  • Mentions detected but no session created. The require_mention setting may be filtering incorrectly. Check that mention_name matches your app’s slug (default: agor). Mentions inside code blocks are ignored by design.
  • Response posted as wrong user. GitHub comments should appear as the app’s bot identity (e.g., agor[bot]). If comments appear as a human user, something is bypassing the connector and using a personal GITHUB_TOKEN instead.
  • Rate limit errors. At 15s polling with 5 repos, you use ~1,200 req/hour out of 5,000. If you’re monitoring many repos, increase poll_interval_ms or use watch_repos to narrow scope.

General

  • 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.
  • 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.
BSL 1.1 © 2026 Maxime Beauchemin