A TypeScript-based bridge that forwards messages between channels on different Mattermost instances. Uses regular user accounts for authentication - no bot setup required!
This project was vibe coded with Claude Opus 4
The left mattermost (the one we want to monitor messages on) has a user posting a message:
The bridge will mirror the message to the right mattermost (the destination mattermost)
The console / app log
The bridge includes a built-in status channel that provides real-time monitoring of bridge activity and logs. When enabled with STATS_CHANNEL_UPDATES=logs, it creates a dedicated #mattermost-bridge-status channel that continuously updates with:
- Event summaries - Message counts and bridge activity
- Live log feed - Last 30 log lines updated every 10 minutes (respects TIMEZONE setting)
- Single message updates - No channel spam, just one continuously updated status message
This gives you an always up-to-date view of your bridge health without needing to check Docker logs or the console.
The bridge listens for messages on a source channel and forwards them to a target channel on a different Mattermost instance, preserving:
- User avatars
- File attachments
- Message formatting
- Original context (timestamp, channel, server)
- Cross-server messaging - Bridge channels between any two Mattermost instances
- Profile picture sync - Downloads and re-uploads user avatars with intelligent caching
- File attachment support - Seamlessly forwards all file attachments
- MFA/2FA support - Works with multi-factor authentication enabled accounts
- Email domain filtering - Exclude messages from specific email domains
- Minimal attachments - Clean, baby blue message formatting with profile pictures
- Message catch-up - Automatically recover missed messages when bridge restarts (with Docker volume persistence)
# Clone and setup
git clone <repository-url>
cd mattermost-bridge
npm install
# Run quick setup
./quick-start.shThe bridge uses environment variables for configuration. You can set these in a .env file or as environment variables in your deployment environment.
# Required: Mattermost Instances
MATTERMOST_LEFT_NAME=SourceServer
MATTERMOST_LEFT_SERVER=https://mattermost.source.com
MATTERMOST_LEFT_USERNAME=[email protected]
MATTERMOST_LEFT_PASSWORD_B64=<base64-encoded-password>
MATTERMOST_LEFT_TEAM=team-name
MATTERMOST_RIGHT_NAME=TargetServer
MATTERMOST_RIGHT_SERVER=https://mattermost.target.com
# Option 1: Use regular user account
MATTERMOST_RIGHT_USERNAME=[email protected]
MATTERMOST_RIGHT_PASSWORD_B64=<base64-encoded-password>
# Option 2: Use bot token (skips username/password if provided)
# MATTERMOST_RIGHT_BOT_TOKEN=your-bot-access-token
# Required: Channel IDs
SOURCE_CHANNEL_ID=abc123def456... # Single channel
# or
SOURCE_CHANNEL_ID=abc123,def456,ghi789 # Multiple channels (comma-separated)
TARGET_CHANNEL_ID=xyz789uvw012...| Variable | Description | Required | Default | Example |
|---|---|---|---|---|
| Left Mattermost (Source) | ||||
MATTERMOST_LEFT_NAME |
Display name for the source server | ✅ | - | SourceServer |
MATTERMOST_LEFT_SERVER |
URL of the source Mattermost server | ✅ | - | https://mattermost.source.com |
MATTERMOST_LEFT_USERNAME |
Username for authentication | ✅ | - | [email protected] |
MATTERMOST_LEFT_PASSWORD_B64 |
Base64-encoded password | ✅ | - | cGFzc3dvcmQxMjMh |
MATTERMOST_LEFT_TEAM |
Team name (for generating message links) | ✅ | - | team-name |
MATTERMOST_LEFT_MFA_SEED |
MFA/2FA seed (if MFA enabled) | ❌ | - | JBSWY3DPEHPK3PXP |
| Right Mattermost (Target) | ||||
MATTERMOST_RIGHT_NAME |
Display name for the target server | ✅ | - | TargetServer |
MATTERMOST_RIGHT_SERVER |
URL of the target Mattermost server | ✅ | - | https://mattermost.target.com |
MATTERMOST_RIGHT_BOT_TOKEN |
Bot access token (if using bot account) | ❌ | - | your-bot-access-token |
MATTERMOST_RIGHT_USERNAME |
Username for authentication (not needed if using bot token) | ✅* | - | [email protected] |
MATTERMOST_RIGHT_PASSWORD_B64 |
Base64-encoded password (not needed if using bot token) | ✅* | - | cGFzc3dvcmQxMjMh |
MATTERMOST_RIGHT_TEAM |
Team name (for message links) | ❌ | - | team-name |
MATTERMOST_RIGHT_MFA_SEED |
MFA/2FA seed (if MFA enabled, not used with bot token) | ❌ | - | GEZDGNBVGY3TQOJQ |
| Bridge Configuration | ||||
SOURCE_CHANNEL_ID |
ID of the channel(s) to monitor. Can be a single ID or comma-separated list | ✅ | - | abc123def456... or abc123,def456,ghi789 |
TARGET_CHANNEL_ID |
ID of the channel to post to | ✅ | - | xyz789uvw012... |
| Logging & Display | ||||
LOG_LEVEL |
Logging verbosity level | ❌ | info |
debug, info, warn, error |
DEBUG_WEBSOCKET_EVENTS |
Enable detailed WebSocket event logging | ❌ | false |
true |
EVENT_SUMMARY_INTERVAL_MINUTES |
How often to log event summaries | ❌ | 10 |
5 |
STATS_CHANNEL_UPDATES |
What to post to #mattermost-bridge-status | ❌ | none |
summary, logs |
none = disable status channel, summary = post event summaries, logs = combined summaries + last 30 log lines (updates single message) |
||||
DISABLE_EMOJI |
Disable emojis in console output | ❌ | false |
true |
TIMEZONE |
Timezone for timestamp formatting | ❌ | UTC |
Europe/Brussels, CET |
| Message Filtering | ||||
DONT_FORWARD_FOR |
Comma-separated email domains to exclude | ❌ | - | @excluded.com,@internal.org |
DRY_RUN |
Log messages without posting to target | ❌ | false |
true |
| Monitoring | ||||
HEARTBEAT_URL |
URL for uptime monitoring | ❌ | - | https://heartbeat.uptimerobot.com/... |
HEARTBEAT_INTERVAL_MINUTES |
How often to send heartbeat pings | ❌ | 15 |
5 |
| Message Catch-Up | ||||
ENABLE_CATCH_UP |
Enable catch-up mode to recover missed messages when bridge was offline | ❌ | false |
true |
CATCH_UP_PERSISTENCE_PATH |
Path to store message tracking state (Docker volume recommended) | ❌ | /data/tracking/message-state.json |
/custom/path/state.json |
MAX_MESSAGES_TO_RECOVER |
Maximum number of messages to recover per channel on startup | ❌ | 100 |
50, 200 |
| Appearance | ||||
FOOTER_ICON |
Custom icon URL for message footers | ❌ | - | https://example.com/icon.png |
LEFT_MESSAGE_EMOJI |
Emoji to add to original message after bridging | ❌ | - | envelope_with_arrow, white_check_mark |
Note: ✅* means required only if MATTERMOST_RIGHT_BOT_TOKEN is not provided.
Instead of using a regular user account for the target server, you can create a bot account:
-
Create Bot Account in Mattermost:
- Go to Integrations → Bot Accounts on your target Mattermost server
- Click Add Bot Account
- Configure the bot with a username and display name
- Grant appropriate permissions (typically needs to post messages)
- Copy the generated Access Token
-
Configure Bot Token:
MATTERMOST_RIGHT_BOT_TOKEN=your-bot-access-token
When using a bot token,
MATTERMOST_RIGHT_USERNAMEandMATTERMOST_RIGHT_PASSWORD_B64are not required. -
Bot Account Limitations:
- Status channel updates (
STATS_CHANNEL_UPDATES) are automatically disabled for bot accounts - Bot accounts cannot use MFA/2FA
- Messages will appear as coming from the bot user
- Status channel updates (
node encode-password.js "your-password-here"# Pull and run
docker pull clnio/mattermost-bridge:latest
docker run --env-file .env clnio/mattermost-bridge:latest
# Or use docker-compose
docker-compose up -dDocker images support both AMD64 and ARM64 architectures.
To enable message catch-up functionality, mount a volume for persistence:
# Create a named volume
docker volume create mattermost-bridge-data
# Run with catch-up enabled
docker run --env-file .env \
-e ENABLE_CATCH_UP=true \
-v mattermost-bridge-data:/data \
clnio/mattermost-bridge:latestOr in docker-compose.yml:
version: '3.8'
services:
mattermost-bridge:
image: clnio/mattermost-bridge:latest
env_file: .env
environment:
- ENABLE_CATCH_UP=true
volumes:
- mattermost-bridge-data:/data
restart: unless-stopped
volumes:
mattermost-bridge-data:The bridge will automatically track forwarded messages and catch up on missed messages when restarted.
# Set up local test environment
./local-env-setup.sh
# Start development
npm run dev
# Run tests
npm test- Left: http://localhost:8065/left (user:
left/leftpass123!) - Right: http://localhost:9065/right (user:
right/rightpass123!)
Authentication Failed
- Verify base64 encoded passwords
- Check MFA seed if using 2FA
- Ensure user has channel access
Channel Not Found
- Verify channel IDs are correct
- Use
mmctl channel search "channel-name" --team "team-name"
Debug Mode
LOG_LEVEL=debug
DEBUG_WEBSOCKET_EVENTS=truenpm start- Start the bridgenpm run dev- Development modenpm test- Run tests./build.sh- Build Docker image./push-live.sh- Push to Docker Hub./local-env-setup.sh- Set up local test environment
- Fork the repository
- Create your feature branch
- Commit your changes
- Push to the branch
- Open a Pull Request
MIT License - see the LICENSE file for details.



