Includes spec, plan, research, data model, contracts, and quickstart guide for mautrix-slack Socket Mode bridge deployment.
360 lines
13 KiB
YAML
360 lines
13 KiB
YAML
# Secrets Schema Contract: Slack Bridge Credentials
|
|
|
|
# This contract defines the structure and handling of sensitive credentials
|
|
# required for the Slack↔Matrix bridge. All secrets are managed via sops-nix
|
|
# with age encryption.
|
|
|
|
# =============================================================================
|
|
# Secret Definitions
|
|
# =============================================================================
|
|
|
|
secrets:
|
|
slack-oauth-token:
|
|
description: "Slack bot user OAuth token for API operations"
|
|
format: "xoxb-[workspace_id]-[token_id]-[secret]"
|
|
example: "xoxb-1234567890-1234567890123-AbCdEfGhIjKlMnOpQrStUvWx"
|
|
required: true
|
|
sensitivity: "high"
|
|
scope: "Bot token scopes (29 scopes from app manifest)"
|
|
usage: "Provided during `login app` interactive authentication"
|
|
rotation: "Manual via Slack app settings → Reinstall app"
|
|
storage:
|
|
- location: "sops-encrypted secrets.yaml (backup/recovery)"
|
|
- location: "Bridge PostgreSQL database (primary storage after login)"
|
|
validation:
|
|
- "Starts with 'xoxb-'"
|
|
- "Length: 50-100 characters"
|
|
- "Format: xoxb-[numbers]-[numbers]-[alphanumeric]"
|
|
revocation:
|
|
- "Slack app settings → OAuth & Permissions → Revoke"
|
|
- "Remove app from workspace"
|
|
|
|
slack-app-token:
|
|
description: "Slack app-level token for Socket Mode WebSocket connection"
|
|
format: "xapp-[level]-[workspace_id]-[token_id]-[secret]"
|
|
example: "xapp-1-A0123456789-1234567890123-abc123def456ghi789jkl012mno345pqr678stu901vwx234yz"
|
|
required: true
|
|
sensitivity: "high"
|
|
scope: "connections:write (Socket Mode)"
|
|
usage: "Provided during `login app` interactive authentication"
|
|
rotation: "Manual via Slack app settings → App-Level Tokens → Regenerate"
|
|
storage:
|
|
- location: "sops-encrypted secrets.yaml (backup/recovery)"
|
|
- location: "Bridge PostgreSQL database (primary storage after login)"
|
|
validation:
|
|
- "Starts with 'xapp-'"
|
|
- "Length: 80-120 characters"
|
|
- "Contains workspace identifier"
|
|
revocation:
|
|
- "Slack app settings → App-Level Tokens → Revoke"
|
|
|
|
# =============================================================================
|
|
# sops-nix Configuration
|
|
# =============================================================================
|
|
|
|
# File: secrets/secrets.yaml (encrypted)
|
|
# ----------------
|
|
slack-oauth-token: "ENC[AES256_GCM,data:...,type:str]"
|
|
slack-app-token: "ENC[AES256_GCM,data:...,type:str]"
|
|
|
|
# Age Keys (.sops.yaml)
|
|
# ---------------------
|
|
keys:
|
|
- &vultr_vps age1vuxcwvdvzl2u7w6kudqvnnf45czrnhwv9aevjq9hyjjpa409jvkqhkz32q
|
|
# Source: /etc/ssh/ssh_host_ed25519_key on VPS
|
|
# Conversion: ssh-to-age < ssh_host_ed25519_key.pub
|
|
# Purpose: Production VPS can decrypt secrets at boot
|
|
|
|
- &admin age18ue40q4fw8uggdlfag7jf5nrawvfvsnv93nurschhuynus200yjsd775v3
|
|
# Source: Admin workstation age key
|
|
# Purpose: Administrator can edit secrets locally
|
|
|
|
# Encryption Rules
|
|
# ----------------
|
|
creation_rules:
|
|
- path_regex: secrets/secrets\.yaml$
|
|
key_groups:
|
|
- age:
|
|
- *vultr_vps # VPS auto-decrypts at boot
|
|
- *admin # Admin can sops secrets/secrets.yaml
|
|
|
|
# =============================================================================
|
|
# NixOS Declaration (hosts/ops-jrz1.nix)
|
|
# =============================================================================
|
|
|
|
# NOTE: With interactive login approach, these secret declarations are NOT
|
|
# required. Tokens are provided via Matrix chat and stored in database.
|
|
# The declarations below are for backup/recovery purposes only.
|
|
|
|
# sops:
|
|
# defaultSopsFile: ../secrets/secrets.yaml
|
|
# age:
|
|
# sshKeyPaths: ["/etc/ssh/ssh_host_ed25519_key"]
|
|
#
|
|
# secrets:
|
|
# slack-oauth-token:
|
|
# owner: "mautrix_slack"
|
|
# group: "mautrix_slack"
|
|
# mode: "0440"
|
|
# # Runtime location: /run/secrets/slack-oauth-token
|
|
# # Decrypted at boot, available only to mautrix_slack user
|
|
#
|
|
# slack-app-token:
|
|
# owner: "mautrix_slack"
|
|
# group: "mautrix_slack"
|
|
# mode: "0440"
|
|
# # Runtime location: /run/secrets/slack-app-token
|
|
# # Decrypted at boot, available only to mautrix_slack user
|
|
|
|
# =============================================================================
|
|
# Runtime Secret Locations
|
|
# =============================================================================
|
|
|
|
# After sops-nix decryption (boot time):
|
|
# /run/secrets/slack-oauth-token
|
|
# - Permissions: 0440 (-r--r-----)
|
|
# - Owner: mautrix_slack:mautrix_slack
|
|
# - Storage: tmpfs (RAM-only, cleared on reboot)
|
|
# - Usage: NOT used with interactive login approach
|
|
# - Purpose: Backup for disaster recovery
|
|
|
|
# /run/secrets/slack-app-token
|
|
# - Permissions: 0440 (-r--r-----)
|
|
# - Owner: mautrix_slack:mautrix_slack
|
|
# - Storage: tmpfs (RAM-only, cleared on reboot)
|
|
# - Usage: NOT used with interactive login approach
|
|
# - Purpose: Backup for disaster recovery
|
|
|
|
# Bridge Database (runtime, primary storage):
|
|
# /var/lib/mautrix_slack/mautrix_slack.db (or PostgreSQL)
|
|
# - Table: "user"
|
|
# - Columns: mxid, slack_user_id, access_token (encrypted)
|
|
# - Storage: Persistent, survives reboots
|
|
# - Encryption: Filesystem-level (LUKS)
|
|
# - Usage: Active tokens after `login app` authentication
|
|
|
|
# =============================================================================
|
|
# Token Generation Process
|
|
# =============================================================================
|
|
|
|
# Step 1: Create Slack App
|
|
# -------------------------
|
|
# 1. Go to: https://api.slack.com/apps
|
|
# 2. Click "Create New App" → "From an app manifest"
|
|
# 3. Select workspace: "chochacho"
|
|
# 4. Paste manifest from: https://github.com/mautrix/slack/blob/main/app-manifest.yaml
|
|
# 5. Review scopes (29 bot scopes, 46 event subscriptions)
|
|
# 6. Click "Create"
|
|
|
|
# Step 2: Enable Socket Mode
|
|
# ---------------------------
|
|
# 1. In app settings → "Socket Mode"
|
|
# 2. Toggle "Enable Socket Mode" → ON
|
|
# 3. Click "Generate an app-level token"
|
|
# 4. Token name: "socket-mode-token"
|
|
# 5. Add scope: "connections:write"
|
|
# 6. Click "Generate"
|
|
# 7. Copy token (starts with "xapp-") → Save securely
|
|
|
|
# Step 3: Install App to Workspace
|
|
# ---------------------------------
|
|
# 1. In app settings → "Install App"
|
|
# 2. Click "Install to Workspace"
|
|
# 3. Review permissions (29 scopes)
|
|
# 4. Click "Allow"
|
|
# 5. Copy "Bot User OAuth Token" (starts with "xoxb-") → Save securely
|
|
|
|
# Step 4: Store Tokens in sops-nix (Optional, for backup)
|
|
# --------------------------------------------------------
|
|
# 1. Edit encrypted secrets file:
|
|
# sops secrets/secrets.yaml
|
|
#
|
|
# 2. Add tokens:
|
|
# slack-oauth-token: "xoxb-..."
|
|
# slack-app-token: "xapp-..."
|
|
#
|
|
# 3. Save (auto-encrypts with age keys)
|
|
# 4. Commit to git (encrypted version is safe)
|
|
# 5. Deploy configuration (secrets decrypted at boot)
|
|
|
|
# Step 5: Authenticate Bridge (Primary Method)
|
|
# ---------------------------------------------
|
|
# 1. Open Matrix client (Element, etc.)
|
|
# 2. Start DM with: @slackbot:clarun.xyz
|
|
# 3. Send command: "login app"
|
|
# 4. Bot prompts: "Please provide bot token"
|
|
# 5. Send: "xoxb-..." (paste bot token)
|
|
# 6. Bot prompts: "Please provide app token"
|
|
# 7. Send: "xapp-..." (paste app token)
|
|
# 8. Bot responds: "Successfully logged in" (or error)
|
|
# 9. Tokens stored in bridge database (persistent)
|
|
|
|
# =============================================================================
|
|
# Security Best Practices
|
|
# =============================================================================
|
|
|
|
security:
|
|
storage:
|
|
- rule: "NEVER commit unencrypted tokens to git"
|
|
enforcement: ".gitignore excludes secrets.yaml.dec"
|
|
|
|
- rule: "NEVER hardcode tokens in NixOS configuration"
|
|
enforcement: "Use sops-nix or interactive login only"
|
|
|
|
- rule: "NEVER log tokens in plaintext"
|
|
enforcement: "mautrix bridges sanitize logs automatically"
|
|
|
|
access_control:
|
|
- rule: "Tokens only readable by service user"
|
|
enforcement: "owner: mautrix_slack, mode: 0440"
|
|
|
|
- rule: "Tokens cleared on reboot (tmpfs)"
|
|
enforcement: "/run/secrets on tmpfs filesystem"
|
|
|
|
- rule: "Tokens encrypted at rest in database"
|
|
enforcement: "LUKS encryption on /var filesystem"
|
|
|
|
rotation:
|
|
- frequency: "Every 90 days (recommended)"
|
|
process:
|
|
- "Generate new tokens in Slack app settings"
|
|
- "Update sops-encrypted secrets.yaml"
|
|
- "Re-authenticate bridge via `login app` command"
|
|
- "Verify functionality with test message"
|
|
- "Revoke old tokens in Slack"
|
|
|
|
- emergency:
|
|
- "If tokens compromised, revoke immediately in Slack"
|
|
- "Generate new tokens"
|
|
- "Re-authenticate bridge"
|
|
- "Review audit logs for unauthorized access"
|
|
|
|
monitoring:
|
|
- "Enable IP allowlisting in Slack app settings (if VPS has static IP)"
|
|
- "Monitor Slack app usage dashboard for anomalies"
|
|
- "Alert on authentication failures in bridge logs"
|
|
- "Track token usage via Slack audit logs (Enterprise Grid)"
|
|
|
|
# =============================================================================
|
|
# Disaster Recovery
|
|
# =============================================================================
|
|
|
|
# Scenario 1: Lost Tokens (Bridge Database Corrupted)
|
|
# ----------------------------------------------------
|
|
# If bridge database is lost but sops-encrypted secrets.yaml exists:
|
|
# 1. Restore from backup or re-deploy service
|
|
# 2. Tokens in sops-encrypted secrets.yaml can be retrieved
|
|
# 3. Re-authenticate via `login app` command
|
|
# 4. Bridge resumes operation
|
|
|
|
# Scenario 2: Lost Secrets File (Git Repository Intact)
|
|
# ------------------------------------------------------
|
|
# If secrets.yaml is lost but git repository exists:
|
|
# 1. Clone git repository (encrypted secrets.yaml present)
|
|
# 2. Decrypt on VPS (age key from SSH host key)
|
|
# 3. Extract tokens: sops -d secrets/secrets.yaml | grep slack
|
|
# 4. Re-authenticate bridge via `login app` command
|
|
|
|
# Scenario 3: Complete VPS Failure (Need to Regenerate)
|
|
# ------------------------------------------------------
|
|
# If VPS is destroyed and no backups exist:
|
|
# 1. Go to Slack app settings
|
|
# 2. Regenerate app-level token (old token revoked)
|
|
# 3. Reinstall app to workspace (new bot token generated)
|
|
# 4. Update sops-encrypted secrets.yaml with new tokens
|
|
# 5. Deploy to new VPS
|
|
# 6. Authenticate bridge via `login app` with new tokens
|
|
|
|
# Scenario 4: Workspace Migration (Slack → New Workspace)
|
|
# --------------------------------------------------------
|
|
# If migrating from "delpadtech" to "chochacho":
|
|
# 1. Create new Slack app in "chochacho" workspace
|
|
# 2. Generate new tokens (different workspace = different tokens)
|
|
# 3. Update NixOS config: workspace = "chochacho"
|
|
# 4. Update secrets.yaml with new tokens
|
|
# 5. Deploy configuration
|
|
# 6. Authenticate with new tokens
|
|
# 7. Old workspace bridge automatically disconnects
|
|
|
|
# =============================================================================
|
|
# Token Scope Reference
|
|
# =============================================================================
|
|
|
|
# Bot Token Scopes (xoxb-) - 29 Required
|
|
# ---------------------------------------
|
|
bot_scopes:
|
|
channels:
|
|
- "channels:read" # List public channels
|
|
- "channels:history" # Read messages in public channels
|
|
- "channels:write.invites" # Invite users to channels
|
|
- "channels:write.topic" # Edit channel topics
|
|
|
|
groups: # Private channels
|
|
- "groups:read"
|
|
- "groups:history"
|
|
- "groups:write"
|
|
- "groups:write.invites"
|
|
- "groups:write.topic"
|
|
|
|
im: # Direct messages
|
|
- "im:read"
|
|
- "im:history"
|
|
- "im:write"
|
|
- "im:write.topic"
|
|
|
|
mpim: # Group direct messages
|
|
- "mpim:read"
|
|
- "mpim:history"
|
|
- "mpim:write"
|
|
- "mpim:write.topic"
|
|
|
|
chat:
|
|
- "chat:write" # Send messages
|
|
- "chat:write.public" # Send to public channels without joining
|
|
- "chat:write.customize" # Customize bot name/avatar (for ghosting)
|
|
|
|
files:
|
|
- "files:read" # Download files
|
|
- "files:write" # Upload files
|
|
|
|
reactions:
|
|
- "reactions:read" # View reactions
|
|
- "reactions:write" # Add/remove reactions
|
|
|
|
pins:
|
|
- "pins:read" # View pinned messages
|
|
- "pins:write" # Pin/unpin messages
|
|
|
|
users:
|
|
- "users:read" # View user info
|
|
- "users.profile:read" # View user profiles
|
|
- "users:read.email" # View user emails
|
|
|
|
workspace:
|
|
- "team:read" # View workspace info
|
|
- "emoji:read" # View custom emoji
|
|
|
|
# App-Level Token Scopes (xapp-) - 1 Required
|
|
# --------------------------------------------
|
|
app_scopes:
|
|
- "connections:write" # Establish Socket Mode WebSocket connections
|
|
|
|
# =============================================================================
|
|
# Related Documentation
|
|
# =============================================================================
|
|
|
|
# Slack API Scopes: https://api.slack.com/scopes
|
|
# sops-nix: https://github.com/Mic92/sops-nix
|
|
# Age Encryption: https://age-encryption.org/
|
|
# mautrix-slack Auth: https://docs.mau.fi/bridges/go/slack/authentication.html
|
|
|
|
# =============================================================================
|
|
# Version Information
|
|
# =============================================================================
|
|
|
|
# Contract Version: 1.0
|
|
# Created: 2025-10-22
|
|
# Last Updated: 2025-10-22
|
|
# Related Spec: 002-slack-bridge-integration/spec.md
|
|
# Security Requirement: FR-007 (sops-nix encrypted secrets)
|