From 70ff03f05c8297ea5b7130ee031ffa92aab101ae Mon Sep 17 00:00:00 2001 From: cortex Date: Wed, 20 May 2026 16:22:00 +0100 Subject: [PATCH 1/9] refactored interactive section into one script --- init/scripts/compose_gen.sh | 71 +++++++-------- init/scripts/config_gen.sh | 8 +- init/scripts/config_link.sh | 8 +- init/scripts/set_env.sh | 62 ------------- init/scripts/worker_gen.sh | 8 +- setup.sh | 175 ++++++++++++++++++++++++++---------- 6 files changed, 171 insertions(+), 161 deletions(-) delete mode 100755 init/scripts/set_env.sh diff --git a/init/scripts/compose_gen.sh b/init/scripts/compose_gen.sh index f5936ed..bb74b58 100755 --- a/init/scripts/compose_gen.sh +++ b/init/scripts/compose_gen.sh @@ -11,51 +11,41 @@ OUTPUT_FILE="${ROOT_DIR}/compose.yaml" require_file "$TEMPLATE_FILE" "Compose template" -log_info "Configuring optional services..." +log_info "Generating compose.yaml based on .env configuration..." -read -r -p "Enable LiveKit RTC Stack? (Used for Element call) (Y/n) [Y]: " LIVEKIT_INPUT -LIVEKIT_INPUT="${LIVEKIT_INPUT:-Y}" -if [[ "$LIVEKIT_INPUT" =~ ^[Yy]$ ]]; then - INCLUDE_LIVEKIT=true - LIVEKIT_KEY=$(openssl rand -hex 16) - LIVEKIT_SECRET=$(openssl rand -hex 32) - cat >> "$ENV_FILE" </dev/null | grep -q "idp_id"; then yq -y -i "$(cat "$TEMP_FILTER")" "$SYNAPSE_CFG" || { log_error "Failed to inject OIDC config"; exit 1; } fi -if [[ -n "${LIVEKIT_API_KEY:-}" ]]; then +if [[ "${INCLUDE_LIVEKIT}" == "TRUE" ]]; then JWT_URL="https://${JWT_FQDN:-jwt.${DOMAIN}}" - if yq '.livekit.url' "$SYNAPSE_CFG" 2>/dev/null | grep -q "."; then - yq -y -i ".livekit.url = \"$JWT_URL\"" "$SYNAPSE_CFG" - else - yq -y -i ".livekit.url = \"$JWT_URL\"" "$SYNAPSE_CFG" - fi + yq -y -i ".livekit.url = \"$JWT_URL\"" "$SYNAPSE_CFG" fi log_info "OIDC and LiveKit linking complete" diff --git a/init/scripts/set_env.sh b/init/scripts/set_env.sh deleted file mode 100755 index 3086f56..0000000 --- a/init/scripts/set_env.sh +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -source "${SCRIPT_DIR}/functions.sh" -resolve_paths - -main() { - > "$ENV_FILE" - - read -r -p "Base Domain (e.g. example.org) [example.org]: " DOMAIN_INPUT - DOMAIN="${DOMAIN_INPUT:-example.org}" - auto_var "DOMAIN" $DOMAIN "no" - log_info "Base Domain set to: ${DOMAIN}" - - read -r -p "Auto-generate secure defaults for remaining fields? [Y/n]: " GEN_INPUT - GEN_INPUT="${GEN_INPUT:-Y}" - - if [[ "$GEN_INPUT" =~ ^[Yy]$ ]]; then - log_info "Generating secure defaults..." - - auto_var "USER_ID" "1000" "no" - auto_var "GROUP_ID" "1000" "no" - auto_var "VOLUME_PATH" "./matrix" "no" - auto_var "COUNTRY" "GB" "no" - auto_var "PG_USER" "postgres" "no" - auto_var "PG_PASSWORD" "generated" "yes" - auto_var "PG_USER_MATRIX" "matrix" "no" - auto_var "PG_DB_MATRIX" "synapse" "no" - auto_var "PG_PASSWORD_MATRIX" "generated" "yes" - auto_var "PG_USER_MAS" "mas" "no" - auto_var "PG_DB_MAS" "auth" "no" - auto_var "PG_PASSWORD_MAS" "generated" "yes" - - log_info "All fields auto-generated." - else - log_info "Manual entry mode." - - - - - - prompt_var "USER_ID" "Docker UserId" "1000" "no" - prompt_var "GROUP_ID" "Docker GroupID" "1000" "no" - prompt_var "VOLUME_PATH" "Install directory" "./matrix" "no" - prompt_var "COUNTRY" "Country Code (e.g. GB)" "GB" "no" - prompt_var "PG_USER" "PostgreSQL Superuser" "postgres" "no" - prompt_var "PG_PASSWORD" "PostgreSQL Superuser Password" "generated" "yes" - prompt_var "PG_USER_MATRIX" "Synapse Database User" "matrix" "no" - prompt_var "PG_DB_MATRIX" "Synapse Database Name" "synapse" "no" - prompt_var "PG_PASSWORD_MATRIX" "Synapse Database Password" "generated" "yes" - prompt_var "PG_USER_MAS" "MAS Database User" "mas" "no" - prompt_var "PG_DB_MAS" "MAS Database Name" "auth" "no" - prompt_var "PG_PASSWORD_MAS" "MAS Database Password" "generated" "yes" - - chmod 600 "$ENV_FILE" - - log_info "Environment file created: $ENV_FILE" - log_warn "Add $ENV_FILE to .gitignore and backup securely" -} - -main "$@" diff --git a/init/scripts/worker_gen.sh b/init/scripts/worker_gen.sh index 607ac61..b9460e5 100755 --- a/init/scripts/worker_gen.sh +++ b/init/scripts/worker_gen.sh @@ -38,9 +38,9 @@ for ((i=1; i<=WORKER_COUNT; i++)); do sed -e "s/{{WORKER_NAME}}/${worker_name}/g" \ -e "s/{{PORT}}/${port}/g" \ -e "s/{{SERVER_NAME}}/${HOMESERVER_FQDN}/g" \ - -e "s/{{DB_USER}}/${DB_USER:-synapse}/g" \ - -e "s/{{DB_PASSWORD}}/${DB_PASSWORD:-synapse_password}/g" \ - -e "s/{{DB_NAME}}/${DB_NAME:-synapse}/g" \ + -e "s/{{DB_USER}}/${PG_USER_MATRIX}/g" + -e "s/{{DB_PASSWORD}}/${PG_PASSWORD_MATRIX}/g" + -e "s/{{DB_NAME}}/${PG_DB_MATRIX}/g" "$TEMPLATE_CONFIG" > "${WORKERS_DIR}/${worker_name}.yaml" sed -e "s/{{WORKER_NAME}}/${worker_name}/g" \ @@ -51,7 +51,7 @@ for ((i=1; i<=WORKER_COUNT; i++)); do "$TEMPLATE_LOG" > "${WORKERS_DIR}/${worker_name}.log.config" TRAEFIK_LABELS="" - if [[ "$TRAEFIK_ENABLED" == "1" && -n "$DOMAIN" ]]; then + if [[ "$PROXY" == "TRAEFIK" && -n "$DOMAIN" ]]; then TRAEFIK_LABELS=" labels: - traefik.enable=true - traefik.http.services.${worker_name}.loadbalancer.server.port=${port} diff --git a/setup.sh b/setup.sh index 341c7f6..2f5f125 100755 --- a/setup.sh +++ b/setup.sh @@ -1,7 +1,6 @@ #!/usr/bin/env bash -# AMMIDS Setup Orchestrator -# Author: Cortex@Fossgate -# Src: https://git.fossgate.uk/ammids +# AMMIDS Master Setup Wizard +# Orchestrates the entire deployment flow via a single interactive session set -euo pipefail @@ -9,60 +8,144 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "${SCRIPT_DIR}/init/scripts/functions.sh" resolve_paths -ENV_FILE="${ROOT_DIR}/.env" +# Ensure dependencies +require_command "openssl" || require_command "pwgen" -log_info "=== AMMIDS Setup Orchestrator ===" +log_info "=== AMMIDS Interactive Setup Wizard ===" -# 1. Environment Setup -if [[ -f "$ENV_FILE" ]]; then - log_warn ".env exists." - read -r -p " Overwrite and create backup? [y/N]: " confirm - if [[ "$confirm" =~ ^[Yy]$ ]]; then - mkdir -p "$BACKUP_DIR" - BACKUP_FILE="${SCRIPT_DIR}/.env.$(date +%Y%m%d_%H%M%S)" - cp "$ENV_FILE" "$BACKUP_FILE" - log_info "Backup created: $BACKUP_FILE" - "${SCRIPT_DIR}/init/scripts/set_env.sh" - else - log_info "Preserving existing .env. Skipping regeneration." +# 1. Base Configuration +read -r -p "Base Domain (e.g. example.org) [example.org]: " DOMAIN_INPUT +BASE_DOMAIN="${DOMAIN_INPUT:-example.org}" + +log_info "Domain set: ${BASE_DOMAIN}" + +# 2. Service Selection +echo "" +log_info "Select Services:" + +# LiveKit +read -r -p "Enable LiveKit RTC Stack? (Y/n) [Y]: " LIVEKIT_INPUT +LIVEKIT_INPUT="${LIVEKIT_INPUT:-Y}" +if [[ "$LIVEKIT_INPUT" =~ ^[Yy]$ ]]; then + INCLUDE_LIVEKIT="true" +else + INCLUDE_LIVEKIT="false" +fi + +# Web Client (Set to a single value: ELEMENT, CINNY, FLUFFY, or NONE) +echo "Select Web Client: 1) Element 2) Cinny 3) FluffyChat 4) None" +read -r -p "Default [1]: " WEB_APP_INPUT +WEB_APP_INPUT="${WEB_APP_INPUT:-1}" +case "$WEB_APP_INPUT" in + 1) WEBAPP="ELEMENT" ;; + 2) WEBAPP="CINNY" ;; + 3) WEBAPP="FLUFFY" ;; + *) WEBAPP="NONE" ;; +esac +log_info "Web Client selected: ${WEBAPP}" + +# Reverse Proxy (Set to a single value: TRAEFIK or NGINX) +echo "Select Reverse Proxy: 1) Traefik (Handles TLS) 2) Nginx (Behind existing proxy)" +read -r -p "Default [1]: " PROXY_INPUT +PROXY_INPUT="${PROXY_INPUT:-1}" +case "$PROXY_INPUT" in + 1) PROXY="TRAEFIK" ;; + *) PROXY="NGINX" ;; +esac +log_info "Proxy selected: ${PROXY}" + +# Worker Count +read -r -p "Number of Synapse Workers? [Default: 1]: " WORKER_INPUT +WORKER_INPUT="${WORKER_INPUT:-1}" +if ! [[ "$WORKER_INPUT" =~ ^[0-9]+$ ]]; then + log_error "Invalid number. Defaulting to 1." + WORKER_INPUT=1 +fi + +# 3. Environment Variables +echo "" +read -r -p "Auto-generate secure passwords and defaults? [Y/n]: " AUTO_GEN_INPUT +AUTO_GEN_INPUT="${AUTO_GEN_INPUT:-Y}" + +# Initialize .env +> "$ENV_FILE" + +# --- Generate Secrets & Defaults --- +if [[ "$AUTO_GEN_INPUT" =~ ^[Yy]$ ]]; then + log_info "Generating secure defaults..." + auto_var "VOLUME_PATH" "./matrix" "no" + auto_var "DOMAIN" "${BASE_DOMAIN}" "no" + auto_var "HOMESERVER_FQDN" "matrix.${BASE_DOMAIN}" "no" + auto_var "MAS_FQDN" "auth.${BASE_DOMAIN}" "no" + if [[ "$WEBAPP" != "NONE" ]]; then + auto_var "APP_FQDN" "app.${BASE_DOMAIN}" "no" + fi + auto_var "USER_ID" "1000" "no" + auto_var "GROUP_ID" "1000" "no" + auto_var "COUNTRY" "GB" "no" + auto_var "PG_USER" "postgres" "no" + auto_var "PG_PASSWORD" "generated" "yes" + auto_var "PG_USER_MATRIX" "matrix" "no" + auto_var "PG_DB_MATRIX" "synapse" "no" + auto_var "PG_PASSWORD_MATRIX" "generated" "yes" + auto_var "PG_USER_MAS" "mas" "no" + auto_var "PG_DB_MAS" "auth" "no" + auto_var "PG_PASSWORD_MAS" "generated" "yes" + # Generate LiveKit keys if enabled + if [[ "$INCLUDE_LIVEKIT" == "true" ]]; then + echo "LIVEKIT_API_KEY=\"$(openssl rand -hex 16)\"" >> "$ENV_FILE" + echo "LIVEKIT_API_SECRET=\"$(openssl rand -hex 32)\"" >> "$ENV_FILE" + echo "LIVEKIT_FQDN=\"rtc.${BASE_DOMAIN}\"" >> "$ENV_FILE" + echo "JWT_FQDN=\"jwt.${BASE_DOMAIN}\"" >> "$ENV_FILE" + echo "LIVEKIT_UDP_RANGE=\"10000-20000\"" >> "$ENV_FILE" fi else - log_info "No .env found. Running initial setup..." - "${SCRIPT_DIR}/init/scripts/set_env.sh" + log_info "Manual entry mode." + prompt_var "VOLUME_PATH" "Install directory" "./matrix" "no" + auto_var "DOMAIN" "${BASE_DOMAIN}" "no" + prompt_var "HOMESERVER_FQDN" "Homeserver SubDomain" "matrix" "no" + prompt_var "MAS_FQDN" "authentication SubDomain" "auth" "no" + if [[ "$WEBAPP" != "NONE" ]]; then + prompt_var "APP_FQDN" "Web App SubDomain" "app" "no" + fi + prompt_var "USER_ID" "Docker UserID" "1000" "no" + prompt_var "GROUP_ID" "Docker GroupID" "1000" "no" + prompt_var "COUNTRY" "Country Code (e.g. GB)" "GB" "no" + prompt_var "PG_USER" "PostgreSQL Superuser" "postgres" "no" + prompt_var "PG_PASSWORD" "PostgreSQL Superuser Password" "generated" "yes" + prompt_var "PG_USER_MATRIX" "Synapse Database User" "matrix" "no" + prompt_var "PG_DB_MATRIX" "Synapse Database Name" "synapse" "no" + prompt_var "PG_PASSWORD_MATRIX" "Synapse Database Password" "generated" "yes" + prompt_var "PG_USER_MAS" "MAS Database User" "mas" "no" + prompt_var "PG_DB_MAS" "MAS Database Name" "auth" "no" + prompt_var "PG_PASSWORD_MAS" "MAS Database Password" "generated" "yes" fi -# 2. Load Environment & Resolve Paths -source "$ENV_FILE" -resolve_volume_path +auto_var "WORKER_COUNT" "${WORKER_INPUT}" "no" +auto_var "INCLUDE_LIVEKIT" "${INCLUDE_LIVEKIT}" "no" +auto_var "WEBAPP" "${WEBAPP}" "no" +auto_var "PROXY" "${PROXY}" "no" -# 3. Directory & Permission Management -if [[ ! -d "$VOLUME_PATH" ]]; then - log_info "Creating data directory: $VOLUME_PATH" - mkdir -p "$VOLUME_PATH" -fi -fix_ownership "$VOLUME_PATH" +chmod 600 "$ENV_FILE" +log_info "Configuration saved to ${ENV_FILE}" -# 4. Stack Generation -log_info "Generating compose and configurations..." +# 4. Execution Phase +echo "" +log_info "=== Executing Deployment Plan ===" + +log_info "1. Generating Compose File..." "${SCRIPT_DIR}/init/scripts/compose_gen.sh" + +log_info "2. Initializing Database & Configs..." "${SCRIPT_DIR}/init/scripts/config_gen.sh" + +log_info "3. Linking Services (OIDC)..." "${SCRIPT_DIR}/init/scripts/config_link.sh" -# 5. Worker Generation -read -r -p "Number of worker processes? [Default: 1]: " worker_count -worker_count="${worker_count:-1}" - -if ! [[ "$worker_count" =~ ^[0-9]+$ ]]; then - log_error "Invalid number. Aborting worker generation." - exit 1 +if [[ ${WORKER_INPUT} -gt 1 ]]; then + log_info "4. Generating Workers (${WORKER_INPUT})..." + "${SCRIPT_DIR}/init/scripts/worker_gen.sh" -c "$WORKER_INPUT" fi - -if [[ "$worker_count" -gt 0 ]]; then - log_info "Generating $worker_count worker(s)..." - "${SCRIPT_DIR}/init/scripts/worker_gen.sh" -c "$worker_count" -else - log_info "Skipping worker generation (count: 0)." -fi - -log_info "Setup complete. Run 'docker compose up -d' to start services." +log_info "=== Setup Complete ===" +log_info "Run 'docker compose up -d' to start your stack." -- 2.52.0 From 790782fada525a9b0a0671015d411c2948c27b8b Mon Sep 17 00:00:00 2001 From: cortex Date: Wed, 20 May 2026 21:49:06 +0100 Subject: [PATCH 2/9] Worker-gen rewrite --- init/scripts/config_gen.sh | 4 +- init/scripts/worker_gen.sh | 100 ++++++++++++++++++++++++------------- 2 files changed, 67 insertions(+), 37 deletions(-) diff --git a/init/scripts/config_gen.sh b/init/scripts/config_gen.sh index da2d1e2..9949a2e 100755 --- a/init/scripts/config_gen.sh +++ b/init/scripts/config_gen.sh @@ -51,7 +51,7 @@ docker run -it --rm --network matrix_network \ -v "${VOLUME_PATH}/synapse:/data" \ -e SYNAPSE_SERVER_NAME="$HOMESERVER_FQDN" \ -e SYNAPSE_REPORT_STATS=no \ - ghcr.io/element-hq/synapse:latest generate + ghcr.io/element-hq/synapse:latest generate || { log_error "Synapse config generation failed"; exit 1; } fix_ownership "${VOLUME_PATH}/synapse" @@ -59,7 +59,7 @@ log_info "Generating MAS configuration..." docker run -it --rm --network matrix_network \ -v "${VOLUME_PATH}/mas:/data" \ ghcr.io/element-hq/matrix-authentication-service:latest config generate \ - | grep -v '^[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}' > "${VOLUME_PATH}/mas/config.yaml" + | grep -v '^[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}' > "${VOLUME_PATH}/mas/config.yaml" || { log_error "Mas config generation failed"; exit 1; } fix_ownership "${VOLUME_PATH}/mas" log_info "Configuration complete" diff --git a/init/scripts/worker_gen.sh b/init/scripts/worker_gen.sh index b9460e5..c16d8ae 100755 --- a/init/scripts/worker_gen.sh +++ b/init/scripts/worker_gen.sh @@ -4,16 +4,8 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "${SCRIPT_DIR}/functions.sh" resolve_paths - -WORKER_COUNT=1 -while getopts "c:" opt; do - case $opt in - c) WORKER_COUNT="$OPTARG" ;; - *) echo "Usage: $0 [-c count]"; exit 1 ;; - esac -done - require_env +require_command "yq" resolve_volume_path TEMPLATE_COMPOSE="${INIT_DIR}/templates/worker.yaml" @@ -25,24 +17,29 @@ require_file "$TEMPLATE_CONFIG" "Config template" require_file "$TEMPLATE_LOG" "Log template" WORKERS_DIR="${VOLUME_PATH}/synapse/workers" -mkdir -p "$WORKERS_DIR" "${ROOT_DIR}/.tmp" +mkdir -p "$WORKERS_DIR" "${SCRIPT_DIR}/tmp" -TRAEFIK_ENABLED="${PROXY:-0}" -TEMP_COMPOSE="${ROOT_DIR}/.tmp/compose_workers.yaml" +TEMP_COMPOSE="${SCRIPT_DIR}/tmp/compose_workers.yaml" +# Initialize empty compose file for workers > "$TEMP_COMPOSE" for ((i=1; i<=WORKER_COUNT; i++)); do worker_name="synapse-worker-${i}" port=$((9093 + i)) + TEMP_WORKER_COMPOSE="${SCRIPT_DIR}/tmp/${worker_name}.yaml" + log_info "Generating configuration for ${worker_name}..." + + # 1. Generate Worker Config (synapse config file) sed -e "s/{{WORKER_NAME}}/${worker_name}/g" \ -e "s/{{PORT}}/${port}/g" \ -e "s/{{SERVER_NAME}}/${HOMESERVER_FQDN}/g" \ - -e "s/{{DB_USER}}/${PG_USER_MATRIX}/g" - -e "s/{{DB_PASSWORD}}/${PG_PASSWORD_MATRIX}/g" - -e "s/{{DB_NAME}}/${PG_DB_MATRIX}/g" + -e "s/{{DB_USER}}/${PG_USER_MATRIX}/g" \ + -e "s/{{DB_PASSWORD}}/${PG_PASSWORD_MATRIX}/g" \ + -e "s/{{DB_NAME}}/${PG_DB_MATRIX}/g" \ "$TEMPLATE_CONFIG" > "${WORKERS_DIR}/${worker_name}.yaml" + # 2. Generate Log Config sed -e "s/{{WORKER_NAME}}/${worker_name}/g" \ -e "s/{{MAX_BYTES}}/${LOG_MAX_BYTES:-10485760}/g" \ -e "s/{{BACKUP_COUNT}}/${LOG_BACKUP_COUNT:-5}/g" \ @@ -50,37 +47,70 @@ for ((i=1; i<=WORKER_COUNT; i++)); do -e "s/{{SYNAPSE_LOG_LEVEL}}/${SYNAPSE_LOG_LEVEL:-INFO}/g" \ "$TEMPLATE_LOG" > "${WORKERS_DIR}/${worker_name}.log.config" - TRAEFIK_LABELS="" - if [[ "$PROXY" == "TRAEFIK" && -n "$DOMAIN" ]]; then - TRAEFIK_LABELS=" labels: - - traefik.enable=true - - traefik.http.services.${worker_name}.loadbalancer.server.port=${port} - - traefik.http.routers.synapse-worker-${worker_name}.rule=Host(\`${DOMAIN}\`) && PathPrefix(\`/_matrix\`) - - traefik.http.routers.synapse-worker-${worker_name}.entrypoints=websecure - - traefik.http.routers.synapse-worker-${worker_name}.service=${worker_name} - - traefik.http.routers.synapse-worker-${worker_name}.priority=10 -" - fi - + # 3. Generate Temporary Compose Snippet for this worker + # Start with the base template sed -e "s/{{WORKER_NAME}}/${worker_name}/g" \ -e "s/{{PORT}}/${port}/g" \ -e "s/{{APP_TYPE}}/synapse.app.generic_worker/g" \ - -e "s/{{TRAEFIK_LABELS}}/${TRAEFIK_LABELS}/g" \ - "$TEMPLATE_COMPOSE" >> "$TEMP_COMPOSE" + "$TEMPLATE_COMPOSE" > "$TEMP_WORKER_COMPOSE" - echo "" >> "$TEMP_COMPOSE" + # 4. Conditionally Inject Traefik Labels using yq + if [[ "$PROXY" == "TRAEFIK" && -n "$DOMAIN" ]]; then + log_info " -> Adding Traefik labels for ${worker_name}" + + # Construct the labels array safely + # Note: We escape backticks for the shell, but yq handles the YAML structure + yq -i '.services."'"${worker_name}"'".labels = [ + "traefik.enable=true", + "traefik.http.services.'"${worker_name}"'.loadbalancer.server.port='"${port}"'", + "traefik.http.routers.synapse-worker-'"${worker_name}"'.rule=Host(`'"${DOMAIN}"'`) && PathPrefix(`/_matrix`)", + "traefik.http.routers.synapse-worker-'"${worker_name}"'.entrypoints=websecure", + "traefik.http.routers.synapse-worker-'"${worker_name}"'.service='"${worker_name}"'", + "traefik.http.routers.synapse-worker-'"${worker_name}"'.priority=10" + ]' "$TEMP_WORKER_COMPOSE" + else + # Ensure labels key doesn't exist if not using Traefik + yq -i 'del(.services."'"${worker_name}"'".labels)' "$TEMP_WORKER_COMPOSE" 2>/dev/null || true + fi + + # 5. Merge this worker into the temporary aggregate compose file + # If TEMP_COMPOSE is empty, just copy. Otherwise, merge. + if [[ ! -s "$TEMP_COMPOSE" ]]; then + cp "$TEMP_WORKER_COMPOSE" "$TEMP_COMPOSE" + else + # Merge: existing content * new worker content + yq eval-all "select(fileIndex == 0) * select(fileIndex == 1)" \ + "$TEMP_COMPOSE" "$TEMP_WORKER_COMPOSE" > "${TEMP_COMPOSE}.merged" + mv "${TEMP_COMPOSE}.merged" "$TEMP_COMPOSE" + fi + + # Cleanup temp worker file + rm -f "$TEMP_WORKER_COMPOSE" done +# 6. Final Merge into main compose.yaml if [[ -f "$COMPOSE_FILE" ]]; then + # Check if workers already exist to avoid duplicates if ! grep -qE "^ synapse-worker-[0-9]+:" "$COMPOSE_FILE"; then - cat "$TEMP_COMPOSE" >> "$COMPOSE_FILE" - log_info "Appended ${WORKER_COUNT} worker(s) to compose.yaml" + # Merge main compose with generated workers + yq eval-all "select(fileIndex == 0) * select(fileIndex == 1)" \ + "$COMPOSE_FILE" "$TEMP_COMPOSE" > "${COMPOSE_FILE}.new" + + if mv "${COMPOSE_FILE}.new" "$COMPOSE_FILE"; then + log_info "Appended ${WORKER_COUNT} worker(s) to compose.yaml" + else + log_error "Failed to update compose.yaml" + exit 1 + fi else - log_warn "Some workers already exist. Manual merge required" - cp "$TEMP_COMPOSE" "${ROOT_DIR}/.tmp/compose_workers.yaml" + log_warn "Some workers already exist in compose.yaml. Manual merge required." + cp "$TEMP_COMPOSE" "${SCRIPT_DIR}/tmp/compose_workers.yaml" + log_warn "Saved new worker definitions to ${SCRIPT_DIR}/tmp/compose_workers.yaml" fi else - log_warn "compose.yaml not found. Generated: ${TEMP_COMPOSE}" + # If no main compose exists, the generated file becomes the main one + mv "$TEMP_COMPOSE" "$COMPOSE_FILE" + log_info "Created new compose.yaml with ${WORKER_COUNT} worker(s)" fi rm -f "$TEMP_COMPOSE" -- 2.52.0 From 5606d8083eeeb02c34102c43709cfd2cd2c17089 Mon Sep 17 00:00:00 2001 From: cortex Date: Thu, 21 May 2026 07:13:08 +0100 Subject: [PATCH 3/9] config rewrite to fix oidc issues --- init/scripts/config_gen.sh | 26 ++++++++++++++++++++++++-- init/scripts/config_link.sh | 27 +++++++++++++++++++++++---- 2 files changed, 47 insertions(+), 6 deletions(-) diff --git a/init/scripts/config_gen.sh b/init/scripts/config_gen.sh index 9949a2e..a5a3c3b 100755 --- a/init/scripts/config_gen.sh +++ b/init/scripts/config_gen.sh @@ -17,7 +17,7 @@ log_info "Initializing PostgreSQL..." docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" up -d --wait matrix-db # Wait for database readiness (silent except on failure) -for ATTEMPT in $(seq 1 30); do +for ATTEMPT in $(seq 1 60); do if docker compose -f "$COMPOSE_FILE" exec -T matrix-db psql -U "$PG_USER" -c "SELECT 1;" > /dev/null 2>&1; then break fi @@ -44,7 +44,7 @@ SELECT CASE WHEN NOT EXISTS (SELECT 1 FROM pg_database WHERE datname = 'synapse' EOF ) -echo "$INIT_SQL" | docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" exec -T matrix-db psql -U "$PG_USER" -f - +echo "$INIT_SQL" | docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" exec -T matrix-db psql -U "$PG_USER" -f - > /dev/null log_info "Generating Synapse configuration..." docker run -it --rm --network matrix_network \ @@ -53,13 +53,35 @@ docker run -it --rm --network matrix_network \ -e SYNAPSE_REPORT_STATS=no \ ghcr.io/element-hq/synapse:latest generate || { log_error "Synapse config generation failed"; exit 1; } +SYNAPSE_CFG="${VOLUME_PATH}/synapse/homeserver.yaml" +yq -y -i '.database.name = "psycopg2"' "$SYNAPSE_CFG" +yq -y -i ".database.args.user = \"${PG_USER_MATRIX}\"" "$SYNAPSE_CFG" +yq -y -i ".database.args.password = \"${PG_PASSWORD_MATRIX}\"" "$SYNAPSE_CFG" +yq -y -i ".database.args.database = \"${PG_DB_MATRIX}\"" "$SYNAPSE_CFG" +yq -y -i ".database.args.host = \"matrix-db\"" "$SYNAPSE_CFG" +yq -y -i ".database.args.port = 5432" "$SYNAPSE_CFG" +yq -y -i ".database.args.sslmode = \"prefer\"" "$SYNAPSE_CFG" +yq -y -i ".public_baseurl = \"https://${HOMESERVER_FQDN}\"" "$SYNAPSE_CFG" +yq -y -i ".server_name = \"${HOMESERVER_FQDN}\"" "$SYNAPSE_CFG" + fix_ownership "${VOLUME_PATH}/synapse" log_info "Generating MAS configuration..." + docker run -it --rm --network matrix_network \ -v "${VOLUME_PATH}/mas:/data" \ ghcr.io/element-hq/matrix-authentication-service:latest config generate \ | grep -v '^[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}' > "${VOLUME_PATH}/mas/config.yaml" || { log_error "Mas config generation failed"; exit 1; } +MAS_CFG="${VOLUME_PATH}/mas/config.yaml" +# Ensure 'clients' array exists (prevents errors if we try to append later) +if ! yq '.clients' "$MAS_CFG" > /dev/null 2>&1; then + yq -y -i '.clients = []' "$MAS_CFG" +fi +yq -y -i ".public_base_url = \"https://${MAS_FQDN}\"" "$MAS_CFG" +yq -y -i ".http.listen = \"0.0.0.0:8000\"" "$MAS_CFG" +yq -y -i ".database.uri = \"postgres://${PG_USER_MAS}:${PG_PASSWORD_MAS}@matrix-db:5432/auth?sslmode=prefer\"" "$MAS_CFG" + fix_ownership "${VOLUME_PATH}/mas" + log_info "Configuration complete" diff --git a/init/scripts/config_link.sh b/init/scripts/config_link.sh index 3464574..f19f5db 100755 --- a/init/scripts/config_link.sh +++ b/init/scripts/config_link.sh @@ -22,13 +22,32 @@ TEMP_FILTER=$(mktemp) trap "rm -f $TEMP_FILTER" EXIT cat > "$TEMP_FILTER" </dev/null || echo "") -CLIENT_ID=$(yq '.oidc.providers[0].client_id' "$MAS_CFG" | tr -d '"') -CLIENT_SECRET=$(yq '.oidc.providers[0].client_secret' "$MAS_CFG" | tr -d '"') +if [[ -z "$EXISTING_CLIENT" ]]; then + # Append new client to MAS config + yq -y -i "$(cat "$TEMP_FILTER")" "$MAS_CFG" || { log_error "Failed to register Synapse client in MAS"; exit 1; } + log_info " -> Registered Synapse as OIDC client in MAS" +else + log_info " -> Synapse client already registered in MAS, skipping" +fi ISSUER_URL="https://${MAS_FQDN}" -- 2.52.0 From 2fb51c7d28f1ca250bf90a4359dcdf9429b9a587 Mon Sep 17 00:00:00 2001 From: cortex Date: Thu, 21 May 2026 07:20:11 +0100 Subject: [PATCH 4/9] combined config generation and linking to one script --- init/scripts/config_gen.sh | 78 ++++++++++++++++++++++++++++++++++--- init/scripts/config_link.sh | 69 -------------------------------- 2 files changed, 72 insertions(+), 75 deletions(-) delete mode 100755 init/scripts/config_link.sh diff --git a/init/scripts/config_gen.sh b/init/scripts/config_gen.sh index a5a3c3b..8a656f0 100755 --- a/init/scripts/config_gen.sh +++ b/init/scripts/config_gen.sh @@ -6,17 +6,25 @@ source "${SCRIPT_DIR}/functions.sh" resolve_paths require_env +require_command "yq" +require_command "openssl" require_docker + +# ============================================================================ +# PHASE 1: Infrastructure Setup +# ============================================================================ + resolve_volume_path mkdir -p "${VOLUME_PATH}/synapse" "${VOLUME_PATH}/mas" "${VOLUME_PATH}/postgres" "${VOLUME_PATH}/proxy" if [[ "${WORKER_COUNT}" -gt 0 ]]; then -mkdir -p "${VOLUME_PATH}/webapp" + mkdir -p "${VOLUME_PATH}/webapp" fi + log_info "Initializing PostgreSQL..." docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" up -d --wait matrix-db -# Wait for database readiness (silent except on failure) +# Wait for database readiness for ATTEMPT in $(seq 1 60); do if docker compose -f "$COMPOSE_FILE" exec -T matrix-db psql -U "$PG_USER" -c "SELECT 1;" > /dev/null 2>&1; then break @@ -25,7 +33,7 @@ for ATTEMPT in $(seq 1 60); do sleep 2 done -# Database initialization SQL +# Database initialization INIT_SQL=$(cat < /dev/null +# ============================================================================ +# PHASE 2: Configuration Generation +# ============================================================================ + log_info "Generating Synapse configuration..." docker run -it --rm --network matrix_network \ -v "${VOLUME_PATH}/synapse:/data" \ @@ -67,14 +79,12 @@ yq -y -i ".server_name = \"${HOMESERVER_FQDN}\"" "$SYNAPSE_CFG" fix_ownership "${VOLUME_PATH}/synapse" log_info "Generating MAS configuration..." - docker run -it --rm --network matrix_network \ -v "${VOLUME_PATH}/mas:/data" \ ghcr.io/element-hq/matrix-authentication-service:latest config generate \ | grep -v '^[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}' > "${VOLUME_PATH}/mas/config.yaml" || { log_error "Mas config generation failed"; exit 1; } MAS_CFG="${VOLUME_PATH}/mas/config.yaml" -# Ensure 'clients' array exists (prevents errors if we try to append later) if ! yq '.clients' "$MAS_CFG" > /dev/null 2>&1; then yq -y -i '.clients = []' "$MAS_CFG" fi @@ -84,4 +94,60 @@ yq -y -i ".database.uri = \"postgres://${PG_USER_MAS}:${PG_PASSWORD_MAS}@matrix- fix_ownership "${VOLUME_PATH}/mas" -log_info "Configuration complete" +# ============================================================================ +# PHASE 3: OIDC Linking +# ============================================================================ + +log_info "Linking Synapse and MAS via OIDC..." + +CLIENT_ID="synapse-client" +CLIENT_SECRET=$(openssl rand -hex 32) + +TEMP_FILTER=$(mktemp) +trap "rm -f $TEMP_FILTER" EXIT + +cat > "$TEMP_FILTER" </dev/null || echo "") + +if [[ -z "$EXISTING_CLIENT" ]]; then + yq -y -i "$(cat "$TEMP_FILTER")" "$MAS_CFG" || { log_error "Failed to register Synapse client in MAS"; exit 1; } + log_info " -> Registered Synapse as OIDC client in MAS" +else + log_info " -> Synapse client already registered in MAS, skipping" +fi + +ISSUER_URL="https://${MAS_FQDN}" + +fix_ownership "$SYNAPSE_CFG" + +cat > "$TEMP_FILTER" </dev/null | grep -q "idp_id"; then + yq -y -i "$(cat "$TEMP_FILTER")" "$SYNAPSE_CFG" || { log_error "Failed to inject OIDC config"; exit 1; } +fi + +if [[ "${INCLUDE_LIVEKIT:-FALSE}" == "TRUE" ]]; then + JWT_URL="https://${JWT_FQDN:-jwt.${DOMAIN}}" + yq -y -i ".livekit.url = \"$JWT_URL\"" "$SYNAPSE_CFG" +fi + +log_info "Config generation complete" diff --git a/init/scripts/config_link.sh b/init/scripts/config_link.sh deleted file mode 100755 index f19f5db..0000000 --- a/init/scripts/config_link.sh +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -source "${SCRIPT_DIR}/functions.sh" -resolve_paths - -require_env -require_command "yq" -require_command "openssl" - -SYNAPSE_CFG="${VOLUME_PATH}/synapse/homeserver.yaml" -MAS_CFG="${VOLUME_PATH}/mas/config.yaml" - -require_file "$SYNAPSE_CFG" "Synapse config" -require_file "$MAS_CFG" "MAS config" - -CLIENT_ID="synapse-client" -CLIENT_SECRET=$(openssl rand -hex 32) - -TEMP_FILTER=$(mktemp) -trap "rm -f $TEMP_FILTER" EXIT - -cat > "$TEMP_FILTER" </dev/null || echo "") - -if [[ -z "$EXISTING_CLIENT" ]]; then - # Append new client to MAS config - yq -y -i "$(cat "$TEMP_FILTER")" "$MAS_CFG" || { log_error "Failed to register Synapse client in MAS"; exit 1; } - log_info " -> Registered Synapse as OIDC client in MAS" -else - log_info " -> Synapse client already registered in MAS, skipping" -fi - -ISSUER_URL="https://${MAS_FQDN}" - -fix_ownership "$SYNAPSE_CFG" - -cat > "$TEMP_FILTER" </dev/null | grep -q "idp_id"; then - yq -y -i "$(cat "$TEMP_FILTER")" "$SYNAPSE_CFG" || { log_error "Failed to inject OIDC config"; exit 1; } -fi - -if [[ "${INCLUDE_LIVEKIT}" == "TRUE" ]]; then - JWT_URL="https://${JWT_FQDN:-jwt.${DOMAIN}}" - yq -y -i ".livekit.url = \"$JWT_URL\"" "$SYNAPSE_CFG" -fi - -log_info "OIDC and LiveKit linking complete" -- 2.52.0 From 099ae71dfd0f28ef577ac1e4536ad452af2b26c1 Mon Sep 17 00:00:00 2001 From: cortex Date: Thu, 21 May 2026 07:50:21 +0100 Subject: [PATCH 5/9] project rename and readme update --- README.md | 306 ++++++++++++++++++------------------------------------ 1 file changed, 102 insertions(+), 204 deletions(-) diff --git a/README.md b/README.md index c26da98..fc2d5a5 100644 --- a/README.md +++ b/README.md @@ -1,212 +1,110 @@ -# AMMIDS -**Automated Modular Matrix Infrastructure Deployment Script** +# mimic -> **Status:** Beta / Experimental -> **Target OS:** Linux (Ubuntu/Debian recommended) -> **License:** AGPL-3.0 -> **Maintainer:** Cortex@Fossgate -> **Source:** [https://git.fossgate.uk/main/ammids](https://git.fossgate.uk/main/ammids) +**Modular Infrastructure for Matrix Integration & Configuration** -AMMIDS is a modular, automated deployment suite designed to spin up a production-ready, upstreamable **Matrix** infrastructure on a fresh VPS. It orchestrates **Synapse**, **Matrix Authentication Service (MAS)**, **PostgreSQL**, and optional components like **LiveKit** and various web clients, all behind a reverse proxy. +mimic is an interactive deployment wizard for self-hosting a Matrix homeserver stack. It automates the provisioning and configuration of Synapse, the Matrix Authentication Service (MAS), PostgreSQL, reverse proxying, optional web clients, and Synapse workers — all driven from a single guided setup session. -## ⚠️ Important Prerequisites +### What it deploys -This script is **tested exclusively on Linux** (specifically Ubuntu 22.04+ and Debian 11+). It relies on GNU-specific utilities (e.g., `sed -i`, `stat -c`) and will **not work** on macOS, BSD, or Windows without significant modification. +- **Synapse** — the Matrix homeserver, with optional generic workers for horizontal scaling +- **MAS** — Matrix Authentication Service, linked to Synapse via OIDC +- **PostgreSQL** — dedicated databases and roles for Synapse and MAS +- **Reverse proxy** — Traefik (automatic TLS) or Nginx (behind an existing proxy) +- **Web client** — Element, Cinny, or FluffyChat (optional) +- **LiveKit** — real-time communication stack (optional) -### Required Dependencies +### How it works -Before running the setup, ensure the following are installed on your VPS: - -| Dependency | Purpose | Install Command (Debian/Ubuntu) | -| :--- | :--- | :--- | -| **Docker & Docker Compose** | Container orchestration | | -| **yq** | YAML processing (Critical) | | -| **OpenSSL** | Secure password generation | (Usually pre-installed) | -| **Git** | Repository cloning | | -| **Sudo** | Permission management | | - -> **Note:** If `yq` is missing, the stack initialization will fail immediately. Ensure it is in your `$PATH` and executable. - -## 🏗️ Architecture Overview - -AMMIDS generates a modular Docker Compose stack with the following components: - -* **Core Services:** - * **Synapse:** The Matrix homeserver (supports generic workers for scaling). - * **MAS (Matrix Auth Service):** Handles OIDC authentication and user sessions. - * **PostgreSQL:** Centralized database for Synapse and MAS. - * **Redis:** Required for Synapse replication and worker communication. -* **Proxy Layer:** - * **Traefik** (Default) or **nginx**: Manages SSL termination and routing. -* **Optional Modules:** - * **LiveKit:** For voice/video call support. - * **Web Clients:** Element, Cinny, or FluffyChat. - * **Workers:** Scalable Synapse workers (Federation Senders, Media Repos, etc.). - -## 🚀 Quick Start - -### 1. DNS Configuration -Before running the installer, ensure your DNS records point to your VPS IP address. You will need the following records: - -| Record Type | Host/Subdomain | Purpose | -| :--- | :--- | :--- | -| `A` | `matrix` | Matrix Homeserver (Synapse) | -| `A` | `auth` | Authentication Service (MAS) | -| `A` | `app` | Web Client (Element/Cinny/FluffyChat) | -| `A` | `rtc` | LiveKit (if enabled) | -| `A` | `jwt` | LiveKit JWT Bridge (if enabled) | - -*These subdomains can be adjusted in the generated .env file.* - -### 2. Clone and Execute -Clone the repository and navigate to the project root: - - - -Run the main orchestrator script: - - - -**What happens during setup:** -1. **Environment Generation:** Prompts for your domain, database credentials, and installation path. Creates `.env`. -2. **Directory Prep:** Creates the data volume directory and fixes ownership. -3. **Stack Initialization:** - * Starts the database and waits for readiness. - * Runs database migration scripts. - * Generates Synapse and MAS configuration files. - * Links Synapse and MAS via Native OIDC. -4. **Compose Generation:** Creates the final `compose.yaml` with your selected modules. - -### 3. Launch the Stack -Once the script completes, start the services: - - - -Verify the status: - - - -## 📂 Project Structure - - - -## ⚙️ Configuration & Customization - -### Environment Variables (`.env`) -The `set_env.sh` script generates a secure `.env` file. Key variables include: -* `DOMAIN`: Your base domain (e.g., `example.org`). -* `HOMESERVER_FQDN`: Your Matrix server address. -* `VOLUME_PATH`: Where data is stored (defaults to `./matrix`). -* `PG_*`: Database credentials (auto-generated if left blank). -* `LIVEKIT_API_KEY`: (Optional) Enable voice/video calls. - -### Adding Workers -To scale your Synapse instance, use the worker generator: - - - -This adds 2 generic workers and updates `compose.yaml`. - -### Changing Web Clients -Re-run `./init/scripts/compose_gen.sh` to toggle between Element, Cinny, FluffyChat, or disable web clients entirely. - -### OIDC Integration -The `config_link.sh` script automatically configures **Native OIDC** between Synapse and MAS. It: -1. Generates a Client ID/Secret in MAS. -2. Injects the OIDC provider configuration into Synapse. -3. Optionally configures **LiveKit** if enabled in `.env`. - -## 🛡️ Proxy & TLS Selection - -During setup, you will be asked to choose a reverse proxy. This choice dictates how TLS/SSL is handled: - -* **Traefik (Default)**: - * **Use this if**: You want the stack to manage SSL certificates automatically via Let's Encrypt. - * **Behavior**: Traefik listens on ports 80/443, handles ACME challenges, and serves HTTPS traffic directly. - * **Requirement**: Ports 80 and 443 must be open to the public internet. - -* **Nginx**: - * **Use this if**: You already have a reverse proxy (e.g., Cloudflare, Caddy, or an existing Nginx instance) handling TLS termination. - * **Behavior**: Nginx in this stack acts purely as an internal router. It does **not** request certificates. - * **Requirement**: Your external proxy must forward traffic to the Nginx container (or the internal services) over HTTP. - -## 🐛 Troubleshooting - -### "Command not found: yq" -The script relies on `yq` for YAML manipulation. Install it manually: - - - -### "compose.yaml not found" -If the script reports success but `compose.yaml` is missing: -1. Check if you are running the script from the correct directory. -2. Ensure `init/templates/compose.yaml.template` exists. -3. Check `/var/log/syslog` or `dmesg` for permission errors on the root directory. - -### Database Not Ready -If the stack fails to start with a database timeout: - - - -Ensure your VPS has sufficient RAM (minimum 2GB recommended for Synapse + Postgres). - -### OIDC Linking Failed -If `config_link.sh` fails: -1. Verify `yq` is installed and functional. -2. Check that `mas/config.yaml` and `synapse/homeserver.yaml` were generated successfully. -3. Ensure `MAS_FQDN` is correctly set in `.env`. - -## 🤝 Contributing - -Contributions are welcome! Please open an issue on the Gitea repository before submitting large changes. - -* **Report Bugs:** [Issues Page](https://git.fossgate.uk/main/ammids/issues) -* **Feature Requests:** [Discussions](https://git.fossgate.uk/main/ammids/discussions) - -## 📜 License - -This project is licensed under the **GNU Affero General Public License v3.0 (AGPL-3.0)**. - -See the [LICENSE](LICENSE) file for details. +The `setup.sh` wizard collects your preferences, writes a `.env`, then orchestrates four stages: compose file generation (`compose_gen.sh`), database and config initialisation (`config_gen.sh`) and worker generation (`worker_gen.sh`). Everything is template-driven — no hand-editing of YAML required. --- -*AMMIDS is provided "as is", without warranty of any kind. Use at your own risk.* \ No newline at end of file + +## Dependencies + +mimic targets **Linux** and has only been tested on **Ubuntu 24.04**. + +| Dependency | Purpose | +|---|---| +| Docker | Container runtime | +| Docker Compose (v2 plugin) | Container orchestration | +| yq | YAML processor (Mike Farah's Go-based `yq`) | +| openssl | Secret / password generation | + + ```bash + # Docker & Docker Compose plugin + sudo apt update && sudo apt install -y docker.io docker-compose-plugin + + # yq — Mike Farah's Go-based version (NOT the Python yq) + sudo wget https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 -O /usr/bin/yq + sudo chmod +x /usr/bin/yq + + # openssl (usually pre-installed on Ubuntu 24.04) + sudo apt install -y openssl + + # Add your user to the docker group (log out/in afterwards) + sudo usermod -aG docker "$USER" + ``` + +--- + +## Installation + +### 1. Configure DNS + +Before running mimic, create **A records** pointing to your server's public IP for every subdomain you plan to use: + +| Subdomain | Required? | Purpose | +|---|---|---| +| `matrix.` | Always | Synapse homeserver | +| `auth.` | Always | Matrix Authentication Service | +| `app.` | If a web client is selected | Element / Cinny / FluffyChat | +| `rtc.` | If LiveKit is enabled | LiveKit media server | +| `jwt.` | If LiveKit is enabled | LiveKit JWT token issuer | + + Example for `example.org` with server IP `203.0.113.42`: + _Subdomains shown are defaults; custom values can be configured in the wizard._ + ``` + matrix.example.org IN A 203.0.113.42 + auth.example.org IN A 203.0.113.42 + app.example.org IN A 203.0.113.42 + rtc.example.org IN A 203.0.113.42 + jwt.example.org IN A 203.0.113.42 + ``` + +If using Traefik, ensure ports **80** and **443** are open — Traefik uses HTTP-01 ACME challenges for automatic TLS certificate provisioning. + +### 2. Clone the repository + + ```bash + git clone https://git.fossgate.uk/main/mimic.git + cd mimic + chmod +x setup.sh + ``` + +### 3. Run the setup wizard + + ```bash + ./setup.sh + ``` + + The wizard walks you through: + + 1. **Base domain** — e.g. `example.org` + 2. **Service toggles** — LiveKit on/off, web client choice, proxy choice + 3. **Worker count** — number of Synapse generic workers (0 = single-process) + 4. **Credentials** — auto-generate secure passwords, or enter them manually + + On completion, `.env` and `compose.yaml` are written to the project root and all service configs are placed under the volume path (default `./matrix/`). + +### 4. Launch + +```bash +docker compose up -d +``` + +Verify the stack is healthy: +```bash +docker compose ps +``` + + -- 2.52.0 From 047d2a314ab841b127c23926ec2af07af57ad593 Mon Sep 17 00:00:00 2001 From: cortex <1+cortex@noreply.localhost> Date: Thu, 21 May 2026 06:51:43 +0000 Subject: [PATCH 6/9] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fc2d5a5..a3717f2 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ mimic is an interactive deployment wizard for self-hosting a Matrix homeserver s ### How it works -The `setup.sh` wizard collects your preferences, writes a `.env`, then orchestrates four stages: compose file generation (`compose_gen.sh`), database and config initialisation (`config_gen.sh`) and worker generation (`worker_gen.sh`). Everything is template-driven — no hand-editing of YAML required. +The `setup.sh` wizard collects your preferences, writes a `.env`, then orchestrates three stages: compose file generation (`compose_gen.sh`), database and config initialisation (`config_gen.sh`) and worker generation (`worker_gen.sh`). Everything is template-driven — no hand-editing of YAML required. --- -- 2.52.0 From bce505372aa6f8a25eca0ed0183a66c15e23091c Mon Sep 17 00:00:00 2001 From: cortex <1+cortex@noreply.localhost> Date: Thu, 21 May 2026 06:53:08 +0000 Subject: [PATCH 7/9] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index a3717f2..8f1fde7 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,7 @@ Before running mimic, create **A records** pointing to your server's public IP f | Subdomain | Required? | Purpose | |---|---|---| +| `` | Always | Base Url | | `matrix.` | Always | Synapse homeserver | | `auth.` | Always | Matrix Authentication Service | | `app.` | If a web client is selected | Element / Cinny / FluffyChat | -- 2.52.0 From 0cf16f40ddad635e8e505c3f7e27bdff4752faa1 Mon Sep 17 00:00:00 2001 From: cortex <1+cortex@noreply.localhost> Date: Thu, 21 May 2026 07:04:47 +0000 Subject: [PATCH 8/9] Update README.md --- README.md | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 8f1fde7..8272819 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,12 @@ -# mimic - +# MIMIC **Modular Infrastructure for Matrix Integration & Configuration** +> **Status:** Beta / Experimental +> **Target OS:** Linux (Ubuntu/Debian recommended) +> **License:** AGPL-3.0 +> **Maintainer:** Cortex@Fossgate +> **Source:** [https://git.fossgate.uk/main/mimic](https://git.fossgate.uk/main/mimic) + mimic is an interactive deployment wizard for self-hosting a Matrix homeserver stack. It automates the provisioning and configuration of Synapse, the Matrix Authentication Service (MAS), PostgreSQL, reverse proxying, optional web clients, and Synapse workers — all driven from a single guided setup session. ### What it deploys @@ -49,10 +54,12 @@ mimic targets **Linux** and has only been tested on **Ubuntu 24.04**. ## Installation -### 1. Configure DNS +### 1. Configure DNS and Firewall Before running mimic, create **A records** pointing to your server's public IP for every subdomain you plan to use: + _Subdomains shown are defaults, custom values can be configured in the wizard._ + | Subdomain | Required? | Purpose | |---|---|---| | `` | Always | Base Url | @@ -63,8 +70,8 @@ Before running mimic, create **A records** pointing to your server's public IP f | `jwt.` | If LiveKit is enabled | LiveKit JWT token issuer | Example for `example.org` with server IP `203.0.113.42`: - _Subdomains shown are defaults; custom values can be configured in the wizard._ ``` + example.org IN A 203.0.113.42 matrix.example.org IN A 203.0.113.42 auth.example.org IN A 203.0.113.42 app.example.org IN A 203.0.113.42 @@ -72,6 +79,14 @@ Before running mimic, create **A records** pointing to your server's public IP f jwt.example.org IN A 203.0.113.42 ``` +### Firewall Requirements +| Port(s) | Protocol | Required For | +|---|---|---| +| 80 | TCP | Traefik ACME challenges | +| 443 | TCP | HTTPS traffic | +| 10000–20000 | UDP | LiveKit media (if enabled) | + +Adjust the UDP range via `LIVEKIT_UDP_RANGE` in `.env` if needed. If using Traefik, ensure ports **80** and **443** are open — Traefik uses HTTP-01 ACME challenges for automatic TLS certificate provisioning. ### 2. Clone the repository -- 2.52.0 From d32f1de72071344acf060929819ce462fbbd1bb3 Mon Sep 17 00:00:00 2001 From: cortex <1+cortex@noreply.localhost> Date: Thu, 21 May 2026 07:06:21 +0000 Subject: [PATCH 9/9] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8272819..dfa4f7e 100644 --- a/README.md +++ b/README.md @@ -56,9 +56,9 @@ mimic targets **Linux** and has only been tested on **Ubuntu 24.04**. ### 1. Configure DNS and Firewall -Before running mimic, create **A records** pointing to your server's public IP for every subdomain you plan to use: +Before running mimic, create **A records** pointing to your server's public IP for every subdomain you plan to use: - _Subdomains shown are defaults, custom values can be configured in the wizard._ + _Subdomains shown are defaults, custom values can be configured in the wizard._ | Subdomain | Required? | Purpose | |---|---|---| -- 2.52.0