Element

CommunicationMatrixDecentralizedE2EESelf-HostedOpen Source

Communication Tool

Element

Overview

Element is a decentralized, privacy-first communication platform built on the Matrix protocol. It provides complete end-to-end encryption, self-hostable distributed architecture, and open-source transparency. Combining Slack-like team features with WhatsApp-like private messaging, it delivers enterprise-grade security while maintaining federation capabilities.

Details

Element (formerly Riot.im) is a Matrix protocol-based communication platform renamed in 2019. Developed by Matrix.org, it provides communication infrastructure without single points of failure through distributed architecture. It has seen growing adoption particularly in government agencies, healthcare institutions, and privacy-conscious enterprises.

In 2024-2025, major updates included complete Element X mobile application redesign, migration to Rust SDK (matrix-rust-sdk), new Sliding Sync API, enhanced Element Call (voice/video calling), and Matrix 2.0 specification compliance. Additional significant expansions included Self-Sovereign Identity (SSI) support, enhanced OIDC integration, and improved Admin console features.

Matrix SDK supports multiple languages including JavaScript, Python, Rust, Swift, and Kotlin, enabling bot development, custom client creation, and system integration. Bridging functionality also provides interoperability with existing platforms like Slack, Discord, and Telegram.

Pros and Cons

Pros

  • Complete Decentralization: No single points of failure, resistance to government/corporate censorship
  • End-to-End Encryption: E2EE by default, complete encryption of messages and files
  • Open Source: Full transparency, code reviewable
  • Self-Host Support: Complete data sovereignty, GDPR compliance
  • Bridging Features: Interoperability with existing platforms
  • Rich APIs/SDKs: Multi-language support including JavaScript, Python, Rust
  • Government/Healthcare Adoption: Official adoption by German government, French government, etc.
  • Open Standards: Standardized Matrix protocol

Cons

  • Complex Setup: Technical complexity of initial setup and server construction
  • Resource Requirements: Server resource consumption for self-hosting
  • Usability: Learning curve compared to Slack or Discord
  • Ecosystem Scale: User base compared to mainstream platforms
  • Mobile Experience: Still developing despite Element X improvements
  • Performance: Sync speed in large rooms

Key Links

Code Examples

JavaScript Matrix SDK Bot Development

// Element/Matrix Bot implementation using matrix-js-sdk
import { MatrixClient, SimpleFsStorageProvider, AutojoinRoomsMixin } from 'matrix-bot-sdk';

class ElementBot {
    constructor(homeserverUrl, accessToken) {
        const storage = new SimpleFsStorageProvider('bot.json');
        this.client = new MatrixClient(homeserverUrl, accessToken, storage);
        
        // Auto-join invited rooms
        AutojoinRoomsMixin.setupOnClient(this.client);
        
        this.setupEventHandlers();
    }

    setupEventHandlers() {
        // Message event handler
        this.client.on('room.message', async (roomId, event) => {
            if (event['sender'] === await this.client.getUserId()) return;
            
            const messageBody = event['content']['body'];
            console.log(`📩 Message in ${roomId}: ${messageBody}`);
            
            await this.handleMessage(roomId, event, messageBody);
        });

        // Room join event handler
        this.client.on('room.join', async (roomId, event) => {
            console.log(`🚪 Joined room: ${roomId}`);
            await this.sendWelcomeMessage(roomId);
        });

        // Encryption event handler
        this.client.on('room.encrypted_message', async (roomId, event) => {
            console.log(`🔐 Encrypted message received in ${roomId}`);
            // Handle encrypted messages (requires device verification)
        });
    }

    async handleMessage(roomId, event, messageBody) {
        const senderId = event['sender'];
        
        // Command processing
        if (messageBody.startsWith('!')) {
            await this.processCommand(roomId, messageBody, senderId);
            return;
        }

        // Keyword detection
        if (messageBody.toLowerCase().includes('help')) {
            await this.sendHelpMessage(roomId);
        }

        // AI integration example
        if (messageBody.toLowerCase().includes('@bot')) {
            await this.handleAIResponse(roomId, messageBody);
        }
    }

    async processCommand(roomId, messageBody, senderId) {
        const [command, ...args] = messageBody.slice(1).split(' ');
        
        switch (command.toLowerCase()) {
            case 'ping':
                await this.sendMessage(roomId, '🏓 Pong!');
                break;
            case 'status':
                await this.sendSystemStatus(roomId);
                break;
            case 'weather':
                await this.sendWeatherInfo(roomId, args[0] || 'Tokyo');
                break;
            case 'encrypt':
                await this.enableEncryption(roomId);
                break;
            case 'members':
                await this.listRoomMembers(roomId);
                break;
            case 'create':
                if (args[0] === 'room') {
                    await this.createRoom(args.slice(1).join(' '), senderId);
                }
                break;
            default:
                await this.sendMessage(roomId, `❓ Unknown command: ${command}. Type !help for help.`);
        }
    }

    async sendMessage(roomId, message, msgtype = 'm.text') {
        const content = {
            msgtype: msgtype,
            body: message,
            format: 'org.matrix.custom.html',
            formatted_body: message.replace(/\n/g, '<br/>')
        };

        try {
            await this.client.sendMessage(roomId, content);
        } catch (error) {
            console.error('Message send error:', error);
        }
    }

    async sendRichMessage(roomId, plainText, htmlText) {
        const content = {
            msgtype: 'm.text',
            body: plainText,
            format: 'org.matrix.custom.html',
            formatted_body: htmlText
        };

        try {
            await this.client.sendMessage(roomId, content);
        } catch (error) {
            console.error('Rich message send error:', error);
        }
    }

    async sendWelcomeMessage(roomId) {
        const welcomeHtml = `
        <h3>🚀 Welcome to Element Bot!</h3>
        <p>Available commands:</p>
        <ul>
            <li><code>!ping</code> - Connection test</li>
            <li><code>!status</code> - System status</li>
            <li><code>!weather [city]</code> - Weather information</li>
            <li><code>!encrypt</code> - Enable encryption</li>
            <li><code>!members</code> - List members</li>
            <li><code>!help</code> - Show help</li>
        </ul>
        `;

        const plainText = `🚀 Welcome to Element Bot!

Available commands:
- !ping - Connection test
- !status - System status
- !weather [city] - Weather information
- !encrypt - Enable encryption
- !members - List members
- !help - Show help`;

        await this.sendRichMessage(roomId, plainText, welcomeHtml);
    }

    async sendSystemStatus(roomId) {
        const os = require('os');
        const uptimeHours = Math.floor(os.uptime() / 3600);
        const uptimeMinutes = Math.floor((os.uptime() % 3600) / 60);
        const memoryUsage = Math.round((os.totalmem() - os.freemem()) / os.totalmem() * 100);

        const statusHtml = `
        <h4>📊 System Status</h4>
        <table>
            <tr><td><strong>Uptime</strong></td><td>${uptimeHours}h ${uptimeMinutes}m</td></tr>
            <tr><td><strong>Memory Usage</strong></td><td>${memoryUsage}%</td></tr>
            <tr><td><strong>Platform</strong></td><td>${os.platform()} ${os.arch()}</td></tr>
            <tr><td><strong>Node.js</strong></td><td>${process.version}</td></tr>
            <tr><td><strong>Matrix SDK</strong></td><td>Connected ✅</td></tr>
        </table>
        `;

        const plainText = `📊 System Status
Uptime: ${uptimeHours}h ${uptimeMinutes}m
Memory Usage: ${memoryUsage}%
Platform: ${os.platform()} ${os.arch()}
Node.js: ${process.version}
Matrix SDK: Connected ✅`;

        await this.sendRichMessage(roomId, plainText, statusHtml);
    }

    async sendWeatherInfo(roomId, city) {
        try {
            const response = await fetch(`https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${process.env.WEATHER_API_KEY}&units=metric`);
            const weather = await response.json();

            if (weather.cod === 200) {
                const weatherHtml = `
                <h4>🌤️ Weather in ${weather.name}</h4>
                <ul>
                    <li><strong>Weather:</strong> ${weather.weather[0].description}</li>
                    <li><strong>Temperature:</strong> ${weather.main.temp}°C</li>
                    <li><strong>Humidity:</strong> ${weather.main.humidity}%</li>
                    <li><strong>Wind Speed:</strong> ${weather.wind.speed} m/s</li>
                </ul>
                `;

                const plainText = `🌤️ Weather in ${weather.name}
Weather: ${weather.weather[0].description}
Temperature: ${weather.main.temp}°C
Humidity: ${weather.main.humidity}%
Wind Speed: ${weather.wind.speed} m/s`;

                await this.sendRichMessage(roomId, plainText, weatherHtml);
            } else {
                await this.sendMessage(roomId, '❌ Failed to fetch weather information');
            }
        } catch (error) {
            console.error('Weather API error:', error);
            await this.sendMessage(roomId, '❌ Error occurred while fetching weather information');
        }
    }

    async enableEncryption(roomId) {
        try {
            await this.client.sendStateEvent(roomId, 'm.room.encryption', '', {
                algorithm: 'm.megolm.v1.aes-sha2'
            });
            await this.sendMessage(roomId, '🔐 Room encryption enabled');
        } catch (error) {
            console.error('Encryption enable error:', error);
            await this.sendMessage(roomId, '❌ Failed to enable encryption');
        }
    }

    async listRoomMembers(roomId) {
        try {
            const members = await this.client.getRoomMembers(roomId);
            const memberList = members
                .filter(member => member.membership === 'join')
                .map(member => member.stateKey)
                .join('\n');

            const memberHtml = `
            <h4>👥 Room Members (${members.length})</h4>
            <ul>
                ${members.map(member => `<li>${member.stateKey}</li>`).join('')}
            </ul>
            `;

            await this.sendRichMessage(roomId, `👥 Room Members:\n${memberList}`, memberHtml);
        } catch (error) {
            console.error('List members error:', error);
            await this.sendMessage(roomId, '❌ Failed to list members');
        }
    }

    async createRoom(roomName, creatorId) {
        try {
            const options = {
                name: roomName,
                topic: `Created by bot for ${creatorId}`,
                preset: 'private_chat',
                is_direct: false,
                invite: [creatorId]
            };

            const roomId = await this.client.createRoom(options);
            await this.sendMessage(roomId, `🎉 Room "${roomName}" created successfully!`);
            
            return roomId;
        } catch (error) {
            console.error('Room creation error:', error);
            throw error;
        }
    }

    async uploadFile(roomId, filePath, fileName) {
        const fs = require('fs');
        
        try {
            const fileBuffer = fs.readFileSync(filePath);
            const mxcUrl = await this.client.uploadContent(fileBuffer, 'application/octet-stream', fileName);
            
            const content = {
                msgtype: 'm.file',
                body: fileName,
                filename: fileName,
                url: mxcUrl
            };

            await this.client.sendMessage(roomId, content);
            console.log(`📎 File upload complete: ${fileName}`);
        } catch (error) {
            console.error('File upload error:', error);
        }
    }

    async start() {
        try {
            await this.client.start();
            console.log('🚀 Element Bot started');
            
            // Setup periodic tasks
            this.setupPeriodicTasks();
        } catch (error) {
            console.error('Bot start error:', error);
        }
    }

    setupPeriodicTasks() {
        // Hourly health check
        setInterval(async () => {
            console.log('⏰ Running periodic health check...');
            // Optional: send status to specific room
        }, 3600000);

        // Daily report (every day at 9 AM)
        const now = new Date();
        const msUntil9AM = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1, 9, 0, 0, 0) - now;
        
        setTimeout(() => {
            setInterval(async () => {
                console.log('📊 Generating daily report...');
                // Send daily report to admin room
            }, 24 * 60 * 60 * 1000);
        }, msUntil9AM);
    }

    async stop() {
        await this.client.stop();
        console.log('🛑 Element Bot stopped');
    }
}

// Usage example
async function main() {
    const homeserverUrl = process.env.MATRIX_HOMESERVER_URL || 'https://matrix.org';
    const accessToken = process.env.MATRIX_ACCESS_TOKEN;

    if (!accessToken) {
        console.error('❌ MATRIX_ACCESS_TOKEN not set');
        process.exit(1);
    }

    const bot = new ElementBot(homeserverUrl, accessToken);

    // Graceful shutdown
    process.on('SIGINT', async () => {
        console.log('🔄 Shutting down...');
        await bot.stop();
        process.exit(0);
    });

    await bot.start();
}

main().catch(console.error);

Matrix Homeserver (Synapse) Setup

# docker-compose.yml for Synapse homeserver
version: '3.8'

services:
  synapse:
    image: matrixdotorg/synapse:latest
    container_name: matrix-synapse
    restart: unless-stopped
    ports:
      - "8008:8008"
    volumes:
      - ./synapse_data:/data
    environment:
      SYNAPSE_SERVER_NAME: your-domain.com
      SYNAPSE_REPORT_STATS: "no"
      SYNAPSE_HTTP_PORT: 8008
    depends_on:
      - postgres
    networks:
      - matrix_network

  postgres:
    image: postgres:15
    container_name: matrix-postgres
    restart: unless-stopped
    environment:
      POSTGRES_DB: synapse
      POSTGRES_USER: synapse
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_INITDB_ARGS: "--encoding=UTF-8 --lc-collate=C --lc-ctype=C"
    volumes:
      - postgres_data:/var/lib/postgresql/data
    networks:
      - matrix_network

  element:
    image: vectorim/element-web:latest
    container_name: matrix-element
    restart: unless-stopped
    ports:
      - "80:80"
    volumes:
      - ./element_config.json:/app/config.json
    networks:
      - matrix_network

  # Optional: Matrix-Discord Bridge
  discord-bridge:
    image: sorunome/mx-puppet-discord
    container_name: matrix-discord-bridge
    restart: unless-stopped
    volumes:
      - ./discord_config.yaml:/data/config.yaml
      - discord_data:/data
    networks:
      - matrix_network

volumes:
  postgres_data:
  discord_data:

networks:
  matrix_network:
    driver: bridge

Element Web Configuration Example

{
    "default_server_config": {
        "m.homeserver": {
            "base_url": "https://your-domain.com",
            "server_name": "your-domain.com"
        },
        "m.identity_server": {
            "base_url": "https://vector.im"
        }
    },
    "brand": "Element",
    "integrations_ui_url": "https://scalar.vector.im/",
    "integrations_rest_url": "https://scalar.vector.im/api",
    "integrations_widgets_urls": [
        "https://scalar.vector.im/_matrix/integrations/v1",
        "https://scalar.vector.im/api",
        "https://scalar-staging.vector.im/_matrix/integrations/v1",
        "https://scalar-staging.vector.im/api",
        "https://scalar-staging.riot.im/scalar/api"
    ],
    "hosting_signup_link": "https://element.io/matrix-services?utm_source=element-web&utm_medium=web",
    "bug_report_endpoint_url": "https://element.io/bugreports/submit",
    "uisi_autorageshake_app": "element-auto-uisi",
    "showLabsSettings": true,
    "features": {
        "feature_pinning": "labs",
        "feature_custom_status": "labs",
        "feature_custom_tags": "labs",
        "feature_state_counters": "labs",
        "feature_voice_messages": "labs",
        "feature_poll": "labs",
        "feature_location_share": "labs"
    },
    "default_country_code": "US",
    "map_style_url": "https://api.maptiler.com/maps/streets/style.json?key=YOUR_API_KEY"
}

Python Matrix SDK Bot Development

# Python Matrix Bot using matrix-nio SDK
import asyncio
import json
import os
from nio import AsyncClient, MatrixRoom, RoomMessageText, RoomMemberEvent
from nio.events.room_events import Event
import aiohttp

class ElementPythonBot:
    def __init__(self, homeserver_url: str, user_id: str, access_token: str):
        self.homeserver_url = homeserver_url
        self.user_id = user_id
        self.access_token = access_token
        self.client = AsyncClient(homeserver_url, user_id)
        self.client.access_token = access_token
        
        # Event handlers
        self.client.add_event_callback(self.message_callback, RoomMessageText)
        self.client.add_event_callback(self.member_callback, RoomMemberEvent)
    
    async def message_callback(self, room: MatrixRoom, event: RoomMessageText):
        """Message event callback"""
        if event.sender == self.user_id:
            return  # Ignore own messages
        
        message_body = event.body
        sender = event.sender
        
        print(f"📩 Message from {sender} in {room.display_name}: {message_body}")
        
        # Command processing
        if message_body.startswith('!'):
            await self.handle_command(room, event, message_body)
        
        # Keyword detection
        elif 'help' in message_body.lower():
            await self.send_help_message(room.room_id)
        
        # AI response (example)
        elif '@bot' in message_body.lower():
            await self.handle_ai_response(room.room_id, message_body)
    
    async def member_callback(self, room: MatrixRoom, event: RoomMemberEvent):
        """Member join/leave event callback"""
        if event.membership == "join" and event.sender != self.user_id:
            await self.send_welcome_message(room.room_id, event.sender)
    
    async def handle_command(self, room: MatrixRoom, event: RoomMessageText, message_body: str):
        """Command processing"""
        parts = message_body[1:].split()
        command = parts[0].lower()
        args = parts[1:] if len(parts) > 1 else []
        
        if command == 'ping':
            await self.send_message(room.room_id, "🏓 Pong!")
        
        elif command == 'status':
            await self.send_system_status(room.room_id)
        
        elif command == 'weather':
            city = args[0] if args else 'Tokyo'
            await self.send_weather_info(room.room_id, city)
        
        elif command == 'encrypt':
            await self.enable_encryption(room.room_id)
        
        elif command == 'room' and args:
            if args[0] == 'info':
                await self.send_room_info(room)
        
        else:
            await self.send_message(room.room_id, f"❓ Unknown command: {command}")
    
    async def send_message(self, room_id: str, message: str, formatted_message: str = None):
        """Send message"""
        content = {
            "msgtype": "m.text",
            "body": message
        }
        
        if formatted_message:
            content["format"] = "org.matrix.custom.html"
            content["formatted_body"] = formatted_message
        
        try:
            await self.client.room_send(
                room_id=room_id,
                message_type="m.room.message",
                content=content
            )
        except Exception as e:
            print(f"❌ Message send error: {e}")
    
    async def send_welcome_message(self, room_id: str, new_member: str):
        """Send welcome message"""
        message = f"🎉 Welcome {new_member}!\n\nAvailable commands:\n!ping - Connection test\n!status - System status\n!weather [city] - Weather information\n!help - Show help"
        
        formatted_message = f"""
        <h3>🎉 Welcome {new_member}!</h3>
        <p><strong>Available commands:</strong></p>
        <ul>
            <li><code>!ping</code> - Connection test</li>
            <li><code>!status</code> - System status</li>
            <li><code>!weather [city]</code> - Weather information</li>
            <li><code>!help</code> - Show help</li>
        </ul>
        """
        
        await self.send_message(room_id, message, formatted_message)
    
    async def send_system_status(self, room_id: str):
        """Send system status"""
        import psutil
        import platform
        
        cpu_percent = psutil.cpu_percent(interval=1)
        memory = psutil.virtual_memory()
        disk = psutil.disk_usage('/')
        
        message = f"""📊 System Status
CPU Usage: {cpu_percent}%
Memory Usage: {memory.percent}%
Disk Usage: {disk.percent}%
Platform: {platform.system()} {platform.machine()}"""
        
        formatted_message = f"""
        <h4>📊 System Status</h4>
        <table>
            <tr><td><strong>CPU Usage</strong></td><td>{cpu_percent}%</td></tr>
            <tr><td><strong>Memory Usage</strong></td><td>{memory.percent}%</td></tr>
            <tr><td><strong>Disk Usage</strong></td><td>{disk.percent}%</td></tr>
            <tr><td><strong>Platform</strong></td><td>{platform.system()} {platform.machine()}</td></tr>
        </table>
        """
        
        await self.send_message(room_id, message, formatted_message)
    
    async def send_weather_info(self, room_id: str, city: str):
        """Send weather information"""
        api_key = os.getenv('WEATHER_API_KEY')
        if not api_key:
            await self.send_message(room_id, "❌ Weather API key not configured")
            return
        
        url = f"https://api.openweathermap.org/data/2.5/weather?q={city}&appid={api_key}&units=metric"
        
        try:
            async with aiohttp.ClientSession() as session:
                async with session.get(url) as response:
                    if response.status == 200:
                        weather_data = await response.json()
                        
                        message = f"""🌤️ Weather in {weather_data['name']}
Weather: {weather_data['weather'][0]['description']}
Temperature: {weather_data['main']['temp']}°C
Humidity: {weather_data['main']['humidity']}%
Wind Speed: {weather_data['wind']['speed']} m/s"""
                        
                        formatted_message = f"""
                        <h4>🌤️ Weather in {weather_data['name']}</h4>
                        <ul>
                            <li><strong>Weather:</strong> {weather_data['weather'][0]['description']}</li>
                            <li><strong>Temperature:</strong> {weather_data['main']['temp']}°C</li>
                            <li><strong>Humidity:</strong> {weather_data['main']['humidity']}%</li>
                            <li><strong>Wind Speed:</strong> {weather_data['wind']['speed']} m/s</li>
                        </ul>
                        """
                        
                        await self.send_message(room_id, message, formatted_message)
                    else:
                        await self.send_message(room_id, "❌ Failed to fetch weather information")
        except Exception as e:
            print(f"Weather API error: {e}")
            await self.send_message(room_id, "❌ Error occurred while fetching weather information")
    
    async def enable_encryption(self, room_id: str):
        """Enable room encryption"""
        try:
            await self.client.room_put_state(
                room_id=room_id,
                event_type="m.room.encryption",
                content={"algorithm": "m.megolm.v1.aes-sha2"}
            )
            await self.send_message(room_id, "🔐 Room encryption enabled")
        except Exception as e:
            print(f"Encryption error: {e}")
            await self.send_message(room_id, "❌ Failed to enable encryption")
    
    async def send_room_info(self, room: MatrixRoom):
        """Send room information"""
        member_count = len(room.users)
        
        message = f"""🏠 Room Information
Name: {room.display_name or room.room_id}
Members: {member_count}
Encryption: {'Enabled' if room.encrypted else 'Disabled'}
Room ID: {room.room_id}"""
        
        formatted_message = f"""
        <h4>🏠 Room Information</h4>
        <table>
            <tr><td><strong>Name</strong></td><td>{room.display_name or room.room_id}</td></tr>
            <tr><td><strong>Members</strong></td><td>{member_count}</td></tr>
            <tr><td><strong>Encryption</strong></td><td>{'Enabled' if room.encrypted else 'Disabled'}</td></tr>
            <tr><td><strong>Room ID</strong></td><td><code>{room.room_id}</code></td></tr>
        </table>
        """
        
        await self.send_message(room.room_id, message, formatted_message)
    
    async def start(self):
        """Start bot"""
        print("🚀 Starting Element Python Bot...")
        
        try:
            # Start sync
            sync_response = await self.client.sync(timeout=30000)
            
            if hasattr(sync_response, 'transport_response'):
                print(f"✅ Sync successful: {sync_response.transport_response.status}")
            
            # Continuous sync
            await self.client.sync_forever(timeout=30000)
            
        except Exception as e:
            print(f"❌ Bot start error: {e}")
        finally:
            await self.client.close()

# Usage example
async def main():
    homeserver_url = os.getenv('MATRIX_HOMESERVER_URL', 'https://matrix.org')
    user_id = os.getenv('MATRIX_USER_ID')
    access_token = os.getenv('MATRIX_ACCESS_TOKEN')
    
    if not all([user_id, access_token]):
        print("❌ Please set environment variables MATRIX_USER_ID, MATRIX_ACCESS_TOKEN")
        return
    
    bot = ElementPythonBot(homeserver_url, user_id, access_token)
    await bot.start()

if __name__ == "__main__":
    asyncio.run(main())

Environment Variables Configuration

# .env file
# Matrix Bot settings
MATRIX_HOMESERVER_URL=https://matrix.org
MATRIX_USER_ID=@your-bot:matrix.org
MATRIX_ACCESS_TOKEN=your-matrix-access-token

# Element Web settings
ELEMENT_WEB_URL=https://app.element.io

# Synapse Homeserver settings
SYNAPSE_SERVER_NAME=your-domain.com
POSTGRES_PASSWORD=your-secure-postgres-password

# External API integration
WEATHER_API_KEY=your-openweather-api-key
GITHUB_TOKEN=your-github-token

# Bridge settings (Discord Bridge example)
DISCORD_BOT_TOKEN=your-discord-bot-token
DISCORD_CLIENT_ID=your-discord-client-id

# SSL/TLS settings
[email protected]
SSL_CERT_PATH=/etc/letsencrypt/live/your-domain.com/fullchain.pem
SSL_KEY_PATH=/etc/letsencrypt/live/your-domain.com/privkey.pem

# Security settings
MATRIX_SECRET_KEY=your-very-long-secure-secret-key
ENABLE_REGISTRATION=false
ENABLE_GUEST_ACCESS=false

Security Hardening Configuration

# synapse/homeserver.yaml security settings example
# General config
server_name: "your-domain.com"
pid_file: /data/homeserver.pid
web_client_location: https://app.element.io/

# Security settings
enable_registration: false
enable_registration_without_verification: false
registrations_require_3pid:
  - email
allowed_local_3pids:
  - medium: email
    pattern: '.*@your-company\.com'

# Rate limiting
rc_message:
  per_second: 5
  burst_count: 10

rc_registration:
  per_second: 0.1
  burst_count: 3

rc_login:
  address:
    per_second: 0.3
    burst_count: 5
  account:
    per_second: 0.3
    burst_count: 5

# Content repository
max_upload_size: 50M
max_image_pixels: 32M
dynamic_thumbnails: false

# Encryption settings
encryption_enabled_by_default_for_room_type: all
trusted_key_servers:
  - server_name: "matrix.org"

# Privacy settings
allow_public_rooms_without_auth: false
allow_public_rooms_over_federation: false

# Federation
federation_domain_whitelist:
  - matrix.org
  - your-trusted-domain.com

# Metrics and monitoring
enable_metrics: true
report_stats: false

Nginx Reverse Proxy Configuration

# /etc/nginx/sites-available/matrix.your-domain.com
server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name matrix.your-domain.com;

    ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;
    ssl_trusted_certificate /etc/letsencrypt/live/your-domain.com/chain.pem;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512;
    ssl_prefer_server_ciphers off;

    # Element web client
    location / {
        proxy_pass http://127.0.0.1:80;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    # Synapse homeserver
    location /_matrix {
        proxy_pass http://127.0.0.1:8008;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Host $host;

        # Nginx by default only allows file uploads up to 1M in size
        client_max_body_size 50M;
    }

    # Security headers
    add_header X-Frame-Options DENY;
    add_header X-Content-Type-Options nosniff;
    add_header X-XSS-Protection "1; mode=block";
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
}

# HTTP redirect
server {
    listen 80;
    listen [::]:80;
    server_name matrix.your-domain.com;
    return 301 https://$server_name$request_uri;
}

Monitoring and Alerting Setup

# monitoring.py - Matrix homeserver monitoring
import asyncio
import aiohttp
import time
from prometheus_client import CollectorRegistry, Gauge, push_to_gateway

class MatrixMonitoring:
    def __init__(self, homeserver_url: str, access_token: str):
        self.homeserver_url = homeserver_url
        self.access_token = access_token
        self.registry = CollectorRegistry()
        
        # Prometheus metrics
        self.user_count = Gauge('matrix_users_total', 'Total number of users', registry=self.registry)
        self.room_count = Gauge('matrix_rooms_total', 'Total number of rooms', registry=self.registry)
        self.federation_count = Gauge('matrix_federation_total', 'Total federated servers', registry=self.registry)
    
    async def collect_metrics(self):
        """Collect system metrics"""
        headers = {'Authorization': f'Bearer {self.access_token}'}
        
        async with aiohttp.ClientSession() as session:
            # User count
            try:
                async with session.get(f'{self.homeserver_url}/_synapse/admin/v2/users', headers=headers) as resp:
                    if resp.status == 200:
                        data = await resp.json()
                        self.user_count.set(data.get('total', 0))
            except Exception as e:
                print(f"User count fetch error: {e}")
            
            # Room count
            try:
                async with session.get(f'{self.homeserver_url}/_synapse/admin/v1/rooms', headers=headers) as resp:
                    if resp.status == 200:
                        data = await resp.json()
                        self.room_count.set(data.get('total_rooms', 0))
            except Exception as e:
                print(f"Room count fetch error: {e}")
    
    async def check_federation_health(self):
        """Check federation health"""
        test_servers = ['matrix.org', 'mozilla.org']
        healthy_servers = 0
        
        for server in test_servers:
            try:
                async with aiohttp.ClientSession() as session:
                    async with session.get(f'{self.homeserver_url}/_matrix/federation/v1/version/{server}', timeout=10) as resp:
                        if resp.status == 200:
                            healthy_servers += 1
            except Exception as e:
                print(f"Federation check failed for {server}: {e}")
        
        self.federation_count.set(healthy_servers)
        return healthy_servers / len(test_servers)
    
    async def send_alert(self, message: str, severity: str = 'warning'):
        """Send alert"""
        # Send alert to Element/Matrix room
        # Using Bot API here
        pass
    
    async def run_monitoring(self):
        """Run monitoring"""
        while True:
            try:
                await self.collect_metrics()
                federation_health = await self.check_federation_health()
                
                # Send metrics to Prometheus
                push_to_gateway('localhost:9091', job='matrix_monitoring', registry=self.registry)
                
                # Alert check
                if federation_health < 0.5:
                    await self.send_alert("🚨 Federation connectivity issues detected")
                
                print(f"✅ Monitoring cycle completed at {time.strftime('%Y-%m-%d %H:%M:%S')}")
                
            except Exception as e:
                print(f"❌ Monitoring error: {e}")
            
            await asyncio.sleep(300)  # 5-minute intervals

# Usage example
async def main():
    monitor = MatrixMonitoring(
        homeserver_url=os.getenv('MATRIX_HOMESERVER_URL'),
        access_token=os.getenv('MATRIX_ADMIN_TOKEN')
    )
    await monitor.run_monitoring()

if __name__ == "__main__":
    asyncio.run(main())