Includes spec, plan, research, data model, contracts, and quickstart guide for mautrix-slack Socket Mode bridge deployment.
498 lines
16 KiB
YAML
498 lines
16 KiB
YAML
# Channel Mapping Contract: Slack↔Matrix Portal Configuration
|
|
|
|
# This contract documents how Slack channels map to Matrix rooms in the
|
|
# mautrix-slack bridge. Unlike traditional bridges with static channel mappings,
|
|
# mautrix-slack uses **automatic portal creation** based on activity.
|
|
|
|
# =============================================================================
|
|
# Automatic Portal Creation Model
|
|
# =============================================================================
|
|
|
|
# mautrix-slack does NOT use static configuration files for channel mappings.
|
|
# Instead, portals (Slack channel ↔ Matrix room pairs) are created automatically
|
|
# based on:
|
|
#
|
|
# 1. Initial Login: Bridge syncs recent conversations (controlled by conversation_count)
|
|
# 2. Message Receipt: Portal auto-created when message arrives in new Slack channel
|
|
# 3. Bot Membership: Channels where Slack bot is invited are auto-bridged
|
|
#
|
|
# This contract documents the EXPECTED structure for understanding operational
|
|
# behavior, not a configuration file to be edited.
|
|
|
|
# =============================================================================
|
|
# Portal Lifecycle
|
|
# =============================================================================
|
|
|
|
portal_lifecycle:
|
|
creation_triggers:
|
|
- event: "Initial authentication (`login app` command)"
|
|
action: "Bridge syncs N recent conversations (conversation_count)"
|
|
portals_created: "Up to conversation_count channels/DMs"
|
|
user_experience: "User receives Matrix room invitations"
|
|
|
|
- event: "Message received in unbridged Slack channel"
|
|
action: "Bridge auto-creates portal"
|
|
portals_created: "1 new portal for that channel"
|
|
user_experience: "Invitation to new Matrix room, message appears"
|
|
|
|
- event: "Slack bot invited to channel (App Login mode)"
|
|
action: "Bridge auto-creates portal"
|
|
portals_created: "1 new portal"
|
|
user_experience: "Matrix users can now interact with channel"
|
|
|
|
destruction_triggers:
|
|
- event: "Admin runs `delete-portal` command (if available)"
|
|
action: "Bridge kicks users from Matrix room, deletes room, removes DB entry"
|
|
result: "Portal destroyed, can be recreated if needed"
|
|
|
|
- event: "Slack channel deleted"
|
|
expected: "Portal becomes inactive (not documented behavior)"
|
|
recommendation: "Test in pilot deployment to confirm"
|
|
|
|
state_changes:
|
|
- from: "Nonexistent"
|
|
to: "Active"
|
|
trigger: "Any creation trigger above"
|
|
|
|
- from: "Active"
|
|
to: "Archived"
|
|
trigger: "Slack channel archived"
|
|
behavior: "Read-only, no new messages flow"
|
|
|
|
- from: "Archived"
|
|
to: "Active"
|
|
trigger: "Slack channel unarchived"
|
|
|
|
- from: "Active"
|
|
to: "Deleted"
|
|
trigger: "delete-portal command or Slack channel deleted"
|
|
|
|
# =============================================================================
|
|
# Portal Entity Structure
|
|
# =============================================================================
|
|
|
|
# Stored in bridge PostgreSQL database (table: portal)
|
|
|
|
portal_entity:
|
|
slack_channel_id:
|
|
type: "string"
|
|
format: "C[A-Z0-9]{8,10}" # Public channel
|
|
example: "C0123ABCDEF"
|
|
primary_key: true
|
|
description: "Slack channel identifier"
|
|
|
|
slack_channel_type:
|
|
type: "enum"
|
|
values:
|
|
- "public_channel" # Standard public channel
|
|
- "private_channel" # Private channel (groups)
|
|
- "dm" # 1:1 direct message
|
|
- "mpim" # Multi-party direct message (group DM)
|
|
- "connect" # Slack Connect shared channel
|
|
description: "Type of Slack conversation"
|
|
|
|
matrix_room_id:
|
|
type: "string"
|
|
format: "!([a-zA-Z0-9]+):clarun.xyz"
|
|
example: "!AbCdEfGhIjKlMnOp:clarun.xyz"
|
|
description: "Matrix room identifier (opaque ID)"
|
|
|
|
matrix_room_alias:
|
|
type: "string"
|
|
format: "#slack_([a-z0-9_-]+):clarun.xyz"
|
|
example: "#slack_dev-platform:clarun.xyz"
|
|
description: "Human-readable Matrix room alias"
|
|
notes:
|
|
- "Based on Slack channel name"
|
|
- "Lowercase, hyphens preserved, special chars removed"
|
|
- "May not exist for DMs"
|
|
|
|
channel_name:
|
|
type: "string"
|
|
example: "dev-platform"
|
|
description: "Slack channel name (without #)"
|
|
notes: "Synced from Slack, updated on channel rename"
|
|
|
|
topic:
|
|
type: "string"
|
|
example: "Platform development discussion"
|
|
description: "Channel topic/description"
|
|
synced: true
|
|
notes: "Updated when Slack topic changes"
|
|
|
|
avatar_url:
|
|
type: "mxc:// URI"
|
|
example: "mxc://clarun.xyz/AbCdEfGhIjKlMnOpQrStUvWxYz"
|
|
description: "Matrix Content URI for channel avatar"
|
|
notes: "Synced from Slack workspace icon"
|
|
|
|
encrypted:
|
|
type: "boolean"
|
|
default: false
|
|
description: "Whether Matrix room has encryption enabled"
|
|
notes: "Controlled by bridge.encryption.default config"
|
|
|
|
in_space:
|
|
type: "boolean"
|
|
default: false
|
|
description: "Whether portal is in a Matrix Space"
|
|
notes: "Spaces not commonly used for bridge portals"
|
|
|
|
members:
|
|
type: "list[string]"
|
|
example: ["U0123ABC", "U0456DEF", "U0789GHI"]
|
|
description: "Slack user IDs of channel members"
|
|
synced: true
|
|
notes: "Updated when users join/leave channel"
|
|
|
|
created_at:
|
|
type: "timestamp"
|
|
example: "2025-10-22T14:30:00Z"
|
|
description: "When portal was created in bridge"
|
|
|
|
last_activity:
|
|
type: "timestamp"
|
|
example: "2025-10-22T15:45:30Z"
|
|
description: "Timestamp of last message in portal"
|
|
notes: "Used for health monitoring"
|
|
|
|
# =============================================================================
|
|
# Example Portal Mappings
|
|
# =============================================================================
|
|
|
|
# Example 1: Public Channel
|
|
# --------------------------
|
|
example_public_channel:
|
|
slack:
|
|
channel_id: "C05N2EXAMPLE"
|
|
channel_name: "dev-platform"
|
|
workspace: "chochacho"
|
|
url: "https://chochacho.slack.com/archives/C05N2EXAMPLE"
|
|
type: "public_channel"
|
|
members: 12
|
|
topic: "Platform infrastructure development"
|
|
|
|
matrix:
|
|
room_id: "!xYzAbCdEfGhIjKlM:clarun.xyz"
|
|
room_alias: "#slack_dev-platform:clarun.xyz"
|
|
display_name: "dev-platform (Slack)"
|
|
topic: "Platform infrastructure development"
|
|
encrypted: false
|
|
members:
|
|
- "@alice:clarun.xyz" # Real Matrix user
|
|
- "@slack_U0123ABC:clarun.xyz" # Ghost user (John from Slack)
|
|
- "@slack_U0456DEF:clarun.xyz" # Ghost user (Jane from Slack)
|
|
- "@slackbot:clarun.xyz" # Bridge bot
|
|
|
|
portal_metadata:
|
|
created_at: "2025-10-22T14:00:00Z"
|
|
last_activity: "2025-10-22T16:30:00Z"
|
|
message_count: 427
|
|
state: "active"
|
|
|
|
# Example 2: Private Channel
|
|
# ---------------------------
|
|
example_private_channel:
|
|
slack:
|
|
channel_id: "G05N2EXAMPLE" # Private channels start with G
|
|
channel_name: "leadership-team"
|
|
workspace: "chochacho"
|
|
type: "private_channel"
|
|
members: 5
|
|
topic: "Leadership discussions"
|
|
|
|
matrix:
|
|
room_id: "!aBcDeFgHiJkLmNoP:clarun.xyz"
|
|
room_alias: "#slack_leadership-team:clarun.xyz"
|
|
display_name: "leadership-team (Slack)"
|
|
topic: "Leadership discussions"
|
|
encrypted: true # High-security channel
|
|
members:
|
|
- "@alice:clarun.xyz"
|
|
- "@bob:clarun.xyz"
|
|
- "@slack_U0789GHI:clarun.xyz"
|
|
- "@slackbot:clarun.xyz"
|
|
|
|
portal_metadata:
|
|
created_at: "2025-10-22T14:05:00Z"
|
|
last_activity: "2025-10-22T15:00:00Z"
|
|
message_count: 89
|
|
state: "active"
|
|
|
|
# Example 3: Direct Message
|
|
# --------------------------
|
|
example_direct_message:
|
|
slack:
|
|
channel_id: "D05N2EXAMPLE" # DMs start with D
|
|
workspace: "chochacho"
|
|
type: "dm"
|
|
members: 2 # Alice (Matrix) and John (Slack)
|
|
|
|
matrix:
|
|
room_id: "!qRsTuVwXyZaBcDeF:clarun.xyz"
|
|
room_alias: null # DMs don't have aliases
|
|
display_name: "John Doe (Slack DM)"
|
|
encrypted: false
|
|
members:
|
|
- "@alice:clarun.xyz"
|
|
- "@slack_U0123ABC:clarun.xyz" # John
|
|
- "@slackbot:clarun.xyz"
|
|
|
|
portal_metadata:
|
|
created_at: "2025-10-22T14:10:00Z"
|
|
last_activity: "2025-10-22T16:45:00Z"
|
|
message_count: 52
|
|
state: "active"
|
|
|
|
# Example 4: Archived Channel
|
|
# ----------------------------
|
|
example_archived_channel:
|
|
slack:
|
|
channel_id: "C05N2OLDCHAN"
|
|
channel_name: "old-project"
|
|
workspace: "chochacho"
|
|
type: "public_channel"
|
|
archived: true
|
|
members: 0 # All members removed when archived
|
|
|
|
matrix:
|
|
room_id: "!gHiJkLmNoPqRsTuV:clarun.xyz"
|
|
room_alias: "#slack_old-project:clarun.xyz"
|
|
display_name: "old-project (Slack) [ARCHIVED]"
|
|
topic: "[Archived] Old project discussions"
|
|
encrypted: false
|
|
members:
|
|
- "@slackbot:clarun.xyz" # Only bot remains
|
|
|
|
portal_metadata:
|
|
created_at: "2025-08-15T10:00:00Z"
|
|
last_activity: "2025-10-01T12:00:00Z" # Before archival
|
|
message_count: 1543
|
|
state: "archived"
|
|
|
|
# =============================================================================
|
|
# Configuration Parameters (config.yaml)
|
|
# =============================================================================
|
|
|
|
# The only configuration parameter affecting portal creation:
|
|
|
|
conversation_count:
|
|
type: "integer"
|
|
default: 10
|
|
description: "Number of recent Slack conversations to sync on initial login"
|
|
location: "config.yaml → slack.conversation_count"
|
|
behavior:
|
|
- "Set to 10: Sync 10 most recent channels/DMs"
|
|
- "Set to 0: No initial sync (portals created on-demand only)"
|
|
- "Set to 100: Sync 100 recent conversations (may be slow)"
|
|
recommendation: "Start with 10 for testing, adjust based on team size"
|
|
|
|
# No other static channel mapping configuration exists.
|
|
|
|
# =============================================================================
|
|
# Operational Commands (via Matrix DM with @slackbot:clarun.xyz)
|
|
# =============================================================================
|
|
|
|
# Note: These commands are inferred from other mautrix bridges and may not
|
|
# all be available in mautrix-slack. Verify with `help` command.
|
|
|
|
commands:
|
|
- command: "help"
|
|
description: "Display available bridge commands"
|
|
usage: "help"
|
|
|
|
- command: "login app"
|
|
description: "Authenticate bridge with Slack app credentials"
|
|
usage: "login app"
|
|
prompts:
|
|
- "Please provide bot token (xoxb-...)"
|
|
- "Please provide app token (xapp-...)"
|
|
|
|
- command: "logout"
|
|
description: "Disconnect bridge from Slack"
|
|
usage: "logout"
|
|
effect: "All portals become inactive until re-login"
|
|
|
|
- command: "delete-portal"
|
|
description: "Remove portal for current room (if available)"
|
|
usage: "delete-portal"
|
|
context: "Send from within a bridged portal room"
|
|
effect: "Kicks users, deletes Matrix room, removes from database"
|
|
|
|
- command: "sync"
|
|
description: "Re-sync portals from Slack (if available)"
|
|
usage: "sync"
|
|
effect: "Creates portals for newly joined Slack channels"
|
|
|
|
- command: "status"
|
|
description: "Display bridge connection status (if available)"
|
|
usage: "status"
|
|
expected_output:
|
|
- "Connection: Connected"
|
|
- "Workspace: chochacho"
|
|
- "Portals: 12 active"
|
|
- "Last message: 30 seconds ago"
|
|
|
|
# =============================================================================
|
|
# Gradual Rollout Strategy
|
|
# =============================================================================
|
|
|
|
# Phase 1: Single Test Channel (Week 1-2)
|
|
# ----------------------------------------
|
|
phase_1:
|
|
goal: "Validate bridge functionality with minimal scope"
|
|
conversation_count: 5 # Limit initial sync
|
|
channels:
|
|
- slack_channel: "#dev-platform"
|
|
matrix_room: "#slack_dev-platform:clarun.xyz"
|
|
members: ["@alice:clarun.xyz", "@bob:clarun.xyz"]
|
|
purpose: "Testing all bridge features"
|
|
success_criteria:
|
|
- "Messages flow bidirectionally within 5 seconds"
|
|
- "Reactions, edits, deletes sync correctly"
|
|
- "File attachments work"
|
|
- "No service crashes or errors"
|
|
|
|
# Phase 2: Small User Group (Week 3-4)
|
|
# -------------------------------------
|
|
phase_2:
|
|
goal: "Test multi-user shared portals"
|
|
conversation_count: 10
|
|
channels:
|
|
- "#dev-platform"
|
|
- "#general"
|
|
- "#random"
|
|
users: 5
|
|
success_criteria:
|
|
- "Multiple Matrix users can interact in same portal"
|
|
- "Ghost users appear correctly"
|
|
- "Performance acceptable with 5 users"
|
|
- "No conflicts or race conditions"
|
|
|
|
# Phase 3: Organic Expansion (Week 5+)
|
|
# -------------------------------------
|
|
phase_3:
|
|
goal: "Full team adoption"
|
|
conversation_count: 20 # Increase as confidence grows
|
|
channels: "Auto-created based on activity"
|
|
users: "All team members (2-5)"
|
|
approach:
|
|
- "Don't pre-configure channel lists"
|
|
- "Let users authenticate individually"
|
|
- "Portals created organically as users interact"
|
|
- "Monitor health metrics"
|
|
success_criteria:
|
|
- "99% uptime over 7 days"
|
|
- "All messages delivered within 5 seconds"
|
|
- "User feedback positive"
|
|
- "No operational issues"
|
|
|
|
# =============================================================================
|
|
# Monitoring Portal Health
|
|
# =============================================================================
|
|
|
|
# Health indicators per portal (from data-model.md):
|
|
|
|
health_indicators:
|
|
connection_status:
|
|
values: ["connected", "disconnected", "refreshing"]
|
|
source: "Service logs"
|
|
alert: "If disconnected > 5 minutes"
|
|
|
|
last_successful_message:
|
|
type: "timestamp"
|
|
source: "portal.last_activity in database"
|
|
alert: "If > 1 hour old in active channel"
|
|
|
|
error_count:
|
|
type: "integer"
|
|
source: "Service logs (ERROR level)"
|
|
alert: "If > 10 errors in 10 minutes"
|
|
|
|
portal_count:
|
|
type: "integer"
|
|
source: "SELECT COUNT(*) FROM portal"
|
|
expected: "Grows organically, typically 5-20 for small team"
|
|
|
|
ghost_user_count:
|
|
type: "integer"
|
|
source: "SELECT COUNT(*) FROM puppet"
|
|
expected: "One per Slack user in bridged channels"
|
|
|
|
# Monitoring queries:
|
|
|
|
monitoring_queries:
|
|
- name: "List active portals"
|
|
query: |
|
|
SELECT slack_channel_id, mxid, name, topic
|
|
FROM portal
|
|
WHERE state = 'active'
|
|
ORDER BY last_activity DESC;
|
|
|
|
- name: "Find stale portals"
|
|
query: |
|
|
SELECT slack_channel_id, name, last_activity
|
|
FROM portal
|
|
WHERE last_activity < NOW() - INTERVAL '7 days'
|
|
ORDER BY last_activity ASC;
|
|
|
|
- name: "Count messages today"
|
|
query: |
|
|
SELECT COUNT(*)
|
|
FROM message
|
|
WHERE timestamp >= CURRENT_DATE;
|
|
|
|
# =============================================================================
|
|
# Troubleshooting Portal Issues
|
|
# =============================================================================
|
|
|
|
troubleshooting:
|
|
- issue: "Portal not created for Slack channel"
|
|
checks:
|
|
- "Verify bridge is authenticated (status command)"
|
|
- "Check if message was received (look for Slack event in logs)"
|
|
- "Verify bot has access to channel (App Login mode)"
|
|
- "Check bridge logs for errors"
|
|
resolution: "Send message in Slack channel to trigger portal creation"
|
|
|
|
- issue: "Messages not appearing in Matrix"
|
|
checks:
|
|
- "Verify Socket Mode connection active"
|
|
- "Check last_successful_message timestamp"
|
|
- "Look for relay errors in logs"
|
|
- "Verify homeserver is reachable"
|
|
resolution: "Check journalctl -u mautrix-slack for specific errors"
|
|
|
|
- issue: "Messages not appearing in Slack"
|
|
checks:
|
|
- "Verify bot token valid (test with Slack API)"
|
|
- "Check bot is member of channel"
|
|
- "Look for Slack API errors in logs"
|
|
- "Verify rate limits not exceeded"
|
|
resolution: "Re-invite bot to channel if needed"
|
|
|
|
- issue: "Portal shows as archived when channel is active"
|
|
checks:
|
|
- "Verify channel not actually archived in Slack"
|
|
- "Check portal state in database"
|
|
- "Look for Slack channel status sync errors"
|
|
resolution: "Unarchive in Slack, may need to re-sync portal"
|
|
|
|
# =============================================================================
|
|
# Related Documentation
|
|
# =============================================================================
|
|
|
|
# mautrix-slack Docs: https://docs.mau.fi/bridges/go/slack/
|
|
# Matrix Spaces: https://matrix.org/docs/guides/spaces
|
|
# Portal Pattern: https://matrix.org/docs/older/types-of-bridging/#portal-bridging
|
|
|
|
# =============================================================================
|
|
# Version Information
|
|
# =============================================================================
|
|
|
|
# Contract Version: 1.0
|
|
# Created: 2025-10-22
|
|
# Last Updated: 2025-10-22
|
|
# Related Spec: 002-slack-bridge-integration/spec.md
|
|
# Portal Model: Automatic creation (no static mapping)
|