Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Add Slack Socket Mode support#162

Draft
haydenbleasel wants to merge 19 commits intomainfrom
123-slack-socket-mode-support
Draft

Add Slack Socket Mode support#162
haydenbleasel wants to merge 19 commits intomainfrom
123-slack-socket-mode-support

Conversation

@haydenbleasel
Copy link
Member

@haydenbleasel haydenbleasel commented Mar 2, 2026

Summary

  • Adds serverless-compatible Socket Mode forwarding to the Slack adapter, mirroring the Discord gateway pattern
  • A cron-invoked listener maintains the WebSocket, acks events, and forwards them via HTTP POST to the existing webhook endpoint using an x-slack-socket-token header
  • The adapter stays in mode: "webhook" (default) — socket mode listener is a separate external concern
  • routeSocketEvent() now accepts WebhookOptions and uses waitUntil for async handlers
  • New startSocketModeListener() / runSocketModeListener() / forwardSocketEvent() methods
  • Example cron route at /api/slack/socket-mode with createPersistentListener for Redis-based cross-instance coordination
  • Cron runs every 9 min, listener duration 10 min (same as Discord)

Test plan

  • Forwarded event accepted with valid appToken → 200
  • Forwarded event rejected with invalid token → 401
  • Forwarded event rejected when no appToken configured → 401
  • Bypasses signature verification for forwarded events
  • Options passthrough to handlers for forwarded events
  • startSocketModeListener returns 200 with valid config
  • startSocketModeListener returns 500 without waitUntil or appToken
  • routeSocketEvent passes options through to handlers
  • All 258 tests pass
  • Full pnpm validate passes (knip, check, typecheck, test, build)

Closes #123

@haydenbleasel haydenbleasel linked an issue Mar 2, 2026 that may be closed by this pull request
1 task
@vercel
Copy link
Contributor

vercel bot commented Mar 2, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
chat Ready Ready Preview, Comment, Open in v0 Mar 3, 2026 1:06am
chat-sdk-nextjs-chat Ready Ready Preview, Comment, Open in v0 Mar 3, 2026 1:06am

@socket-security
Copy link

socket-security bot commented Mar 2, 2026

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​slack/​socket-mode@​2.0.59910010087100

View full report

…nteractive payloads can cause unhandled promise rejections that crash the Node.js process.

This commit fixes the issue reported at packages/adapter-slack/src/index.ts:1152

**Bug Analysis:**

In `routeSocketEvent` (line 1150), which is a synchronous `void` method, two async operations produce floating promises:

1.  `this.handleSlashCommand(params)` (line 1165) - `handleSlashCommand` is `async` and always returns a `Promise<Response>`. It calls `await this.lookupUser(userId)` which internally calls `await this.chat.getState().get()` (before the try/catch around the API call), and `this.chat.processSlashCommand()`. Any of these could throw.
    
2.  `this.dispatchInteractivePayload(payload)` (line 1172) - Returns `Response | Promise<Response>`. When the payload type is `view_submission`, it delegates to `async handleViewSubmission()`, which calls `await this.chat.processModalSubmit()` and accesses `payload.view.state.values` (which could throw on malformed payloads).
    

Since `routeSocketEvent` is synchronous (`void` return type) and called from a sync context within the socket mode event handler (after `await ack()` has already completed), these returned promises are fire-and-forget. If any reject, it triggers an unhandled promise rejection, which in Node.js 15+ terminates the process by default.

In contrast, in the webhook code path (`handleWebhook`), these same methods are always `return`-ed from async functions, so their promises are properly chained to the caller.

**Fix:**

Added `.catch()` handlers to both floating promises:

1.  For `handleSlashCommand`: Added `.catch()` that logs the error via `this.logger.error`.
2.  For `dispatchInteractivePayload`: Since it returns `Response | Promise<Response>` (only a Promise for `view_submission`), used `instanceof Promise` to conditionally attach a `.catch()` handler only when the result is a Promise.

This approach was chosen over making `routeSocketEvent` async because: (a) it doesn't change the method signature, (b) the caller doesn't need to await it (the ack has already been sent), and (c) errors are logged rather than silently swallowed.


Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
Co-authored-by: haydenbleasel <[email protected]>
haydenbleasel and others added 3 commits March 2, 2026 16:51
- Export SlackForwardedSocketEvent type
- Add x-slack-socket-token check at top of handleWebhook() for forwarded events
- Update routeSocketEvent() to accept WebhookOptions and use waitUntil
- Add startSocketModeListener(), runSocketModeListener(), forwardSocketEvent()

Co-Authored-By: Claude Opus 4.6 <[email protected]>
- Forwarded event accepted/rejected based on appToken
- Bypasses signature verification for forwarded events
- Options passthrough to handlers
- startSocketModeListener returns 200/500 appropriately

Co-Authored-By: Claude Opus 4.6 <[email protected]>
- New /api/slack/socket-mode route using createPersistentListener
- Mirrors Discord gateway pattern (CRON_SECRET auth, Redis coordination)
- Cron runs every 9 min, listener duration 10 min

Co-Authored-By: Claude Opus 4.6 <[email protected]>
haydenbleasel and others added 5 commits March 2, 2026 17:00
Make signingSecret optional (string | undefined) instead of falling
back to "". verifySignature now returns false when no secret is
configured, preventing HMAC with an empty key from silently passing.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Sync errors from processEventPayload were silently dropped in
socket mode. Wrap with try-catch for parity with slash_commands
and interactive cases.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Stop using the Slack app-level token (xapp-...) as the bearer token
for HTTP forwarding. Adds socketForwardingSecret config option
(auto-detected from SLACK_SOCKET_FORWARDING_SECRET) with fallback
to appToken for backwards compatibility.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Validate body.event exists and construct a properly typed
SlackWebhookPayload instead of using `as unknown as`.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Remove export — only used internally by the forwarding mechanism.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Slack Socket Mode Support

1 participant