Slack Simulator

A local, browser-based simulator for developing and testing Slack apps and bots

Technical Specification

System Requirements

Installation

# Clone the repository
git clone https://github.com/clydedz/slack-simulator.git
cd slack-simulator

# Install dependencies
yarn install

# Start development server
yarn dev

The simulator starts both the API server (port 4500) and the SPA dev server (port 5173). Access the SPA at http://localhost:5173. Refer to the getting started documentation for more details on further details on how to customize the simulator and configure it to work with your app locally.

High-Level Architecture

┌────────────────────────────────────────────────────────────────┐
│  Browser (http://localhost:PORT)                               │
│  ┌──────────────────────────────────────────────────────────┐  │
│  │  Simulator SPA — Workspace UI + Admin/API Console        │  │
│  │  (Identity Switcher, Channel List, Message Composer)     │  │
│  └──────────────────────────────────────────────────────────┘  │
│                          │ REST + WebSocket (control plane)    │
└──────────────────────────┼─────────────────────────────────────┘
                           │
                           ▼
┌────────────────────────────────────────────────────────────────┐
│  Simulator Server (http://localhost:PORT)                      │
│  ┌──────────────────────────────────────────────────────────┐  │
│  │                  ┌────────────────────┐                  │  │
│  │                  │ Control API        │                  │  │
│  │                  │ (SPA ↔ Domain Core)│                  │  │
│  │                  └────────┬───────────┘                  │  │
│  │                           │                              │  │
│  │  ┌────────────────────────▼───────────────────────────┐  │  │
│  │  │ Domain Core + Dispatcher                           │  │  │
│  │  │ ┌─────────────────┐  ┌──────────────┐              │  │  │
│  │  │ │ Fake Slack Web  │  │ Socket Mode  │              │  │  │
│  │  │ │ API (/api/*)    │  │ (/ws/socket) │              │  │  │
│  │  │ └────────┬────────┘  └───────┬──────┘              │  │  │
│  │  │          │                   │                     │  │  │
│  │  │          └─────────┬─────────┘                     │  │  │
│  │  │                    │                               │  │  │
│  │  │ (workspace, channels, messages, users, apps)       │  │  │
│  │  └────────────────────┼───────────────────────────────┘  │  │
│  │                       │                                  │  │
│  │          ┌────────────▼───────────┐                      │  │
│  │          │ Simulator Database     │                      │  │
│  │          │ (SQLite)               │                      │  │
│  │          └────────────────────────┘                      │  │
│  └──────────────────────────────────────────────────────────┘  │
└──────────────────────────┼─────────────────────────────────────┘
                           │ HTTP + WebSocket
                           ▼
┌────────────────────────────────────────────────────────────────┐
│  Your Slack App                                                │
│  (Bolt framework or raw @slack/web-api)                        │
│  — configured to point at http://localhost:PORT/api/           │
└────────────────────────────────────────────────────────────────┘

Simulator Tech Stack

Package scripts:

How your Slack app connects to the Slack Simulator

Auth model is deliberately lightweight — this is local-only, not a security boundary.

Bolt configuration

import { App } from '@slack/bolt';

const app = new App({
  token: process.env.SLACK_BOT_TOKEN,
  signingSecret: process.env.SLACK_SIGNING_SECRET,
  socketMode: true,
  appToken: process.env.SLACK_APP_TOKEN,
  clientOptions: { slackApiUrl: 'http://localhost:4500/api/' },
});

For Socket Mode, Bolt calls apps.connections.open to discover a wss:// URL. The Slack Simulator returns its own ws://localhost:PORT/ws/socket-mode?ticket=… URL and speaks the Socket Mode envelope protocol (hello, events_api, interactive, slash_commands, disconnect, with envelope_id + ack).

For Events / Interactivity over HTTP, request URLs are configured in apps.json via the requestUrl field. The Slack Simulator's outbound dispatcher POSTs to those URLs with proper X-Slack-Signature and X-Slack-Request-Timestamp headers signed with the per-app signing secret.

Everything else (signed payload shape, retry-on-non-2xx behavior, response_url tokens with TTLs) mirrors Slack's documented contract.

The slack-simulator folder

The .slack-simulator directory is created automatically in the project root when the simulator starts. It contains:

The folder is generated on first startup if it doesn't exist. The database is seeded from config/seed.json and config/apps.json when the workspace table is empty. The credentials file is regenerated whenever the database is seeded.

Why a single Node process

The simulator is designed for local development only. When running the built version (vs Vite dev mode), the server serves both the SPA and the API from the same port. In development, the SPA runs via Vite's dev server on a separate port while the API server runs on port 4500. Co-locating them in the built version means:


Testing Tech Stack

Package scripts:


Documentation Tech Stack

Package scripts:


References