Some checks failed
Forge CI / smoke-and-build (pull_request) Failing after 1m1s
Deploy Synapse on Ezra VPS with PostgreSQL backend, bot registration, and management tooling. Closes #272 Components: - docker-compose.yml: Synapse + PostgreSQL 16 stack - homeserver.yaml: Production config (registration disabled, rate limits, retention) - setup.sh: One-shot deploy (generates secrets, starts stack, registers accounts, gets bot token) - manage.sh: Day-to-day ops (status, restart, logs, backup, update, create-user, teardown) - docs/synapse-deployment.md: Full deployment guide with Nginx TLS, DNS, troubleshooting Security: - Registration disabled by default - Rate limiting on login/registration/messages - Client API bound to localhost (Nginx proxy for public access) - Secrets chmod 600, .gitignore'd - Federation certificate verification enabled Bot account auto-registered and access token acquired — credentials written to synapse-credentials.env for hermes-agent integration.
212 lines
7.6 KiB
Bash
Executable File
212 lines
7.6 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Synapse Homeserver — One-Shot Setup Script
|
|
# Matrix Phase 1: Deploy Synapse on Ezra VPS
|
|
#
|
|
# Usage:
|
|
# ./setup.sh <server_name> [admin_user] [admin_password]
|
|
#
|
|
# Example:
|
|
# ./setup.sh matrix.timmy-time.xyz hermes-bot 'secure-pass-123'
|
|
#
|
|
# What it does:
|
|
# 1. Generates .env with secrets
|
|
# 2. Prepares homeserver.yaml with correct server name
|
|
# 3. Generates signing key
|
|
# 4. Starts Synapse + PostgreSQL via Docker Compose
|
|
# 5. Waits for Synapse to be healthy
|
|
# 6. Registers admin user + bot account
|
|
# 7. Outputs Matrix credentials for hermes-agent
|
|
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
cd "$SCRIPT_DIR"
|
|
|
|
# --- Colors ---
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
CYAN='\033[0;36m'
|
|
NC='\033[0m'
|
|
|
|
info() { echo -e "${GREEN}[SETUP]${NC} $*"; }
|
|
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
|
|
error() { echo -e "${RED}[ERROR]${NC} $*"; exit 1; }
|
|
|
|
# --- Args ---
|
|
SERVER_NAME="${1:?Usage: $0 <server_name> [admin_user] [admin_password]}"
|
|
ADMIN_USER="${2:-timmy-admin}"
|
|
ADMIN_PASS="${3:-$(openssl rand -hex 16)}"
|
|
BOT_USER="${4:-hermes-bot}"
|
|
BOT_PASS="${5:-$(openssl rand -hex 16)}"
|
|
|
|
echo -e "${CYAN}"
|
|
echo "╔══════════════════════════════════════════════════╗"
|
|
echo "║ Synapse Homeserver — Matrix Phase 1 Deploy ║"
|
|
echo "╚══════════════════════════════════════════════════╝"
|
|
echo -e "${NC}"
|
|
info "Server name: $SERVER_NAME"
|
|
info "Admin user: @$ADMIN_USER:$SERVER_NAME"
|
|
info "Bot user: @$BOT_USER:$SERVER_NAME"
|
|
echo ""
|
|
|
|
# --- Preflight ---
|
|
info "Preflight checks..."
|
|
command -v docker >/dev/null 2>&1 || error "docker not found. Install Docker first."
|
|
command -v docker compose version >/dev/null 2>&1 || error "docker compose not found. Install Docker Compose plugin."
|
|
info "Docker: $(docker --version | head -1)"
|
|
info "Compose: $(docker compose version | head -1)"
|
|
|
|
# --- Generate .env ---
|
|
info "Generating .env..."
|
|
POSTGRES_PASSWORD=$(openssl rand -hex 24)
|
|
REGISTRATION_SECRET=$(openssl rand -hex 16)
|
|
|
|
cat > .env <<EOF
|
|
# Synapse deployment — generated $(date -u +%Y-%m-%dT%H:%M:%SZ)
|
|
# DO NOT COMMIT THIS FILE
|
|
|
|
POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
|
|
SYNAPSE_SERVER_NAME=${SERVER_NAME}
|
|
SYNAPSE_REPORT_STATS=no
|
|
REGISTRATION_SECRET=${REGISTRATION_SECRET}
|
|
EOF
|
|
chmod 600 .env
|
|
info ".env written with secure permissions"
|
|
|
|
# --- Prepare homeserver.yaml ---
|
|
info "Preparing homeserver.yaml..."
|
|
sed -i.bak "s/SERVER_NAME_PLACEHOLDER/${SERVER_NAME}/g" homeserver.yaml
|
|
rm -f homeserver.yaml.bak
|
|
info "Server name set to: $SERVER_NAME"
|
|
|
|
# --- Generate signing key ---
|
|
info "Generating signing key..."
|
|
# Synapse will generate its own key on first run if missing
|
|
# But we pre-create the data volume structure
|
|
docker volume create synapse_data >/dev/null 2>&1 || true
|
|
docker volume create synapse_db >/dev/null 2>&1 || true
|
|
|
|
# --- Start the stack ---
|
|
info "Starting Synapse + PostgreSQL..."
|
|
docker compose up -d
|
|
|
|
# --- Wait for Synapse to be healthy ---
|
|
info "Waiting for Synapse to start (up to 120s)..."
|
|
MAX_WAIT=120
|
|
ELAPSED=0
|
|
while [ $ELAPSED -lt $MAX_WAIT ]; do
|
|
if curl -sfS http://127.0.0.1:8008/health >/dev/null 2>&1; then
|
|
info "Synapse is healthy!"
|
|
break
|
|
fi
|
|
sleep 3
|
|
ELAPSED=$((ELAPSED + 3))
|
|
if [ $((ELAPSED % 15)) -eq 0 ]; then
|
|
info "Still waiting... (${ELAPSED}s)"
|
|
fi
|
|
done
|
|
|
|
if [ $ELAPSED -ge $MAX_WAIT ]; then
|
|
warn "Synapse did not respond within ${MAX_WAIT}s. Check logs:"
|
|
echo " docker compose logs synapse"
|
|
error "Aborting registration."
|
|
fi
|
|
|
|
# --- Register admin user ---
|
|
info "Registering admin user @$ADMIN_USER:$SERVER_NAME..."
|
|
docker compose exec -T synapse register_new_matrix_user \
|
|
http://localhost:8008 \
|
|
-c /data/homeserver.yaml \
|
|
-u "$ADMIN_USER" \
|
|
-p "$ADMIN_PASS" \
|
|
--admin \
|
|
--no-extra-prompt 2>&1 || {
|
|
# User might already exist if re-running
|
|
warn "Admin user registration returned non-zero (may already exist)"
|
|
}
|
|
|
|
# --- Register bot user ---
|
|
info "Registering bot user @$BOT_USER:$SERVER_NAME..."
|
|
docker compose exec -T synapse register_new_matrix_user \
|
|
http://localhost:8008 \
|
|
-c /data/homeserver.yaml \
|
|
-u "$BOT_USER" \
|
|
-p "$BOT_PASS" \
|
|
--no-admin \
|
|
--no-extra-prompt 2>&1 || {
|
|
warn "Bot user registration returned non-zero (may already exist)"
|
|
}
|
|
|
|
# --- Get bot access token ---
|
|
info "Acquiring bot access token..."
|
|
BOT_TOKEN_RESPONSE=$(curl -sfS -X POST "http://127.0.0.1:8008/_matrix/client/v3/login" \
|
|
-H 'Content-Type: application/json' \
|
|
-d "{
|
|
\"type\": \"m.login.password\",
|
|
\"identifier\": {
|
|
\"type\": \"m.id.user\",
|
|
\"user\": \"${BOT_USER}\"
|
|
},
|
|
\"password\": \"${BOT_PASS}\",
|
|
\"device_name\": \"Hermes Agent\"
|
|
}")
|
|
|
|
BOT_ACCESS_TOKEN=$(echo "$BOT_TOKEN_RESPONSE" | python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])" 2>/dev/null || echo "FAILED_TO_EXTRACT")
|
|
BOT_DEVICE_ID=$(echo "$BOT_TOKEN_RESPONSE" | python3 -c "import sys,json; print(json.load(sys.stdin)['device_id'])" 2>/dev/null || echo "UNKNOWN")
|
|
|
|
if [ "$BOT_ACCESS_TOKEN" = "FAILED_TO_EXTRACT" ]; then
|
|
warn "Could not extract bot access token automatically."
|
|
warn "Login manually: curl -X POST http://127.0.0.1:8008/_matrix/client/v3/login ..."
|
|
fi
|
|
|
|
# --- Write credentials file ---
|
|
CREDENTIALS_FILE="synapse-credentials.env"
|
|
cat > "$CREDENTIALS_FILE" <<EOF
|
|
# Synapse Credentials — generated $(date -u +%Y-%m-%dT%H:%M:%SZ)
|
|
# Add these to hermes-agent's ~/.hermes/.env
|
|
|
|
# Matrix integration
|
|
MATRIX_HOMESERVER=http://${SERVER_NAME}:8008
|
|
MATRIX_ACCESS_TOKEN=${BOT_ACCESS_TOKEN}
|
|
MATRIX_USER_ID=@${BOT_USER}:${SERVER_NAME}
|
|
MATRIX_DEVICE_ID=${BOT_DEVICE_ID}
|
|
MATRIX_ENCRYPTION=true
|
|
|
|
# Admin credentials (for user management)
|
|
SYNAPSE_ADMIN_USER=@${ADMIN_USER}:${SERVER_NAME}
|
|
SYNAPSE_ADMIN_PASSWORD=${ADMIN_PASS}
|
|
|
|
# Bot credentials
|
|
SYNAPSE_BOT_USER=@${BOT_USER}:${SERVER_NAME}
|
|
SYNAPSE_BOT_PASSWORD=${BOT_PASS}
|
|
EOF
|
|
chmod 600 "$CREDENTIALS_FILE"
|
|
info "Credentials written to: $CREDENTIALS_FILE"
|
|
|
|
# --- Summary ---
|
|
echo ""
|
|
echo -e "${GREEN}╔══════════════════════════════════════════════════╗${NC}"
|
|
echo -e "${GREEN}║ Synapse Deployed Successfully! ║${NC}"
|
|
echo -e "${GREEN}╚══════════════════════════════════════════════════╝${NC}"
|
|
echo ""
|
|
echo -e " Server: ${CYAN}https://${SERVER_NAME}${NC}"
|
|
echo -e " Client API: ${CYAN}http://127.0.0.1:8008${NC}"
|
|
echo -e " Federation: ${CYAN}https://${SERVER_NAME}:8448${NC}"
|
|
echo ""
|
|
echo -e " Admin: ${YELLOW}@${ADMIN_USER}:${SERVER_NAME}${NC}"
|
|
echo -e " Bot: ${YELLOW}@${BOT_USER}:${SERVER_NAME}${NC}"
|
|
echo -e " Bot Token: ${YELLOW}${BOT_ACCESS_TOKEN:0:20}...${NC}"
|
|
echo ""
|
|
echo -e " Credentials: ${CYAN}${SCRIPT_DIR}/${CREDENTIALS_FILE}${NC}"
|
|
echo ""
|
|
echo -e "${GREEN}Next steps:${NC}"
|
|
echo " 1. Point DNS: ${SERVER_NAME} → $(curl -s ifconfig.me 2>/dev/null || echo '<VPS_IP>')"
|
|
echo " 2. Set up TLS: nginx/certbot reverse proxy for :8008 and :8448"
|
|
echo " 3. Copy credentials to hermes-agent: cp ${CREDENTIALS_FILE} ~/.hermes/.env"
|
|
echo " 4. Start hermes: hermes gateway --platform matrix"
|
|
echo ""
|
|
echo " Manage: docker compose logs -f | docker compose restart | docker compose down"
|
|
echo " Users: docker compose exec synapse register_new_matrix_user http://localhost:8008 -c /data/homeserver.yaml -u <user> -p <pass>"
|
|
echo ""
|