mirror of
https://github.com/nearai/ironclaw.git
synced 2026-09-03 08:06:01 +08:00
Guidance and docs now describe what ships: every declared [channel.reply] binds one ReplySink (stream or message is cadence only), the session channel's sink is the projection sink composition attaches, final-reply attachments materialize inside reply publication, and the two 2026-08 channel design records carry superseded-in-part banners pointing at the progressive-reply design. The Slack rows say stream replies; the unused agents.sessions.rename link is gone from the setup docs; tests/AGENTS.md maps the SSE first-text journeys. Code-adjacent fixes riding along: the RebornRuntime delivery-coordinator accessor is test-support again (production wiring takes the coordinator from the factory) with the struct ratchet re-pinned 267 -> 268 / 43 -> 44 against main's 269 / 44; the binary's session-reply-channel naming is pinned by a CLI test; the DCR recovery diagnostics are debug-level and the malformed-record renewal has a caller-level test (sabotage-verified); the stale notice-route comment, the split doc block in composition test-support, the misplaced doc on the failure-notice test, the fake channel adapter counters' docs, and the ExtensionBindings doc are corrected; the Slack exactly-once integration journey waits for the attempt to settle before sampling the wire. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
408 lines
17 KiB
Plaintext
408 lines
17 KiB
Plaintext
---
|
|
title: "Slack"
|
|
description: "Talk to your agent in Slack channels and direct messages"
|
|
icon: slack
|
|
---
|
|
|
|
Connect IronClaw to a Slack workspace so your agent can read and reply to messages in
|
|
channels and DMs.
|
|
|
|
## Prerequisites
|
|
|
|
- A Slack workspace where you can install apps
|
|
- An IronClaw instance reachable from Slack over HTTPS
|
|
|
|
---
|
|
|
|
## Setup
|
|
|
|
<Steps>
|
|
|
|
<Step title="Create a Slack app">
|
|
|
|
Create an app at [api.slack.com/apps](https://api.slack.com/apps). The fastest route is
|
|
**From an app manifest** — paste the [manifest below](#app-manifest), which enables the
|
|
**Agents** feature and sets the scopes, events, the `/ironclaw` slash command, and
|
|
redirect URL in one step. Replace `example.com` with your own host first.
|
|
|
|
If you build the app from scratch instead, enable **Agents** in the app's sidebar (this is
|
|
what the manifest's `features.agent_view` block does — see [Native Agent](#native-agent)),
|
|
then configure **OAuth & Permissions**, **Event Subscriptions**, **Slash Commands**, and
|
|
the redirect URL to match that manifest.
|
|
|
|
Once created, install the app to your workspace and copy the bot token from **OAuth &
|
|
Permissions**, and the signing secret from **Basic Information**. IronClaw uses the signing
|
|
secret to verify that incoming requests really came from Slack.
|
|
|
|
</Step>
|
|
|
|
<Step title="Start IronClaw">
|
|
|
|
```bash
|
|
ironclaw serve
|
|
```
|
|
|
|
</Step>
|
|
|
|
<Step title="Enter the deployment configuration (operator, once per instance)">
|
|
|
|
In the [web interface](/using/webui), open **Admin → Configuration** and fill in the
|
|
**Slack deployment configuration** card. Every field is required; the secrets are
|
|
written to the encrypted secret store and never shown again.
|
|
|
|
| Field | Where it comes from |
|
|
| --- | --- |
|
|
| Bot token | **OAuth & Permissions** → Bot User OAuth Token (`xoxb-…`) |
|
|
| Signing secret | **Basic Information** → App Credentials |
|
|
| Workspace (team) ID | Your workspace id (`T…`) — also the `team_id` in every Events API payload |
|
|
| App ID | **Basic Information** → App ID (`A…`) |
|
|
| Installation ID | A stable label you choose for this app/workspace installation, e.g. `slack-acme` |
|
|
| Bot user ID | The bot's member id (`U…`), shown under **App Home** or returned by `auth.test` |
|
|
| OAuth client ID | **Basic Information** → App Credentials |
|
|
| OAuth client secret | **Basic Information** → App Credentials |
|
|
|
|
The OAuth client id and secret drive the per-user ("personal") authorization described
|
|
below; the rest configure the workspace bot.
|
|
|
|
<Note>
|
|
The agent can't do this operator step for you — registering the app credentials stays in
|
|
the web interface. Once it's done, though, asking the agent to "connect Slack" does work
|
|
for the personal half: it installs and activates the extension, opens the in-chat
|
|
connection panel if your personal OAuth is missing, and confirms when your account is
|
|
already connected.
|
|
</Note>
|
|
|
|
</Step>
|
|
|
|
<Step title="Point Slack at your instance">
|
|
|
|
Back in your Slack app, confirm **Event Subscriptions** points at your instance:
|
|
|
|
```
|
|
https://your-host/webhooks/extensions/slack/events
|
|
```
|
|
|
|
Slack sends a verification challenge to that URL, so IronClaw must already be running and
|
|
reachable when you save it. Reinstall the app if Slack asks you to.
|
|
|
|
Also register the `/ironclaw` slash command under **Slash Commands**, pointed at the
|
|
identical Request URL — Slack allows a slash command's endpoint to be any URL, so one
|
|
signed address answers both surfaces. Registering a slash command adds the `commands` bot
|
|
scope, which also needs a reinstall.
|
|
|
|
</Step>
|
|
|
|
<Step title="Talk to it">
|
|
|
|
Pick IronClaw from Slack's **Agents** picker (or open its Messages tab) and start a chat,
|
|
invite the bot to a channel and mention it, or send it a direct message. In a channel the
|
|
bot only responds to messages that mention it; in a DM it responds to everything. The
|
|
answer streams into the thread as the run produces it, each tool call shows up as a task
|
|
card, and Slack's **Stop** button cancels the run.
|
|
|
|
</Step>
|
|
|
|
</Steps>
|
|
|
|
---
|
|
|
|
## Native Agent
|
|
|
|
IronClaw replies on Slack's native Agent surface rather than by posting a finished
|
|
message. Each run is one agent session in the conversation's thread: the session shows
|
|
*processing* while the run works and *suspended* while it waits on you (an approval, a
|
|
sign-in), the answer text streams in as it is produced, tool calls appear as task cards,
|
|
and the session returns to *active* when the run ends. This is what the manifest's
|
|
`features.agent_view` block, the `assistant:write` bot scope, and the Agent event
|
|
subscriptions are for.
|
|
|
|
<Warning>
|
|
Switching an app to `agent_view` is a one-way door. Slack's migration guide is explicit:
|
|
"Once you change your app's manifest from `assistant_view` to `agent_view`, you can't
|
|
revert to the Assistant messaging experience." Create the app from the manifest above,
|
|
or switch an existing app knowingly.
|
|
</Warning>
|
|
|
|
- **Agents picker.** With the Agents feature enabled, IronClaw is listed in Slack's Agents
|
|
picker and its Messages tab behaves like a chat with sessions; each new chat there is
|
|
a thread. `agent_description` (300 characters at most) and `suggested_prompts` are what
|
|
people see before their first message — edit them in the manifest to taste.
|
|
- **Stop button.** It appears while a session is processing because the app subscribes to
|
|
`agent_session_stopped`. Pressing it is normalized into the channel's `stop` command,
|
|
the run is cancelled, and IronClaw transitions the session out of *processing* itself
|
|
(Slack does not do that automatically).
|
|
- **If the Agent feature is missing.** There is no conventional-message fallback. A
|
|
workspace whose app was installed without the Agents feature answers `feature_disabled`
|
|
to `agents.sessions.setStatus`, and the reply fails clearly — the run's delivery is
|
|
recorded as failed with a reason naming `features.agent_view` — until an operator
|
|
enables **Agents** in the app settings and reinstalls the app. The same applies to a
|
|
bot token that lacks `chat:write` (`missing_scope`): the session and streaming methods
|
|
(`agents.sessions.setStatus`, `chat.startStream`,
|
|
`chat.appendStream`, `chat.stopStream`) all require it, and `assistant:write` is added
|
|
automatically when the feature is enabled.
|
|
- **Channels.** Streaming into a channel needs the person's user and team ids
|
|
(`recipient_user_id`/`recipient_team_id`), which IronClaw records with every inbound
|
|
message; a DM does not.
|
|
|
|
---
|
|
|
|
## URLs Slack Needs
|
|
|
|
Both are served by `ironclaw serve`, so they use whatever host and port your instance is
|
|
reachable on. Replace `your-host` and use HTTPS — Slack will not deliver to a plain-HTTP
|
|
or self-signed endpoint.
|
|
|
|
| Slack setting | URL |
|
|
| --- | --- |
|
|
| Event Subscriptions → Request URL | `https://your-host/webhooks/extensions/slack/events` |
|
|
| Slash Commands → Request URL | `https://your-host/webhooks/extensions/slack/events` |
|
|
| OAuth & Permissions → Redirect URL | `https://your-host/api/reborn/product-auth/oauth/slack/callback` |
|
|
|
|
<Note>
|
|
Event Subscriptions and the `/ironclaw` slash command point at the identical Request
|
|
URL — one signed endpoint answers both surfaces, so there is no second address to stand
|
|
up.
|
|
</Note>
|
|
|
|
The redirect URL is used for per-user ("personal") Slack authorization. IronClaw derives
|
|
it from the instance's own public base URL, so there is nothing to set on this side — just
|
|
register the value above in the Slack app.
|
|
|
|
<Warning>
|
|
The redirect URL must match **exactly** on both sides, including scheme, host, port, and
|
|
path. Slack rejects the authorization with a redirect-URI mismatch if it differs by even a
|
|
trailing slash. The path is `/api/reborn/product-auth/oauth/slack/callback` — a shortened
|
|
form like `/oauth/slack/callback` is not served and will 404.
|
|
</Warning>
|
|
|
|
For a local instance the values are `http://127.0.0.1:3000/...`, but Slack cannot reach
|
|
loopback, so events only work once the instance is reachable from the internet.
|
|
|
|
---
|
|
|
|
## App Manifest
|
|
|
|
Create the app with **From an app manifest** and paste this, replacing `example.com` with
|
|
your host. It enables the Agents feature (`features.agent_view`), and sets the scopes, bot
|
|
events, the `/ironclaw` slash command, and redirect URL that the steps above describe.
|
|
|
|
The canonical copy of this manifest ships with the Slack package as
|
|
`crates/extensions/packages/slack/app_manifest.json`; the block below is
|
|
test-pinned to be identical to that file, so either can be imported.
|
|
|
|
```json
|
|
{
|
|
"display_information": {
|
|
"name": "IronClaw"
|
|
},
|
|
"features": {
|
|
"agent_view": {
|
|
"agent_description": "IronClaw is your autonomous assistant: ask it to research, draft, run tools, and act across your connected apps. Replies stream into the thread as they are produced, with each step shown as it happens.",
|
|
"suggested_prompts": [
|
|
{
|
|
"title": "Summarize a thread",
|
|
"message": "Summarize the discussion in the thread I share next and list the open questions."
|
|
},
|
|
{
|
|
"title": "Draft a reply",
|
|
"message": "Draft a concise reply to the last message I received, in my voice."
|
|
},
|
|
{
|
|
"title": "Check my integrations",
|
|
"message": "Which integrations are connected for me right now, and what can you do with them?"
|
|
}
|
|
]
|
|
},
|
|
"app_home": {
|
|
"home_tab_enabled": false,
|
|
"messages_tab_enabled": true,
|
|
"messages_tab_read_only_enabled": false
|
|
},
|
|
"bot_user": {
|
|
"display_name": "IronClaw",
|
|
"always_online": true
|
|
},
|
|
"slash_commands": [
|
|
{
|
|
"command": "/ironclaw",
|
|
"url": "https://example.com/webhooks/extensions/slack/events",
|
|
"description": "Run IronClaw commands",
|
|
"usage_hint": "status | model use <name> | new | stop",
|
|
"should_escape": false
|
|
}
|
|
]
|
|
},
|
|
"oauth_config": {
|
|
"redirect_urls": [
|
|
"https://example.com/api/reborn/product-auth/oauth/slack/callback"
|
|
],
|
|
"scopes": {
|
|
"user": [
|
|
"search:read",
|
|
"channels:history",
|
|
"groups:history",
|
|
"im:history",
|
|
"mpim:history",
|
|
"channels:read",
|
|
"groups:read",
|
|
"im:read",
|
|
"mpim:read",
|
|
"users:read",
|
|
"chat:write",
|
|
"reactions:read",
|
|
"reactions:write",
|
|
"im:write"
|
|
],
|
|
"bot": [
|
|
"assistant:write",
|
|
"channels:history",
|
|
"app_mentions:read",
|
|
"chat:write",
|
|
"im:history",
|
|
"im:write",
|
|
"files:read",
|
|
"files:write",
|
|
"groups:history",
|
|
"mpim:history",
|
|
"reactions:write",
|
|
"commands"
|
|
]
|
|
},
|
|
"pkce_enabled": false
|
|
},
|
|
"settings": {
|
|
"event_subscriptions": {
|
|
"request_url": "https://example.com/webhooks/extensions/slack/events",
|
|
"bot_events": [
|
|
"app_mention",
|
|
"message.channels",
|
|
"message.groups",
|
|
"message.im",
|
|
"message.mpim",
|
|
"app_home_opened",
|
|
"app_context_changed",
|
|
"agent_session_stopped",
|
|
"agent_session_title_changed"
|
|
]
|
|
},
|
|
"org_deploy_enabled": false,
|
|
"socket_mode_enabled": false,
|
|
"token_rotation_enabled": false,
|
|
"is_mcp_enabled": false
|
|
}
|
|
}
|
|
```
|
|
|
|
<Note>
|
|
This is a starting point, not a minimum. The `user` scopes exist for per-user
|
|
authorization — searching and posting as the person rather than as the bot. If you only
|
|
want the bot to participate in channels, you can drop the `user` block and the redirect
|
|
URL with it. The `agent_view` block, `assistant:write`, `chat:write`, and the Agent
|
|
events (`app_home_opened`, `app_context_changed`, `agent_session_stopped`,
|
|
`agent_session_title_changed`) are not optional: they are how replies reach Slack.
|
|
(The legacy `assistant_thread_*` events are deliberately absent — Slack's Agent
|
|
View validator rejects manifests that subscribe to them.)
|
|
</Note>
|
|
|
|
`assistant:write` is added by Slack when the Agents feature is enabled and is declared
|
|
here so a manifest import carries it. `chat:write` covers every session and streaming
|
|
method. `files:read` lets the bot resolve and download inbound file shares.
|
|
`files:write` lets it return explicitly attached workspace files through
|
|
Slack's external upload flow. IronClaw never uses the retired `files.upload`
|
|
method. `reactions:write` lets the bot react to a message it delivers a notice
|
|
about on the message lane; live progress on the Agent surface shows in the
|
|
session itself. `channels:history` and its private/group/DM siblings also let
|
|
the bot read a streaming message back after a network fault mid-reply.
|
|
|
|
The `user` scopes are the union of what the sixteen personal Slack tools need.
|
|
`reactions:read`, `reactions:write`, and `im:write` joined that union when the
|
|
reaction and open-DM tools landed — an account connected before then holds a
|
|
narrower grant, and the first call to one of those tools answers with a
|
|
reconnect prompt. Reconnecting requests the wider set and updates the same
|
|
account in place; the earlier read-only tools keep working in the meantime.
|
|
|
|
Slack validates the request URL when you save the manifest, so start IronClaw before
|
|
creating the app.
|
|
|
|
---
|
|
|
|
## Configuration
|
|
|
|
Slack has no settings in the configuration file. The instance-wide values — bot token,
|
|
signing secret, workspace and app ids, and the OAuth client credentials — are entered
|
|
once under **Admin → Configuration** on the **Slack deployment configuration** card, and
|
|
the secrets go straight into the encrypted secret store. Each person then connects their
|
|
own Slack account from the **Extensions** page (or by asking the agent to "connect
|
|
Slack").
|
|
|
|
There is no channel allowlist or routing table. Inviting the bot into a channel is what
|
|
enables it: Slack only delivers a channel's events to the app because the bot is a
|
|
member, and each message runs as the person who mentioned the bot. In channels — shared
|
|
or not — the bot answers only when explicitly mentioned: a plain thread reply without a
|
|
mention is dropped without starting a run, though approve/deny replies and `/ironclaw`
|
|
commands still work; in DMs it answers every message.
|
|
|
|
There is no key that turns Slack on. The webhook route is always mounted, and it starts
|
|
accepting events once the Slack extension is installed and its signing secret is
|
|
registered. Until then it answers `503`. There is no `IRONCLAW_REBORN_SLACK_ENABLED`
|
|
environment variable either — the gate it fed was removed in #6116.
|
|
|
|
<Note>
|
|
An older configuration file may still carry a `[slack]` section. Nothing reads it. A
|
|
leftover setup field — `api_app_id`, `team_id`, `bot_token_env`, `signing_secret_env`,
|
|
`channel_routes`, and the rest — now stops startup with a pointer to the web interface
|
|
instead of being quietly ignored, so delete the section. `[slack].enabled` is accepted
|
|
but inert, so an older install keeps booting.
|
|
</Note>
|
|
|
|
---
|
|
|
|
## Troubleshooting
|
|
|
|
<AccordionGroup>
|
|
|
|
<Accordion title="Slack can't verify the request URL">
|
|
IronClaw has to be running and publicly reachable over HTTPS before you save the URL.
|
|
Confirm the instance answers from outside your network, and that you used the exact
|
|
`/webhooks/extensions/slack/events` path.
|
|
</Accordion>
|
|
|
|
<Accordion title="Slack rejects authorization with a redirect-URI mismatch">
|
|
The redirect URL registered in the Slack app must match the one IronClaw sends character
|
|
for character. Check the scheme, the host, the port, and that the path is the full
|
|
`/api/reborn/product-auth/oauth/slack/callback`.
|
|
</Accordion>
|
|
|
|
<Accordion title="The bot doesn't respond in a channel">
|
|
Invite the bot to that channel, and make sure you subscribed to the matching message
|
|
event. A bot only sees conversations it belongs to.
|
|
</Accordion>
|
|
|
|
<Accordion title="Requests are rejected">
|
|
A wrong or missing signing secret makes IronClaw reject every incoming request. Re-enter
|
|
it through the setup flow, then restart the server.
|
|
</Accordion>
|
|
|
|
<Accordion title="Replies fail with feature_disabled, not_agent_app, or missing_scope">
|
|
The app is installed without Slack's Agents feature, or its bot token predates it. Enable
|
|
**Agents** in the app settings (the manifest's `features.agent_view` block), confirm the
|
|
bot scopes include `assistant:write` and `chat:write`, reinstall the app, and update the
|
|
bot token under **Admin → Configuration** if Slack issued a new one. IronClaw does not
|
|
fall back to plain messages when the Agent surface is unavailable; the failed delivery
|
|
names the missing capability.
|
|
</Accordion>
|
|
|
|
<Accordion title="The Stop button does not appear">
|
|
Slack shows it only while a session is processing and only when the app subscribes to
|
|
`agent_session_stopped`. Add the event subscription (it is in the manifest above) and
|
|
reinstall the app.
|
|
</Accordion>
|
|
|
|
<Accordion title="I can't find where to configure Slack">
|
|
The instance-wide credentials live under **Admin → Configuration**, on the **Slack
|
|
deployment configuration** card. The **Extensions** page handles the personal half —
|
|
installing the extension and connecting your own Slack account.
|
|
</Accordion>
|
|
|
|
</AccordionGroup>
|