An experimental bidirectional bridge connecting Mattermost and Slack channels. Not affiliated with Mattermost or Slack.
- Architecture - System design and component overview
- API Reference - Module and function documentation
- Development Guide - Local setup and testing
- Deployment Guide - Production deployment strategies
- Custom Emoji Support - Slack custom emoji synchronization
- Sharding Guide - Distributed deployments with sharding
- Environment Variables - Complete configuration reference
- Features
- Quick Start
- Prerequisites
- Installation
- Configuration
- Usage
- Troubleshooting
- Roadmap
- Contributing
- Bidirectional messaging between Slack and Mattermost channels
- Multiple channel pairs - bridge multiple Slack-Mattermost channel pairs simultaneously
- Advanced channel mapping - flexible JSON-based configuration for channel routing
- User mapping & identity spoofing - map user IDs to custom display names and avatars
- Thread support - replies stay organized in threads
- File sharing - attachments sync between platforms
- Message editing - edits propagate to the other platform
- Message deletion - deletions sync bidirectionally
- Reaction synchronization - emoji reactions sync bidirectionally with automatic translation
- Slack custom emoji support - automatic detection and sync of workspace custom emojis
- Username & avatar preservation - see who sent each message
- Flexible storage backend - Redis (default) or in-memory storage for development
- Prometheus metrics -
/metricsendpoint for monitoring and observability - Exponential backoff reconnection - smart reconnection with jitter to avoid thundering herd
- Critical error alerting - send alerts to configured Slack or Mattermost channel
- Periodic health checks - automatic status messages to monitor bridge health
- Auto-reconnection - WebSocket reconnects automatically on disconnect
- Structured logging - contextual logging with configurable log levels
- Docker deployment - ready-to-use Docker and docker-compose setup
- Health monitoring - built-in health check endpoint for container orchestration
- Worker pools - concurrent message processing for high-volume scenarios
- Performance benchmarking - built-in tools for performance analysis
- Slash commands - interactive bridge control and monitoring via
/bridgecommand - User presence synchronization - sync online/away/offline status between platforms
- Optional sharding - distribute load across multiple instances for high availability
- Direct messages (DMs)
- Huddles/voice channels
# Clone the repository
git clone https://github.com/markkr125/mattermost-slack-bridge.git
cd mattermost-slack-bridge
# Configure environment
cp .env.example .env
# Edit .env with your settings
# Start with Docker Compose
docker-compose up -d
# View logs
docker-compose logs -f bridge-app# Clone the repository
git clone https://github.com/markkr125/mattermost-slack-bridge.git
cd mattermost-slack-bridge
# Install dependencies
npm install
# Set up Redis (required)
# On macOS: brew install redis && brew services start redis
# On Ubuntu: sudo apt-get install redis-server && sudo service redis start
# On Docker: docker run -d -p 6379:6379 redis:alpine
# Configure environment
cp .env.example .env
# Edit .env with your settings
# Start the bridge
npm start- Node.js 14 or higher (required for ES6 features and regex lookbehinds)
- Redis server (for persistent message mapping)
- Slack workspace admin access to create an app
- Mattermost instance with admin access
git clone https://github.com/markkr125/mattermost-slack-bridge.git
cd mattermost-slack-bridge
npm installThe bridge requires Redis to store message mappings persistently.
Option A: Local Redis
# macOS
brew install redis
brew services start redis
# Ubuntu/Debian
sudo apt-get install redis-server
sudo service redis start
# Verify it's running
redis-cli ping # Should return "PONG"Option B: Docker
docker run -d --name redis -p 6379:6379 redis:alpineOption C: Cloud Redis (RedisLabs, AWS ElastiCache, etc.)
- Use the connection URL provided by your service
cp .env.example .envEdit .env with your credentials (see Configuration section below).
The bridge supports two configuration modes:
For a simple single channel pair, use environment variables:
SLACK_CHANNEL_ID=C0123456789
MM_CHANNEL_ID=abcde12345For multiple channel pairs, use the CHANNEL_MAPPINGS environment variable with JSON format:
CHANNEL_MAPPINGS=[{"slack":"C0123456789","mattermost":"abcde12345"},{"slack":"C9876543210","mattermost":"zyxwv98765"}]Configuration Examples:
Two channel pairs:
CHANNEL_MAPPINGS=[{"slack":"C01ABC123","mattermost":"ch1abc123"},{"slack":"C02DEF456","mattermost":"ch2def456"}]Three channel pairs:
# Note: Multi-line formatting shown below is for readability only.
# The actual .env value must be on a single line or properly escaped for your shell.
CHANNEL_MAPPINGS=[{"slack":"C01ABC123","mattermost":"ch1abc123"},{"slack":"C02DEF456","mattermost":"ch2def456"},{"slack":"C03GHI789","mattermost":"ch3ghi789"}]Notes:
- If
CHANNEL_MAPPINGSis set, it takes precedence overSLACK_CHANNEL_IDandMM_CHANNEL_ID - Each mapping requires both
slackandmattermostfields - The bridge will log all configured channel mappings on startup
- Thread mappings are kept separate per channel pair (no cross-channel thread confusion)
The bridge supports two storage backends for message mappings:
Option 1: Redis (Recommended for Production)
STORAGE_BACKEND=redis
REDIS_URL=redis://localhost:6379
REDIS_EXPIRY_DAYS=180 # Default: 6 months- Pros: Persistent storage, survives restarts, suitable for production
- Cons: Requires Redis server installation
Option 2: In-Memory (Development/Testing)
STORAGE_BACKEND=memory- Pros: No external dependencies, instant setup
- Cons: All mappings lost on restart, not suitable for production
Configuration Options:
STORAGE_BACKEND:redis(default) ormemoryREDIS_URL: Connection string for your Redis instance (when using Redis backend)REDIS_EXPIRY_DAYS: How long to keep message mappings (default: 180 days)
Override user identities for custom display names and avatars:
# Map Slack users to custom Mattermost display names/avatars
SLACK_USER_MAPPINGS={"U12345":{"mm_user_id":"abc123","display_name":"John Doe","avatar_url":"https://example.com/avatar.jpg"}}
# Map Mattermost users to custom Slack display names/avatars
MM_USER_MAPPINGS={"abc123":{"slack_user_id":"U12345","display_name":"John Doe","avatar_url":"https://example.com/avatar.jpg"}}Use Cases:
- Consistent usernames across platforms
- Custom avatars for bots or service accounts
- Override default username/avatar fetching
- Fix mismatched identities
The bridge exposes Prometheus-compatible metrics at /metrics endpoint:
Available Metrics:
bridge_messages_total- Counter of messages bridged per directionbridge_message_latency_seconds- Histogram of message processing latencybridge_reconnections_total- Counter of reconnection attemptsbridge_failed_events_total- Counter of failed eventsbridge_connection_status- Gauge of connection status (1=connected, 0=disconnected)- Plus default Node.js metrics (CPU, memory, etc.)
Example Prometheus scrape config:
scrape_configs:
- job_name: 'mattermost-slack-bridge'
static_configs:
- targets: ['localhost:3000']Configure critical error alerts and periodic health checks:
# Channel to send alerts (format: 'slack:CHANNEL_ID' or 'mm:CHANNEL_ID')
ALERT_CHANNEL=slack:C0123456789
# Periodic health check interval in minutes (default: 60)
HEALTH_CHECK_INTERVAL_MINUTES=60Alert Types:
- Critical alerts: Authentication failures, permanent errors, reconnection failures
- Status messages: Periodic "Bridge healthy" messages
- Error notifications: WebSocket errors, initialization failures
The bridge uses exponential backoff with jitter for smart reconnections.
Configure logging levels and behavior:
LOG_LEVEL=info # Options: error, warn, info, debugLog Levels:
error: Only critical errorswarn: Warnings and errorsinfo: General information, warnings, and errors (default)debug: Detailed debugging information (includes all levels)
The bridge uses structured logging with contextual information for better monitoring and troubleshooting.
- Go to https://api.slack.com/apps
- Click Create New App β From scratch
- Name your app (e.g., "Mattermost Bridge") and select your workspace
-
Navigate to OAuth & Permissions in the sidebar
-
Scroll to Bot Token Scopes and add these scopes:
Scope Purpose chat:writeSend messages chat:write.customizeUse custom usernames and avatars files:readAccess shared files channels:historyRead channel messages channels:readView channel info users:readGet user profiles reactions:readRead emoji reactions reactions:writeAdd/remove emoji reactions -
Click Install to Workspace at the top
-
Copy the Bot User OAuth Token (starts with
xoxb-) β This is yourSLACK_BOT_TOKEN
-
Go to Event Subscriptions in the sidebar
-
Toggle Enable Events to On
-
Set Request URL to:
http://your-server:3000/slack/events- For local development, use ngrok:
ngrok http 3000 - For Docker deployment, ensure your container is accessible
- Your server must be running for Slack to verify this URL
- For local development, use ngrok:
-
Under Subscribe to bot events, add:
message.channels- Listen for channel messagesreaction_added- Listen for reactions being addedreaction_removed- Listen for reactions being removed
-
Save Changes
- Signing Secret: Go to Basic Information β App Credentials β Copy the Signing Secret
- Channel ID:
- Open Slack, right-click your target channel
- Select View channel details
- Scroll to the bottom and copy the Channel ID (e.g.,
C0123456789)
Add these to your .env:
SLACK_BOT_TOKEN=xoxb-your-token-here
SLACK_SIGNING_SECRET=your-signing-secret
SLACK_CHANNEL_ID=C0123456789- Log in to Mattermost as System Admin
- Go to System Console β Integrations β Integration Management
- Enable the following settings:
- β Enable Bot Accounts
- β Enable Personal Access Tokens
- β Enable integrations to override usernames
- β Enable integrations to override profile picture icons
- Exit System Console and go to Main Menu β Integrations
- Select Bot Accounts β Add Bot Account
- Configure the bot:
- Username: e.g.,
slack-bridge - Display Name: e.g.,
Slack Bridge - Role: Enable post:all permission
- Username: e.g.,
- Click Create Bot Account
- Copy the Access Token β This is your
MM_TOKEN
- Open the channel you want to bridge
- Click the channel name β View Info
- The URL will show the channel ID (e.g.,
abcde12345)- Example:
https://your.mattermost.com/team/channels/abcde12345
- Example:
Add these to your .env:
MM_TOKEN=your-mattermost-token
MM_URL=https://your.mattermost.com
MM_CHANNEL_ID=abcde12345With Docker:
docker-compose up -d
docker-compose logs -f bridge-appLocally:
npm startYou should see:
[2026-02-05 10:30:00] INFO [redis]: Redis connected successfully
[2026-02-05 10:30:00] INFO [main]: Slack bot authenticated
[2026-02-05 10:30:00] INFO [main]: Mattermost bot authenticated
[2026-02-05 10:30:00] INFO [main]: Mattermost WebSocket connected
[2026-02-05 10:30:00] INFO [main]: Bridge server started
- Send a message in your Slack channel β should appear in Mattermost
- Send a message in Mattermost β should appear in Slack
- Edit a message on either platform β edit syncs to the other
- Delete a message β deletion syncs bidirectionally
- Add a reaction (emoji) β reaction syncs to the other platform
- Remove a reaction β removal syncs bidirectionally
The bridge exposes a health check endpoint for monitoring:
curl http://localhost:3000/healthResponse:
{
"status": "operational",
"service": "mattermost-slack-bridge",
"timestamp": "2026-02-05T10:30:00.000Z"
}Error: Redis connection refused
# Check if Redis is running
redis-cli ping
# If not running:
# macOS: brew services start redis
# Linux: sudo service redis-server start
# Docker: docker start redisError: url_verification failed
- Ensure your bridge is running before setting up the Request URL
- For local development, use ngrok:
ngrok http 3000 - Check that PORT in
.envmatches your server port
- Check logs for error messages
- Verify bot permissions:
- Slack bot is added to the channel
- Mattermost bot has access to the channel
- Check Redis:
redis-cli > KEYS * # Should show stored message mappings
The bridge automatically reconnects after 5 seconds. If it keeps disconnecting:
- Check your Mattermost server status
- Verify
MM_TOKENis valid and hasn't expired - Check firewall settings
Container won't start:
# Check logs
docker-compose logs bridge-app
# Verify environment variables
docker-compose config
# Restart services
docker-compose restartHealth check failing:
# Test health endpoint
docker exec mattermost-slack-bridge wget -q -O- http://localhost:3000/health- Configure environment:
cp .env.example .env
# Edit .env with your actual credentials- Start the services:
docker-compose up -d- View logs:
docker-compose logs -f- Stop the services:
docker-compose down- Start Redis:
docker run -d --name bridge-redis \
-p 6379:6379 \
redis:7-alpine- Build the bridge image:
docker build -t mattermost-slack-bridge .- Run the bridge:
docker run -d --name bridge-app \
--link bridge-redis:redis \
-p 3000:3000 \
-e SLACK_BOT_TOKEN=xoxb-your-token \
-e SLACK_SIGNING_SECRET=your-secret \
-e MM_TOKEN=your-mm-token \
-e MM_URL=https://your.mattermost.com \
-e CHANNEL_MAPPINGS='[{"slack":"C123","mattermost":"mm123"}]' \
-e REDIS_URL=redis://redis:6379 \
-e LOG_LEVEL=info \
mattermost-slack-bridgeAll the same environment variables from .env.example can be passed to Docker:
SLACK_BOT_TOKEN- Slack bot OAuth tokenSLACK_SIGNING_SECRET- Slack app signing secretMM_TOKEN- Mattermost personal access tokenMM_URL- Mattermost server URLCHANNEL_MAPPINGS- JSON array of channel mappingsREDIS_URL- Redis connection URLREDIS_EXPIRY_DAYS- Message mapping expiry (default: 180)LOG_LEVEL- Logging level (error, warn, info, debug)PORT- Server port (default: 3000)
The bridge includes a concurrent message processor for handling high-volume message traffic efficiently.
Configuration:
WORKER_POOL_SIZE=10 # Number of concurrent message processing workers (default: 10)The worker pool automatically queues incoming messages and processes them concurrently up to the configured limit, preventing overload and ensuring smooth operation even during traffic spikes.
Usage in Code:
const { MessageProcessor } = require('./src/utils/message-processor');
const processor = new MessageProcessor({ maxConcurrent: 10, name: 'messages' });
// Submit tasks for concurrent processing
await processor.submit(async () => {
// Process message
});
// Get statistics
const stats = processor.snapshot();
console.log(`Completed: ${stats.completed}, Errors: ${stats.errors}`);Built-in performance monitoring and benchmarking tools help you measure and optimize bridge performance.
Running Benchmarks:
# Run all benchmarks
node tools/benchmark-cli.js --scenario all --iterations 100
# Run specific benchmark
node tools/benchmark-cli.js --scenario message-processing --iterations 50
# Test concurrent processing
node tools/benchmark-cli.js --scenario concurrent --pool-size 20 --iterations 200Available Scenarios:
message-processing- Message processing throughputconcurrent- Concurrent message handling with worker poolsfile-ops- File operation simulationall- Run all benchmarks
In-Application Monitoring:
const { perfMonitor } = require('./src/utils/perf-monitor');
// Track operation performance
const { output, measurement } = await perfMonitor.track('my-operation', async () => {
// Your code here
return result;
});
console.log(`Duration: ${measurement.durationMs}ms`);
// Generate report
console.log(perfMonitor.report());Interactive bridge control and monitoring via slash commands in both Slack and Mattermost.
Setup for Slack:
- Go to your Slack app configuration β Slash Commands
- Click Create New Command
- Configure the command:
- Command:
/bridge - Request URL:
https://your-server:3000/slack/events - Short Description:
Control and monitor the Mattermost-Slack bridge - Usage Hint:
[status|stats|perf|help]
- Command:
- Save the command
Setup for Mattermost:
- Go to System Console β Integrations β Slash Commands
- Click Add Slash Command
- Configure:
- Command Trigger Word:
bridge - Request URL:
https://your-server:3000/mattermost/commands - Request Method:
POST - Response Username:
Bridge Bot
- Command Trigger Word:
- Save the command
Available Commands:
# Show bridge status and uptime
/bridge status
# Display connection statistics
/bridge stats
# View performance metrics
/bridge perf
# Show help message
/bridge helpAll command responses are ephemeral (only visible to you).
Synchronize user online/away/offline status between Slack and Mattermost.
Configuration:
# Enable presence synchronization
PRESENCE_SYNC_ENABLED=true
# Sync interval in minutes (default: 5)
PRESENCE_SYNC_INTERVAL_MINUTES=5How It Works:
- Slack β Mattermost: When a Slack user's presence changes (active/away), their Mattermost status is updated
- Periodic Sync: Every N minutes, all user presences are synchronized from Slack to Mattermost
- Status Mapping:
- Slack
activeβ Mattermostonline - Slack
awayβ Mattermostaway
- Slack
Note: Due to Slack API limitations, bots cannot set user presence in Slack, so Mattermost β Slack presence sync is not fully supported.
User Mapping:
Presence sync requires user mappings to be configured. Users will be automatically mapped when they send messages, or you can configure explicit mappings in your user mappings configuration.
Example:
const { registerPresenceMapping } = require('./src/handlers/presence');
// Register user mapping for presence sync
registerPresenceMapping('SLACK_USER_ID', 'MM_USER_ID');Better error handlingPersistent message storage with RedisEdit/delete message supportSupport multiple channel pairsAdvanced channel mapping configurationReaction synchronizationBetter logging and monitoringDocker deployment optionFlexible storage backends (Redis + in-memory)User mapping and identity spoofingPrometheus metrics and observabilityExponential backoff reconnectionsCritical error alertingPeriodic health status monitoringWorker pools for high-volume scenariosPerformance benchmarking toolsSlash commands supportUser presence synchronizationSlack custom emoji supportOptional sharding for distributed deployments
- Enhanced custom emoji management (auto-upload to Mattermost)
- Bidirectional custom emoji sync
- Dynamic shard rebalancing
Contributions are welcome! Please feel free to submit issues or pull requests.
Local Development:
git clone https://github.com/markkr125/mattermost-slack-bridge.git
cd mattermost-slack-bridge
npm install
cp .env.example .env
# Edit .env with your test credentials
npm startWith Docker:
docker-compose up --buildFor detailed development instructions, see Development Guide.
npm test # Run all tests
npm run test:watch # Run tests in watch mode
npm run test:coverage # Run tests with coverage reportThe project uses GitHub Actions for automated testing:
- Tests run automatically on pull requests to
main - Includes unit tests with 70%+ coverage requirement
- Tests run against Node.js 18 with Redis 7
The project has been refactored into a modular structure:
src/
βββ config/ # Configuration management
βββ handlers/ # Message handlers for Slack and Mattermost
βββ storage/ # Redis storage layer
βββ utils/ # Utility functions (markdown conversion)
βββ index.js # Main application entry point
__tests__/ # Jest unit tests
docs/ # Documentation
See Architecture Documentation for more details.
If you encounter issues:
- Check the Troubleshooting section
- Review existing GitHub Issues
- Open a new issue with:
- Bridge version
- Node.js version
- Error logs
- Steps to reproduce
Note: This is an experimental project and not officially supported by Mattermost or Slack.