From 1b33db499e1c8d71995e356efd08ff98311d0d27 Mon Sep 17 00:00:00 2001 From: "Ezra (Archivist)" Date: Sun, 5 Apr 2026 04:38:15 +0000 Subject: [PATCH] [matrix] Add Conduit deployment scaffold for #166, #183 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) --- deploy/conduit/Caddyfile | 58 +++++++ deploy/conduit/conduit.service | 37 ++++ deploy/conduit/conduit.toml | 81 +++++++++ deploy/conduit/install.sh | 121 +++++++++++++ deploy/conduit/scripts/backup.sh | 82 +++++++++ deploy/conduit/scripts/health.sh | 142 ++++++++++++++++ docs/matrix-fleet-comms/README.md | 271 ++++++++++++++++++++++++++++++ 7 files changed, 792 insertions(+) create mode 100644 deploy/conduit/Caddyfile create mode 100644 deploy/conduit/conduit.service create mode 100644 deploy/conduit/conduit.toml create mode 100644 deploy/conduit/install.sh create mode 100644 deploy/conduit/scripts/backup.sh create mode 100644 deploy/conduit/scripts/health.sh create mode 100644 docs/matrix-fleet-comms/README.md diff --git a/deploy/conduit/Caddyfile b/deploy/conduit/Caddyfile new file mode 100644 index 00000000..cce9a778 --- /dev/null +++ b/deploy/conduit/Caddyfile @@ -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 +} diff --git a/deploy/conduit/conduit.service b/deploy/conduit/conduit.service new file mode 100644 index 00000000..20551fac --- /dev/null +++ b/deploy/conduit/conduit.service @@ -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 diff --git a/deploy/conduit/conduit.toml b/deploy/conduit/conduit.toml new file mode 100644 index 00000000..083962a2 --- /dev/null +++ b/deploy/conduit/conduit.toml @@ -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 = [] diff --git a/deploy/conduit/install.sh b/deploy/conduit/install.sh new file mode 100644 index 00000000..817dd156 --- /dev/null +++ b/deploy/conduit/install.sh @@ -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" diff --git a/deploy/conduit/scripts/backup.sh b/deploy/conduit/scripts/backup.sh new file mode 100644 index 00000000..cad3f429 --- /dev/null +++ b/deploy/conduit/scripts/backup.sh @@ -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" diff --git a/deploy/conduit/scripts/health.sh b/deploy/conduit/scripts/health.sh new file mode 100644 index 00000000..e4f64813 --- /dev/null +++ b/deploy/conduit/scripts/health.sh @@ -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 "$@" diff --git a/docs/matrix-fleet-comms/README.md b/docs/matrix-fleet-comms/README.md new file mode 100644 index 00000000..17dfd985 --- /dev/null +++ b/docs/matrix-fleet-comms/README.md @@ -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