Skip to content

Commit e8a3a97

Browse files
haydenbleaselclaude
andcommitted
Add socket mode documentation to Slack adapter README
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 9d8581c commit e8a3a97

File tree

2 files changed

+468
-40
lines changed

2 files changed

+468
-40
lines changed

packages/adapter-slack/README.md

Lines changed: 87 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,85 @@ openssl rand -base64 32
9898

9999
When `encryptionKey` is set, `setInstallation()` encrypts the token before storing and `getInstallation()` decrypts it transparently.
100100

101+
## Socket mode
102+
103+
For environments behind firewalls that can't expose public HTTP endpoints, the adapter supports [Slack Socket Mode](https://api.slack.com/apis/socket-mode). Instead of receiving webhooks, the adapter connects to Slack over a WebSocket.
104+
105+
```typescript
106+
import { Chat } from "chat";
107+
import { createSlackAdapter } from "@chat-adapter/slack";
108+
109+
const bot = new Chat({
110+
userName: "mybot",
111+
adapters: {
112+
slack: createSlackAdapter({
113+
mode: "socket",
114+
appToken: process.env.SLACK_APP_TOKEN!,
115+
botToken: process.env.SLACK_BOT_TOKEN!,
116+
}),
117+
},
118+
});
119+
```
120+
121+
### Slack app setup for socket mode
122+
123+
1. Go to your app's settings at [api.slack.com/apps](https://api.slack.com/apps)
124+
2. Navigate to **Socket Mode** and enable it
125+
3. Generate an **App-Level Token** with the `connections:write` scope — this is your `SLACK_APP_TOKEN` (`xapp-...`)
126+
4. Event subscriptions and interactivity still need to be configured, but no public request URL is required
127+
128+
> Socket mode is not compatible with multi-workspace OAuth (`clientId`/`clientSecret`). It's designed for single-workspace deployments.
129+
130+
### Socket mode on serverless (Vercel)
131+
132+
Socket mode requires a persistent WebSocket connection, which doesn't fit the request/response model of serverless functions. The adapter provides a forwarding mechanism to bridge this gap:
133+
134+
1. A cron job periodically starts a transient socket listener
135+
2. The listener connects via WebSocket, acks events immediately, and forwards them as HTTP requests to your webhook endpoint
136+
3. Your existing webhook route processes the forwarded events normally
137+
138+
```typescript
139+
// api/slack/socket-mode/route.ts
140+
import { bot, slackAdapter } from "@/lib/bot";
141+
142+
export const maxDuration = 800;
143+
144+
export async function GET(request: Request) {
145+
// Verify cron secret
146+
const authHeader = request.headers.get("authorization");
147+
if (authHeader !== `Bearer ${process.env.CRON_SECRET}`) {
148+
return new Response("Unauthorized", { status: 401 });
149+
}
150+
151+
await bot.initialize();
152+
153+
const webhookUrl = `${process.env.VERCEL_URL}/api/webhooks/slack`;
154+
155+
await slackAdapter.startSocketModeListener(
156+
{ forwardTo: webhookUrl },
157+
600_000 // 10 minutes
158+
);
159+
160+
return new Response("OK");
161+
}
162+
```
163+
164+
Schedule the cron job to run every 9 minutes (overlapping with the 10-minute listener duration) to maintain continuous coverage:
165+
166+
```json
167+
// vercel.json
168+
{
169+
"crons": [
170+
{
171+
"path": "/api/slack/socket-mode",
172+
"schedule": "*/9 * * * *"
173+
}
174+
]
175+
}
176+
```
177+
178+
Forwarded events are authenticated using the `socketForwardingSecret` config option (defaults to `SLACK_SOCKET_FORWARDING_SECRET` env var, falling back to `appToken`).
179+
101180
## Slack app setup
102181

103182
### 1. Create a Slack app from manifest
@@ -184,19 +263,25 @@ All options are auto-detected from environment variables when not provided. You
184263
|--------|----------|-------------|
185264
| `botToken` | No | Bot token (`xoxb-...`). Auto-detected from `SLACK_BOT_TOKEN` |
186265
| `signingSecret` | No* | Signing secret for webhook verification. Auto-detected from `SLACK_SIGNING_SECRET` |
266+
| `mode` | No | Connection mode: `"webhook"` (default) or `"socket"` |
267+
| `appToken` | No** | App-level token (`xapp-...`) for socket mode. Auto-detected from `SLACK_APP_TOKEN` |
268+
| `socketForwardingSecret` | No | Shared secret for authenticating forwarded socket events. Auto-detected from `SLACK_SOCKET_FORWARDING_SECRET`, falls back to `appToken` |
187269
| `clientId` | No | App client ID for multi-workspace OAuth. Auto-detected from `SLACK_CLIENT_ID` |
188270
| `clientSecret` | No | App client secret for multi-workspace OAuth. Auto-detected from `SLACK_CLIENT_SECRET` |
189271
| `encryptionKey` | No | AES-256-GCM key for encrypting stored tokens. Auto-detected from `SLACK_ENCRYPTION_KEY` |
190272
| `installationKeyPrefix` | No | Prefix for the state key used to store workspace installations. Defaults to `slack:installation`. The full key is `{prefix}:{teamId}` |
191273
| `logger` | No | Logger instance (defaults to `ConsoleLogger("info")`) |
192274

193-
*`signingSecret` is required — either via config or `SLACK_SIGNING_SECRET` env var.
275+
*`signingSecret` is required for webhook mode — either via config or `SLACK_SIGNING_SECRET` env var.
276+
**`appToken` is required for socket mode — either via config or `SLACK_APP_TOKEN` env var.
194277

195278
## Environment variables
196279

197280
```bash
198281
SLACK_BOT_TOKEN=xoxb-... # Single-workspace only
199-
SLACK_SIGNING_SECRET=...
282+
SLACK_SIGNING_SECRET=... # Required for webhook mode
283+
SLACK_APP_TOKEN=xapp-... # Required for socket mode
284+
SLACK_SOCKET_FORWARDING_SECRET=... # Optional, for socket event forwarding auth
200285
SLACK_CLIENT_ID=... # Multi-workspace only
201286
SLACK_CLIENT_SECRET=... # Multi-workspace only
202287
SLACK_ENCRYPTION_KEY=... # Optional, for token encryption

0 commit comments

Comments
 (0)