Architecture: - ADR-1: Conduit selected over Synapse/Dendrite (Rust, low resource) - ADR-2: Deploy on existing Gitea VPS initially - ADR-3: Full federation enabled Artifacts: - docs/matrix-fleet-comms/README.md (architecture + runbooks) - deploy/conduit/conduit.toml (production config) - deploy/conduit/conduit.service (systemd) - deploy/conduit/Caddyfile (reverse proxy) - deploy/conduit/install.sh (one-command installer) - deploy/conduit/scripts/backup.sh (automated backups) - deploy/conduit/scripts/health.sh (health monitoring) Closes #183 (scaffold complete) Progresses #166 (implementation unblocked)
This commit is contained in:
58
deploy/conduit/Caddyfile
Normal file
58
deploy/conduit/Caddyfile
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
# Caddy configuration for Conduit Matrix homeserver
|
||||||
|
# Location: /etc/caddy/conf.d/matrix.conf (imported by main Caddyfile)
|
||||||
|
# Reference: docs/matrix-fleet-comms/README.md
|
||||||
|
|
||||||
|
matrix.timmy.foundation {
|
||||||
|
# Reverse proxy to Conduit
|
||||||
|
reverse_proxy localhost:8448 {
|
||||||
|
# Headers for WebSocket upgrade (client sync)
|
||||||
|
header_up Host {host}
|
||||||
|
header_up X-Real-IP {remote}
|
||||||
|
header_up X-Forwarded-For {remote}
|
||||||
|
header_up X-Forwarded-Proto {scheme}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Security headers
|
||||||
|
header {
|
||||||
|
X-Frame-Options DENY
|
||||||
|
X-Content-Type-Options nosniff
|
||||||
|
X-XSS-Protection "1; mode=block"
|
||||||
|
Referrer-Policy strict-origin-when-cross-origin
|
||||||
|
Permissions-Policy "geolocation=(), microphone=(), camera=()"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Enable compression
|
||||||
|
encode gzip zstd
|
||||||
|
|
||||||
|
# Let's Encrypt automatic TLS
|
||||||
|
tls {
|
||||||
|
# Email for renewal notifications
|
||||||
|
# Uncomment and set: email admin@timmy.foundation
|
||||||
|
}
|
||||||
|
|
||||||
|
# Logging
|
||||||
|
log {
|
||||||
|
output file /var/log/caddy/matrix-access.log {
|
||||||
|
roll_size 100mb
|
||||||
|
roll_keep 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Well-known delegation for Matrix federation
|
||||||
|
# Allows other servers to discover our homeserver
|
||||||
|
timmy.foundation {
|
||||||
|
handle /.well-known/matrix/server {
|
||||||
|
header Content-Type application/json
|
||||||
|
respond `{"m.server": "matrix.timmy.foundation:443"}`
|
||||||
|
}
|
||||||
|
|
||||||
|
handle /.well-known/matrix/client {
|
||||||
|
header Content-Type application/json
|
||||||
|
header Access-Control-Allow-Origin *
|
||||||
|
respond `{"m.homeserver": {"base_url": "https://matrix.timmy.foundation"}}`
|
||||||
|
}
|
||||||
|
|
||||||
|
# Redirect root to Element Web or documentation
|
||||||
|
redir / https://matrix.timmy.foundation permanent
|
||||||
|
}
|
||||||
37
deploy/conduit/conduit.service
Normal file
37
deploy/conduit/conduit.service
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Conduit Matrix Homeserver
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=conduit
|
||||||
|
Group=conduit
|
||||||
|
|
||||||
|
WorkingDirectory=/opt/conduit
|
||||||
|
ExecStart=/opt/conduit/conduit
|
||||||
|
|
||||||
|
# Restart on failure
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=5
|
||||||
|
|
||||||
|
# Resource limits
|
||||||
|
LimitNOFILE=65536
|
||||||
|
|
||||||
|
# Security hardening
|
||||||
|
NoNewPrivileges=true
|
||||||
|
ProtectSystem=strict
|
||||||
|
ProtectHome=true
|
||||||
|
ReadWritePaths=/opt/conduit/data /opt/conduit/logs
|
||||||
|
ProtectKernelTunables=true
|
||||||
|
ProtectKernelModules=true
|
||||||
|
ProtectControlGroups=true
|
||||||
|
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
|
||||||
|
RestrictNamespaces=true
|
||||||
|
LockPersonality=true
|
||||||
|
|
||||||
|
# Environment
|
||||||
|
Environment="RUST_LOG=info"
|
||||||
|
Environment="CONDUIT_CONFIG=/opt/conduit/conduit.toml"
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
81
deploy/conduit/conduit.toml
Normal file
81
deploy/conduit/conduit.toml
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
# Conduit Homeserver Configuration
|
||||||
|
# Location: /opt/conduit/conduit.toml
|
||||||
|
# Reference: docs/matrix-fleet-comms/README.md
|
||||||
|
|
||||||
|
[global]
|
||||||
|
# The server_name is the canonical name of your homeserver.
|
||||||
|
# It must match the domain in your MXIDs (e.g., @user:timmy.foundation)
|
||||||
|
server_name = "timmy.foundation"
|
||||||
|
|
||||||
|
# Database path - SQLite for simplicity, PostgreSQL available if needed
|
||||||
|
database_path = "/opt/conduit/data/conduit.db"
|
||||||
|
|
||||||
|
# Port to listen on
|
||||||
|
port = 8448
|
||||||
|
|
||||||
|
# Maximum request size (20MB for file uploads)
|
||||||
|
max_request_size = 20000000
|
||||||
|
|
||||||
|
# Allow guests to register (false = closed registration)
|
||||||
|
allow_registration = false
|
||||||
|
|
||||||
|
# Allow guests to join rooms without registering
|
||||||
|
allow_guest_registration = false
|
||||||
|
|
||||||
|
# Require authentication for profile requests
|
||||||
|
authenticate_profile_requests = true
|
||||||
|
|
||||||
|
[registration]
|
||||||
|
# Closed registration - admin creates accounts manually
|
||||||
|
enabled = false
|
||||||
|
|
||||||
|
[federation]
|
||||||
|
# Enable federation to communicate with other Matrix homeservers
|
||||||
|
enabled = true
|
||||||
|
|
||||||
|
# Servers to block from federation
|
||||||
|
# disabled_servers = ["bad.actor.com", "spammer.org"]
|
||||||
|
disabled_servers = []
|
||||||
|
|
||||||
|
# Enable server discovery via .well-known
|
||||||
|
well_known = true
|
||||||
|
|
||||||
|
[media]
|
||||||
|
# Maximum upload size per file (50MB)
|
||||||
|
max_file_size = 50000000
|
||||||
|
|
||||||
|
# Maximum total media cache size (100MB)
|
||||||
|
max_media_size = 100000000
|
||||||
|
|
||||||
|
# Directory for media storage
|
||||||
|
media_path = "/opt/conduit/data/media"
|
||||||
|
|
||||||
|
[retention]
|
||||||
|
# Enable message retention policies
|
||||||
|
enabled = true
|
||||||
|
|
||||||
|
# Default retention for rooms without explicit policy
|
||||||
|
default_room_retention = "30d"
|
||||||
|
|
||||||
|
# Minimum allowed retention period
|
||||||
|
min_retention = "1d"
|
||||||
|
|
||||||
|
# Maximum allowed retention period (null = no limit)
|
||||||
|
max_retention = null
|
||||||
|
|
||||||
|
[logging]
|
||||||
|
# Log level: error, warn, info, debug, trace
|
||||||
|
level = "info"
|
||||||
|
|
||||||
|
# Log to file
|
||||||
|
log_file = "/opt/conduit/logs/conduit.log"
|
||||||
|
|
||||||
|
[security]
|
||||||
|
# Require transaction IDs for idempotent requests
|
||||||
|
require_transaction_ids = true
|
||||||
|
|
||||||
|
# IP range blacklist for incoming federation
|
||||||
|
# ip_range_blacklist = ["10.0.0.0/8", "172.16.0.0/12"]
|
||||||
|
|
||||||
|
# Allow incoming federation from these IP ranges only (empty = allow all)
|
||||||
|
# ip_range_whitelist = []
|
||||||
121
deploy/conduit/install.sh
Normal file
121
deploy/conduit/install.sh
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Conduit Matrix Homeserver Installation Script
|
||||||
|
# Location: Run this on target VPS after cloning timmy-config
|
||||||
|
# Reference: docs/matrix-fleet-comms/README.md
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
CONDUIT_VERSION="0.8.0" # Check https://gitlab.com/famedly/conduit/-/releases
|
||||||
|
CONDUIT_DIR="/opt/conduit"
|
||||||
|
DATA_DIR="$CONDUIT_DIR/data"
|
||||||
|
LOGS_DIR="$CONDUIT_DIR/logs"
|
||||||
|
SCRIPTS_DIR="$CONDUIT_DIR/scripts"
|
||||||
|
CONDUIT_USER="conduit"
|
||||||
|
|
||||||
|
echo "========================================"
|
||||||
|
echo "Conduit Matrix Homeserver Installer"
|
||||||
|
echo "Target: $CONDUIT_DIR"
|
||||||
|
echo "Version: $CONDUIT_VERSION"
|
||||||
|
echo "========================================"
|
||||||
|
echo
|
||||||
|
|
||||||
|
# Check root
|
||||||
|
if [ "$EUID" -ne 0 ]; then
|
||||||
|
echo "Error: Please run as root"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create conduit user
|
||||||
|
echo "[1/8] Creating conduit user..."
|
||||||
|
if ! id "$CONDUIT_USER" &>/dev/null; then
|
||||||
|
useradd -r -s /bin/false -d "$CONDUIT_DIR" "$CONDUIT_USER"
|
||||||
|
echo " Created user: $CONDUIT_USER"
|
||||||
|
else
|
||||||
|
echo " User exists: $CONDUIT_USER"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create directories
|
||||||
|
echo "[2/8] Creating directories..."
|
||||||
|
mkdir -p "$CONDUIT_DIR" "$DATA_DIR" "$LOGS_DIR" "$SCRIPTS_DIR"
|
||||||
|
chown -R "$CONDUIT_USER:$CONDUIT_USER" "$CONDUIT_DIR"
|
||||||
|
|
||||||
|
# Download Conduit
|
||||||
|
echo "[3/8] Downloading Conduit v${CONDUIT_VERSION}..."
|
||||||
|
ARCH=$(uname -m)
|
||||||
|
case "$ARCH" in
|
||||||
|
x86_64)
|
||||||
|
CONDUIT_ARCH="x86_64-unknown-linux-gnu"
|
||||||
|
;;
|
||||||
|
aarch64)
|
||||||
|
CONDUIT_ARCH="aarch64-unknown-linux-gnu"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Error: Unsupported architecture: $ARCH"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
CONDUIT_URL="https://gitlab.com/famedly/conduit/-/releases/download/v${CONDUIT_VERSION}/conduit-${CONDUIT_ARCH}"
|
||||||
|
|
||||||
|
curl -L -o "$CONDUIT_DIR/conduit" "$CONDUIT_URL"
|
||||||
|
chmod +x "$CONDUIT_DIR/conduit"
|
||||||
|
chown "$CONDUIT_USER:$CONDUIT_USER" "$CONDUIT_DIR/conduit"
|
||||||
|
echo " Downloaded: $CONDUIT_DIR/conduit"
|
||||||
|
|
||||||
|
# Install configuration
|
||||||
|
echo "[4/8] Installing configuration..."
|
||||||
|
if [ -f "conduit.toml" ]; then
|
||||||
|
cp conduit.toml "$CONDUIT_DIR/conduit.toml"
|
||||||
|
chown "$CONDUIT_USER:$CONDUIT_USER" "$CONDUIT_DIR/conduit.toml"
|
||||||
|
echo " Installed: $CONDUIT_DIR/conduit.toml"
|
||||||
|
else
|
||||||
|
echo " Warning: conduit.toml not found in current directory"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Install systemd service
|
||||||
|
echo "[5/8] Installing systemd service..."
|
||||||
|
if [ -f "conduit.service" ]; then
|
||||||
|
cp conduit.service /etc/systemd/system/conduit.service
|
||||||
|
systemctl daemon-reload
|
||||||
|
echo " Installed: /etc/systemd/system/conduit.service"
|
||||||
|
else
|
||||||
|
echo " Warning: conduit.service not found in current directory"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Install scripts
|
||||||
|
echo "[6/8] Installing operational scripts..."
|
||||||
|
if [ -d "scripts" ]; then
|
||||||
|
cp scripts/*.sh "$SCRIPTS_DIR/"
|
||||||
|
chmod +x "$SCRIPTS_DIR"/*.sh
|
||||||
|
chown -R "$CONDUIT_USER:$CONDUIT_USER" "$SCRIPTS_DIR"
|
||||||
|
echo " Installed scripts to $SCRIPTS_DIR"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create backup directory
|
||||||
|
echo "[7/8] Creating backup directory..."
|
||||||
|
mkdir -p /backups/conduit
|
||||||
|
chown "$CONDUIT_USER:$CONDUIT_USER" /backups/conduit
|
||||||
|
|
||||||
|
# Setup cron for backups
|
||||||
|
echo "[8/8] Setting up backup cron job..."
|
||||||
|
if [ -f "$SCRIPTS_DIR/backup.sh" ]; then
|
||||||
|
(crontab -l 2>/dev/null || true; echo "0 3 * * * $SCRIPTS_DIR/backup.sh >> $LOGS_DIR/backup.log 2>&1") | crontab -
|
||||||
|
echo " Backup cron job added (3 AM daily)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "========================================"
|
||||||
|
echo "Installation Complete!"
|
||||||
|
echo "========================================"
|
||||||
|
echo
|
||||||
|
echo "Next steps:"
|
||||||
|
echo " 1. Configure DNS: matrix.timmy.foundation -> $(hostname -I | awk '{print $1}')"
|
||||||
|
echo " 2. Configure Caddy: cp Caddyfile /etc/caddy/conf.d/matrix.conf"
|
||||||
|
echo " 3. Start Conduit: systemctl start conduit"
|
||||||
|
echo " 4. Check health: $SCRIPTS_DIR/health.sh"
|
||||||
|
echo " 5. Create admin account (see README.md)"
|
||||||
|
echo
|
||||||
|
echo "Logs: $LOGS_DIR/"
|
||||||
|
echo "Data: $DATA_DIR/"
|
||||||
|
echo "Config: $CONDUIT_DIR/conduit.toml"
|
||||||
82
deploy/conduit/scripts/backup.sh
Normal file
82
deploy/conduit/scripts/backup.sh
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Conduit Matrix Homeserver Backup Script
|
||||||
|
# Location: /opt/conduit/scripts/backup.sh
|
||||||
|
# Reference: docs/matrix-fleet-comms/README.md
|
||||||
|
# Run via cron: 0 3 * * * /opt/conduit/scripts/backup.sh
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
BACKUP_BASE_DIR="/backups/conduit"
|
||||||
|
DATA_DIR="/opt/conduit/data"
|
||||||
|
CONFIG_FILE="/opt/conduit/conduit.toml"
|
||||||
|
RETENTION_DAYS=7
|
||||||
|
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
||||||
|
BACKUP_DIR="$BACKUP_BASE_DIR/$TIMESTAMP"
|
||||||
|
|
||||||
|
# Ensure backup directory exists
|
||||||
|
mkdir -p "$BACKUP_DIR"
|
||||||
|
|
||||||
|
log() {
|
||||||
|
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"
|
||||||
|
}
|
||||||
|
|
||||||
|
log "Starting Conduit backup..."
|
||||||
|
|
||||||
|
# Check if Conduit is running
|
||||||
|
if systemctl is-active --quiet conduit; then
|
||||||
|
log "Stopping Conduit for consistent backup..."
|
||||||
|
systemctl stop conduit
|
||||||
|
RESTART_NEEDED=true
|
||||||
|
else
|
||||||
|
log "Conduit already stopped"
|
||||||
|
RESTART_NEEDED=false
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Backup database
|
||||||
|
if [ -f "$DATA_DIR/conduit.db" ]; then
|
||||||
|
log "Backing up database..."
|
||||||
|
cp "$DATA_DIR/conduit.db" "$BACKUP_DIR/"
|
||||||
|
sqlite3 "$BACKUP_DIR/conduit.db" "VACUUM;"
|
||||||
|
else
|
||||||
|
log "WARNING: Database not found at $DATA_DIR/conduit.db"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Backup configuration
|
||||||
|
if [ -f "$CONFIG_FILE" ]; then
|
||||||
|
log "Backing up configuration..."
|
||||||
|
cp "$CONFIG_FILE" "$BACKUP_DIR/"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Backup media (if exists)
|
||||||
|
if [ -d "$DATA_DIR/media" ]; then
|
||||||
|
log "Backing up media files..."
|
||||||
|
cp -r "$DATA_DIR/media" "$BACKUP_DIR/"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Restart Conduit if it was running
|
||||||
|
if [ "$RESTART_NEEDED" = true ]; then
|
||||||
|
log "Restarting Conduit..."
|
||||||
|
systemctl start conduit
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create compressed archive
|
||||||
|
log "Creating compressed archive..."
|
||||||
|
cd "$BACKUP_BASE_DIR"
|
||||||
|
tar czf "$TIMESTAMP.tar.gz" -C "$BACKUP_DIR" .
|
||||||
|
rm -rf "$BACKUP_DIR"
|
||||||
|
|
||||||
|
ARCHIVE_SIZE=$(du -h "$BACKUP_BASE_DIR/$TIMESTAMP.tar.gz" | cut -f1)
|
||||||
|
log "Backup complete: $TIMESTAMP.tar.gz ($ARCHIVE_SIZE)"
|
||||||
|
|
||||||
|
# Upload to S3 (uncomment and configure when ready)
|
||||||
|
# if command -v aws &> /dev/null; then
|
||||||
|
# log "Uploading to S3..."
|
||||||
|
# aws s3 cp "$BACKUP_BASE_DIR/$TIMESTAMP.tar.gz" s3://timmy-backups/conduit/
|
||||||
|
# fi
|
||||||
|
|
||||||
|
# Cleanup old backups
|
||||||
|
log "Cleaning up backups older than $RETENTION_DAYS days..."
|
||||||
|
find "$BACKUP_BASE_DIR" -name "*.tar.gz" -mtime +$RETENTION_DAYS -delete
|
||||||
|
|
||||||
|
log "Backup process complete"
|
||||||
142
deploy/conduit/scripts/health.sh
Normal file
142
deploy/conduit/scripts/health.sh
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Conduit Matrix Homeserver Health Check
|
||||||
|
# Location: /opt/conduit/scripts/health.sh
|
||||||
|
# Reference: docs/matrix-fleet-comms/README.md
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
HOMESERVER_URL="https://matrix.timmy.foundation"
|
||||||
|
ADMIN_EMAIL="admin@timmy.foundation"
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
log_info() {
|
||||||
|
echo -e "${GREEN}[INFO]${NC} $*"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_warn() {
|
||||||
|
echo -e "${YELLOW}[WARN]${NC} $*"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_error() {
|
||||||
|
echo -e "${RED}[ERROR]${NC} $*"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if Conduit process is running
|
||||||
|
check_process() {
|
||||||
|
if systemctl is-active --quiet conduit; then
|
||||||
|
log_info "Conduit service is running"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
log_error "Conduit service is not running"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check Matrix client-server API
|
||||||
|
check_client_api() {
|
||||||
|
local response
|
||||||
|
response=$(curl -s -o /dev/null -w "%{http_code}" "$HOMESERVER_URL/_matrix/client/versions" 2>/dev/null || echo "000")
|
||||||
|
|
||||||
|
if [ "$response" = "200" ]; then
|
||||||
|
log_info "Client-server API is responding (HTTP 200)"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
log_error "Client-server API returned HTTP $response"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check Matrix versions endpoint
|
||||||
|
check_versions() {
|
||||||
|
local versions
|
||||||
|
versions=$(curl -s "$HOMESERVER_URL/_matrix/client/versions" 2>/dev/null | jq -r '.versions | join(", ")' 2>/dev/null || echo "unknown")
|
||||||
|
|
||||||
|
if [ "$versions" != "unknown" ]; then
|
||||||
|
log_info "Supported Matrix versions: $versions"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
log_warn "Could not determine Matrix versions"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check federation (self-test)
|
||||||
|
check_federation() {
|
||||||
|
local response
|
||||||
|
response=$(curl -s -o /dev/null -w "%{http_code}" "https://federationtester.matrix.org/api/report?server_name=timmy.foundation" 2>/dev/null || echo "000")
|
||||||
|
|
||||||
|
if [ "$response" = "200" ]; then
|
||||||
|
log_info "Federation tester can reach server"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
log_warn "Federation tester returned HTTP $response (may be DNS propagation)"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check disk space
|
||||||
|
check_disk_space() {
|
||||||
|
local usage
|
||||||
|
usage=$(df /opt/conduit/data | tail -1 | awk '{print $5}' | sed 's/%//')
|
||||||
|
|
||||||
|
if [ "$usage" -lt 80 ]; then
|
||||||
|
log_info "Disk usage: ${usage}% (healthy)"
|
||||||
|
return 0
|
||||||
|
elif [ "$usage" -lt 90 ]; then
|
||||||
|
log_warn "Disk usage: ${usage}% (consider cleanup)"
|
||||||
|
return 1
|
||||||
|
else
|
||||||
|
log_error "Disk usage: ${usage}% (critical!)"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check database size
|
||||||
|
check_database() {
|
||||||
|
local db_path="/opt/conduit/data/conduit.db"
|
||||||
|
|
||||||
|
if [ -f "$db_path" ]; then
|
||||||
|
local size
|
||||||
|
size=$(du -h "$db_path" | cut -f1)
|
||||||
|
log_info "Database size: $size"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
log_warn "Database file not found at $db_path"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Main health check
|
||||||
|
main() {
|
||||||
|
echo "========================================"
|
||||||
|
echo "Conduit Matrix Homeserver Health Check"
|
||||||
|
echo "Server: $HOMESERVER_URL"
|
||||||
|
echo "Time: $(date)"
|
||||||
|
echo "========================================"
|
||||||
|
echo
|
||||||
|
|
||||||
|
local exit_code=0
|
||||||
|
|
||||||
|
check_process || exit_code=1
|
||||||
|
check_client_api || exit_code=1
|
||||||
|
check_versions || true # Non-critical
|
||||||
|
check_federation || true # Non-critical during initial setup
|
||||||
|
check_disk_space || exit_code=1
|
||||||
|
check_database || true # Non-critical
|
||||||
|
|
||||||
|
echo
|
||||||
|
if [ $exit_code -eq 0 ]; then
|
||||||
|
log_info "All critical checks passed ✓"
|
||||||
|
else
|
||||||
|
log_error "Some critical checks failed ✗"
|
||||||
|
fi
|
||||||
|
|
||||||
|
return $exit_code
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
||||||
271
docs/matrix-fleet-comms/README.md
Normal file
271
docs/matrix-fleet-comms/README.md
Normal file
@@ -0,0 +1,271 @@
|
|||||||
|
# Matrix/Conduit Fleet Communications
|
||||||
|
|
||||||
|
**Parent Issues**: [#166](https://gitea.timmy/time/Timmy_Foundation/timmy-config/issues/166) | [#183](https://gitea.timmy/time/Timmy_Foundation/timmy-config/issues/183)
|
||||||
|
**Status**: Architecture Complete → Implementation Ready
|
||||||
|
**Owner**: @ezra (architect) → TBD (implementer)
|
||||||
|
**Created**: 2026-04-05
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
Fulfill [Son of Timmy Commandment 6](https://gitea.timmy/time/Timmy_Foundation/timmy-config/blob/main/son-of-timmy.md): establish Matrix/Conduit as the sovereign operator surface for human-to-fleet encrypted communication, moving beyond Telegram as the sole command channel.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Architecture Decision Records
|
||||||
|
|
||||||
|
### ADR-1: Homeserver Selection — Conduit
|
||||||
|
|
||||||
|
**Decision**: Use [Conduit](https://conduit.rs/) (Rust-based Matrix homeserver)
|
||||||
|
|
||||||
|
**Rationale**:
|
||||||
|
| Criteria | Conduit | Synapse | Dendrite |
|
||||||
|
|----------|---------|---------|----------|
|
||||||
|
| Resource Usage | Low (Rust) | High (Python) | Medium (Go) |
|
||||||
|
| Federation | Full | Full | Partial |
|
||||||
|
| Deployment Complexity | Simple binary | Complex stack | Medium |
|
||||||
|
| SQLite Support | Yes (simpler) | No (requires PG) | Yes |
|
||||||
|
| Federation Stability | Production | Production | Beta |
|
||||||
|
|
||||||
|
**Verdict**: Conduit's low resource footprint and SQLite option make it ideal for fleet deployment.
|
||||||
|
|
||||||
|
### ADR-2: Host Selection
|
||||||
|
|
||||||
|
**Decision**: Deploy on existing Gitea VPS (143.198.27.163:3000) initially
|
||||||
|
|
||||||
|
**Rationale**:
|
||||||
|
- Existing infrastructure, known operational state
|
||||||
|
- Sufficient resources (can upgrade if federation load grows)
|
||||||
|
- Consolidated with Gitea simplifies backup/restore
|
||||||
|
|
||||||
|
**Future**: Dedicated Matrix VPS if federation traffic justifies separation.
|
||||||
|
|
||||||
|
### ADR-3: Federation Strategy
|
||||||
|
|
||||||
|
**Decision**: Full federation enabled from day one
|
||||||
|
|
||||||
|
**Rationale**:
|
||||||
|
- Alexander may need to message from any Matrix account
|
||||||
|
- Fleet bots can federate to other homeservers if needed
|
||||||
|
- Nostr bridge experiments (#830) may benefit from federation
|
||||||
|
|
||||||
|
**Implication**: Requires valid TLS certificate and public DNS.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Deployment Scaffold
|
||||||
|
|
||||||
|
### Directory Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
/opt/conduit/
|
||||||
|
├── conduit # Binary
|
||||||
|
├── conduit.toml # Configuration
|
||||||
|
├── data/ # SQLite + media (backup target)
|
||||||
|
│ ├── conduit.db
|
||||||
|
│ └── media/
|
||||||
|
├── logs/ # Rotated logs
|
||||||
|
└── scripts/ # Operational helpers
|
||||||
|
├── backup.sh
|
||||||
|
└── rotate-logs.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### Port Allocation
|
||||||
|
|
||||||
|
| Service | Port | Protocol | Notes |
|
||||||
|
|---------|------|----------|-------|
|
||||||
|
| Conduit HTTP | 8448 | TCP | Matrix client-server API |
|
||||||
|
| Conduit Federation | 8448 | TCP | Same port, different SRV |
|
||||||
|
| Element Web | 8080 | TCP | Optional web client |
|
||||||
|
|
||||||
|
**DNS Requirements**:
|
||||||
|
- `matrix.timmy.foundation` → A record to VPS IP
|
||||||
|
- `_matrix._tcp.timmy.foundation` → SRV record for federation
|
||||||
|
|
||||||
|
### Reverse Proxy (Caddy)
|
||||||
|
|
||||||
|
```caddyfile
|
||||||
|
matrix.timmy.foundation {
|
||||||
|
reverse_proxy localhost:8448
|
||||||
|
|
||||||
|
header {
|
||||||
|
X-Frame-Options DENY
|
||||||
|
X-Content-Type-Options nosniff
|
||||||
|
}
|
||||||
|
|
||||||
|
tls {
|
||||||
|
# Let's Encrypt automatic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Conduit Configuration (conduit.toml)
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[global]
|
||||||
|
server_name = "timmy.foundation"
|
||||||
|
database_path = "/opt/conduit/data/conduit.db"
|
||||||
|
port = 8448
|
||||||
|
max_request_size = 20000000 # 20MB for file uploads
|
||||||
|
|
||||||
|
[registration]
|
||||||
|
# Closed registration - admin creates accounts
|
||||||
|
enabled = false
|
||||||
|
|
||||||
|
[ federation]
|
||||||
|
enabled = true
|
||||||
|
disabled_servers = []
|
||||||
|
|
||||||
|
[ media ]
|
||||||
|
max_file_size = 50000000 # 50MB
|
||||||
|
max_media_size = 100000000 # 100MB total cache
|
||||||
|
|
||||||
|
[ retention ]
|
||||||
|
enabled = true
|
||||||
|
default_room_retention = "30d"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prerequisites Checklist
|
||||||
|
|
||||||
|
### Infrastructure
|
||||||
|
- [ ] DNS A record: `matrix.timmy.foundation` → 143.198.27.163
|
||||||
|
- [ ] DNS SRV record: `_matrix._tcp.timmy.foundation` → 0 0 8448 matrix.timmy.foundation
|
||||||
|
- [ ] Firewall: TCP 8448 open to world (federation)
|
||||||
|
- [ ] Firewall: TCP 8080 open to world (Element Web, optional)
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
- [ ] Conduit binary (latest release: check https://gitlab.com/famedly/conduit)
|
||||||
|
- [ ] Caddy installed (or nginx if preferred)
|
||||||
|
- [ ] SQLite (usually present, verify version ≥ 3.30)
|
||||||
|
- [ ] systemd (for service management)
|
||||||
|
|
||||||
|
### Accounts (Bootstrap)
|
||||||
|
- [ ] `@admin:timmy.foundation` — Server admin
|
||||||
|
- [ ] `@alexander:timmy.foundation` — Operator primary
|
||||||
|
- [ ] `@ezra:timmy.foundation` — Archivist bot
|
||||||
|
- [ ] `@timmy:timmy.foundation` — Coordinator bot
|
||||||
|
|
||||||
|
### Rooms (Bootstrap)
|
||||||
|
- [ ] `#fleet-ops:timmy.foundation` — Operator-to-fleet command channel
|
||||||
|
- [ ] `#fleet-intel:timmy.foundation` — Intelligence sharing
|
||||||
|
- [ ] `#fleet-social:timmy.foundation` — General chat
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Implementation Phases
|
||||||
|
|
||||||
|
### Phase 1: Infrastructure (Est: 2 hours)
|
||||||
|
1. Create DNS records
|
||||||
|
2. Open firewall ports
|
||||||
|
3. Download Conduit binary
|
||||||
|
4. Create directory structure
|
||||||
|
|
||||||
|
### Phase 2: Deployment (Est: 2 hours)
|
||||||
|
1. Write conduit.toml
|
||||||
|
2. Create systemd service
|
||||||
|
3. Configure Caddy reverse proxy
|
||||||
|
4. Start Conduit, verify health
|
||||||
|
|
||||||
|
### Phase 3: Bootstrap (Est: 1 hour)
|
||||||
|
1. Create admin account via CLI
|
||||||
|
2. Create user accounts
|
||||||
|
3. Create rooms, set permissions
|
||||||
|
4. Verify end-to-end encryption
|
||||||
|
|
||||||
|
### Phase 4: Migration Planning (Est: 4 hours)
|
||||||
|
1. Map Telegram channels to Matrix rooms
|
||||||
|
2. Design bridge architecture (if needed)
|
||||||
|
3. Create cutover timeline
|
||||||
|
4. Document operator onboarding
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Operational Runbooks
|
||||||
|
|
||||||
|
### Backup
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
# /opt/conduit/scripts/backup.sh
|
||||||
|
BACKUP_DIR="/backups/conduit/$(date +%Y%m%d_%H%M%S)"
|
||||||
|
mkdir -p "$BACKUP_DIR"
|
||||||
|
|
||||||
|
# Stop Conduit briefly for consistent snapshot
|
||||||
|
systemctl stop conduit
|
||||||
|
|
||||||
|
cp /opt/conduit/data/conduit.db "$BACKUP_DIR/"
|
||||||
|
cp /opt/conduit/conduit.toml "$BACKUP_DIR/"
|
||||||
|
cp -r /opt/conduit/data/media "$BACKUP_DIR/"
|
||||||
|
|
||||||
|
systemctl start conduit
|
||||||
|
|
||||||
|
# Compress and upload to S3/backup target
|
||||||
|
tar czf "$BACKUP_DIR.tar.gz" -C "$BACKUP_DIR" .
|
||||||
|
# aws s3 cp "$BACKUP_DIR.tar.gz" s3://timmy-backups/conduit/
|
||||||
|
```
|
||||||
|
|
||||||
|
### Account Creation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# As admin, create new user
|
||||||
|
curl -X POST \
|
||||||
|
-H "Authorization: Bearer $ADMIN_TOKEN" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"username":"newuser","password":"secure_password_123"}' \
|
||||||
|
https://matrix.timmy.foundation/_matrix/client/v3/register
|
||||||
|
```
|
||||||
|
|
||||||
|
### Health Check
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
# /opt/conduit/scripts/health.sh
|
||||||
|
curl -s https://matrix.timmy.foundation/_matrix/client/versions | jq .
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Cross-Issue Linkages
|
||||||
|
|
||||||
|
| Issue | Relationship | Action |
|
||||||
|
|-------|--------------|--------|
|
||||||
|
| #166 | Parent epic | This scaffold enables #166 execution |
|
||||||
|
| #183 | Scaffold child | This document fulfills #183 acceptance criteria |
|
||||||
|
| #830 | Deep Dive | Matrix rooms can receive #830 intelligence briefings |
|
||||||
|
| #137 | Related | Verify no conflict with existing comms work |
|
||||||
|
| #138 | Related | Verify no conflict with Nostr bridge |
|
||||||
|
| #147 | Related | Check if Matrix replaces or supplements existing plans |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Artifacts Created
|
||||||
|
|
||||||
|
| File | Purpose |
|
||||||
|
|------|---------|
|
||||||
|
| `docs/matrix-fleet-comms/README.md` | This architecture document |
|
||||||
|
| `deploy/conduit/conduit.toml` | Production configuration |
|
||||||
|
| `deploy/conduit/conduit.service` | systemd service definition |
|
||||||
|
| `deploy/conduit/Caddyfile` | Reverse proxy configuration |
|
||||||
|
| `deploy/conduit/scripts/backup.sh` | Backup automation |
|
||||||
|
| `deploy/conduit/scripts/health.sh` | Health check script |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Next Actions
|
||||||
|
|
||||||
|
1. **DNS**: Create `matrix.timmy.foundation` A and SRV records
|
||||||
|
2. **Firewall**: Open TCP 8448 on VPS
|
||||||
|
3. **Install**: Download and configure Conduit
|
||||||
|
4. **Bootstrap**: Create initial accounts and rooms
|
||||||
|
5. **Onboard**: Add Alexander, test end-to-end encryption
|
||||||
|
6. **Migrate**: Plan Telegram→Matrix transition
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Ezra's Sign-off**: This scaffold transforms #166 from fuzzy epic to executable implementation plan. All prerequisites are named, all acceptance criteria are mapped to artifacts, and the deployment path is phase-gated for incremental delivery.
|
||||||
|
|
||||||
|
— Ezra, Archivist
|
||||||
|
2026-04-05
|
||||||
Reference in New Issue
Block a user