Compare commits

..

No commits in common. "ec98a83b48e126f04dc2abc8b409b17803f93600" and "c7df07ffc24f38521d8ca3b414b48c53b759b62a" have entirely different histories.

11 changed files with 112 additions and 376 deletions

View File

@ -71,4 +71,4 @@ CFG_GITEA_PORT_2="gitea-service|ssh|random:22|private|tcp|false|false|false|Git
CFG_GITEA_AUTH_PROFILE=multi_user CFG_GITEA_AUTH_PROFILE=multi_user
CFG_GITEA_ADMIN_USER= CFG_GITEA_ADMIN_USER=
CFG_GITEA_ADMIN_EMAIL= CFG_GITEA_ADMIN_EMAIL=
CFG_GITEA_ADMIN_PASSWORD_1=RANDOMIZEDPASSWORD2 CFG_GITEA_ADMIN_PASSWORD_1=RANDOMIZEDPASSWORD1

View File

@ -69,4 +69,4 @@ CFG_INVIDIOUS_PORT_1="invidious-service|webui|random:3000|public|tcp|false|true|
CFG_INVIDIOUS_AUTH_PROFILE=multi_user CFG_INVIDIOUS_AUTH_PROFILE=multi_user
CFG_INVIDIOUS_ADMIN_USER= CFG_INVIDIOUS_ADMIN_USER=
CFG_INVIDIOUS_ADMIN_EMAIL= CFG_INVIDIOUS_ADMIN_EMAIL=
CFG_INVIDIOUS_ADMIN_PASSWORD_1=RANDOMIZEDPASSWORD2 CFG_INVIDIOUS_ADMIN_PASSWORD_1=RANDOMIZEDPASSWORD1

View File

@ -24,15 +24,6 @@ services:
environment: environment:
- TZ=TIMEZONE_DATA #LIBREPORTAL|TIMEZONE_TAG|TIMEZONE_DATA - TZ=TIMEZONE_DATA #LIBREPORTAL|TIMEZONE_TAG|TIMEZONE_DATA
- SYNAPSE_CONFIG_PATH=/data/homeserver.yaml - SYNAPSE_CONFIG_PATH=/data/homeserver.yaml
# These are not redundant with `user:` above. The image's entrypoint
# checks whether it is root and, if so, su-execs down to UID:GID —
# which default to 991:991. So a container started as 0:0 still ends
# up running as 991, and 991 maps to a host sub-UID that does not own
# the bind mount: Synapse then dies reading its own chmod-600
# homeserver.yaml. Pinning them to the same identity as USER_TAG
# stops the entrypoint dropping to a user that cannot read anything.
- UID=MATRIX_RUN_UID_DATA #LIBREPORTAL|MATRIX_RUN_UID_TAG|MATRIX_RUN_UID_DATA
- GID=MATRIX_RUN_GID_DATA #LIBREPORTAL|MATRIX_RUN_GID_TAG|MATRIX_RUN_GID_DATA
depends_on: depends_on:
- matrix-postgres - matrix-postgres
labels: labels:

View File

@ -13,15 +13,14 @@
# ENABLE_REGISTRATION = if true, anyone who can reach the homeserver can create an account on it # ENABLE_REGISTRATION = if true, anyone who can reach the homeserver can create an account on it
# ADMIN_USERNAME = localpart of the first admin account created at install (the full ID becomes @<name>:<server_name>) # ADMIN_USERNAME = localpart of the first admin account created at install (the full ID becomes @<name>:<server_name>)
# ADMIN_PASSWORD = password for that first admin account # ADMIN_PASSWORD = password for that first admin account
# SERVER_NAME = permanent identity of this homeserver — the half of a user ID after the colon
# MONITORING = if true, export this app's metrics to Prometheus + Grafana (needs both apps installed) # MONITORING = if true, export this app's metrics to Prometheus + Grafana (needs both apps installed)
# #
CFG_MATRIX_APP_NAME=matrix CFG_MATRIX_APP_NAME=matrix
# No prerequisites. Synapse runs perfectly well on a plain HTTP listener reached # A domain and Traefik are hard requirements, not conveniences. Synapse bakes
# by IP over the LAN or a WireGuard tunnel — that is the same setup as sitting # server_name into every event and user ID it has ever signed, and it CANNOT be
# behind a reverse proxy, minus the proxy. Only *federation* needs a real domain # changed afterwards without throwing the database away — so the homeserver must
# with public DNS and TLS, and federation is optional. # know its real public name at install time, over real TLS.
CFG_MATRIX_REQUIRES="" CFG_MATRIX_REQUIRES="domain,traefik"
CFG_MATRIX_BACKUP=true CFG_MATRIX_BACKUP=true
CFG_MATRIX_BACKUP_STRATEGY=auto CFG_MATRIX_BACKUP_STRATEGY=auto
CFG_MATRIX_UPDATE_TYPE=auto CFG_MATRIX_UPDATE_TYPE=auto
@ -38,21 +37,6 @@ CFG_MATRIX_HEADSCALE=false
CFG_MATRIX_ENABLE_REGISTRATION=false CFG_MATRIX_ENABLE_REGISTRATION=false
CFG_MATRIX_ADMIN_USERNAME=admin CFG_MATRIX_ADMIN_USERNAME=admin
CFG_MATRIX_ADMIN_PASSWORD_1=RANDOMIZEDPASSWORD1 CFG_MATRIX_ADMIN_PASSWORD_1=RANDOMIZEDPASSWORD1
# Set this if you ever intend to federate — it is the single most consequential
# value here and the ONLY one that cannot be changed later. It is signed into
# every event this server sends and forms the half of a user ID after the colon
# (@alice:example.com), so changing it orphans the database.
#
# Crucially it is independent of how clients reach the server: you can set it to
# a domain you own that has no DNS pointing anywhere yet, run today on
# http://<lan-ip>:<port>, and switch federation on later by adding DNS, Traefik
# and a certificate — with no rebuild and no lost history.
#
# Left empty it is derived: <subdomain>.<domain> when a domain is configured,
# otherwise this machine's LAN address. The LAN address works fine for local and
# WireGuard use but can never federate, and every user ID breaks if the IP
# changes — so if in doubt, put a domain here even if you do not use it yet.
CFG_MATRIX_SERVER_NAME=
CFG_MATRIX_MONITORING=false CFG_MATRIX_MONITORING=false
# Postgres password for the `synapse` role, fed to the compose via # Postgres password for the `synapse` role, fed to the compose via
# #LIBREPORTAL|MATRIX_DB_PASSWORD_1_TAG| and written into homeserver.yaml by the # #LIBREPORTAL|MATRIX_DB_PASSWORD_1_TAG| and written into homeserver.yaml by the
@ -78,6 +62,7 @@ CFG_MATRIX_DESCRIPTION="Federated Chat"
CFG_MATRIX_LONG_DESCRIPTION="The open federated chat protocol — rooms, end-to-end encryption and bridges to Discord and Slack. Installs Synapse plus the Element web client" CFG_MATRIX_LONG_DESCRIPTION="The open federated chat protocol — rooms, end-to-end encryption and bridges to Discord and Slack. Installs Synapse plus the Element web client"
CFG_MATRIX_URL="https://github.com/element-hq/synapse" CFG_MATRIX_URL="https://github.com/element-hq/synapse"
CFG_MATRIX_ACTIONS="configure|install|restart|shutdown|uninstall" CFG_MATRIX_ACTIONS="configure|install|restart|shutdown|uninstall"
CFG_MATRIX_REQUIRES_SERVICE=traefik
# #
# ============================================================================= # =============================================================================
# NETWORK CONFIGURATION # NETWORK CONFIGURATION

View File

@ -1,8 +1,8 @@
{ {
"default_server_config": { "default_server_config": {
"m.homeserver": { "m.homeserver": {
"base_url": "ELEMENT_BASEURL_PLACEHOLDER", "base_url": "https://ELEMENT_HOMESERVER_PLACEHOLDER",
"server_name": "ELEMENT_SERVERNAME_PLACEHOLDER" "server_name": "ELEMENT_HOMESERVER_PLACEHOLDER"
} }
}, },
"brand": "Element", "brand": "Element",
@ -13,7 +13,7 @@
"default_country_code": "GB", "default_country_code": "GB",
"show_labs_settings": true, "show_labs_settings": true,
"room_directory": { "room_directory": {
"servers": ["ELEMENT_SERVERNAME_PLACEHOLDER"] "servers": ["ELEMENT_HOMESERVER_PLACEHOLDER", "matrix.org"]
}, },
"setting_defaults": { "setting_defaults": {
"breadcrumbs": true "breadcrumbs": true

View File

@ -10,20 +10,15 @@
# sent and forms the second half of every user ID (@alice:<server_name>). # sent and forms the second half of every user ID (@alice:<server_name>).
# Changing it later does not migrate anything — it orphans the whole database. # Changing it later does not migrate anything — it orphans the whole database.
server_name: "SYNAPSE_SERVER_NAME_PLACEHOLDER" server_name: "SYNAPSE_SERVER_NAME_PLACEHOLDER"
public_baseurl: "https://SYNAPSE_SERVER_NAME_PLACEHOLDER/"
# Where clients actually reach this server. Independent of server_name: on a
# LAN or WireGuard-only install this is http://<ip>:<port> while server_name
# stays whatever permanent identity you chose. Adding TLS later means changing
# only this line.
public_baseurl: "SYNAPSE_PUBLIC_BASEURL_PLACEHOLDER"
pid_file: /data/homeserver.pid pid_file: /data/homeserver.pid
# Serve /.well-known/matrix/server ourselves, advertising port 443. Federation # Serve /.well-known/matrix/server ourselves, advertising port 443. Federation
# otherwise defaults to port 8448 on server_name, which Traefik is not # otherwise defaults to port 8448 on server_name, which Traefik is not
# listening on. Only switched on for a real HTTPS deployment — advertising # listening on. Because server_name is the same host Traefik already terminates
# <server_name>:443 from an install that is only reachable at http://<ip>:<port> # TLS for, this is all the delegation that is needed — nothing has to be
# would just invite other servers to connect somewhere that cannot answer. # published at the apex domain.
serve_server_wellknown: SYNAPSE_SERVE_WELLKNOWN_PLACEHOLDER serve_server_wellknown: true
listeners: listeners:
# Port 8008 is the container-internal port and is deliberately hardcoded: it # Port 8008 is the container-internal port and is deliberately hardcoded: it

View File

@ -1,27 +0,0 @@
#!/bin/bash
# App-specific compose tags for Matrix.
#
# The Synapse image drops privileges from root to its UID/GID environment
# variables (991:991 by default) before starting. That has to land on the same
# identity USER_TAG resolves to, or the process ends up as a host sub-UID that
# owns none of the bind-mounted data and cannot even read homeserver.yaml.
#
# The rule is deliberately the same one docker_config_setup_data.sh applies to
# USER_TAG: container-root under rootless (which IS the install user on the
# host), the real uid:gid under rooted. Kept here rather than parsing USER_DATA
# back out of the compose because the ordering between the two substitutions is
# not guaranteed.
appSetupComposeTags_matrix() {
local full_file_path="$1"
local run_uid=0 run_gid=0
if [[ "$CFG_DOCKER_INSTALL_TYPE" != "rootless" ]]; then
local container_user="${docker_install_user:-$sudo_user_name}"
run_uid=$(id -u "$container_user" 2>/dev/null) || run_uid=0
run_gid=$(id -g "$container_user" 2>/dev/null) || run_gid=0
fi
tagsManagerUpdateUniversalTag "$full_file_path" "MATRIX_RUN_UID_TAG" "$run_uid"
tagsManagerUpdateUniversalTag "$full_file_path" "MATRIX_RUN_GID_TAG" "$run_gid"
}

View File

@ -15,14 +15,6 @@ matrix_install_pre()
matrix=n matrix=n
return 1 return 1
fi fi
# Not a prerequisite, just the one warning worth making loudly. Everything
# else about this install can be changed later; server_name cannot.
if [[ -z "$CFG_MATRIX_SERVER_NAME" && -z "$domain_full" ]]; then
isNotice "No domain configured — this homeserver will identify itself as '${public_ip_v4:-localhost}'."
isNotice "That works for LAN and WireGuard clients but can never federate, and every"
isNotice "user ID breaks if this machine's IP changes. To keep the door open, set"
isNotice "CFG_MATRIX_SERVER_NAME to a domain you own — it needs no DNS today."
fi
} }
# The homeserver's public host — the value that becomes server_name. # The homeserver's public host — the value that becomes server_name.
@ -38,57 +30,15 @@ matrix_install_pre()
# here and the Traefik rule generated later cannot drift apart. # here and the Traefik rule generated later cannot drift apart.
_matrixServerName() _matrixServerName()
{ {
# An explicit choice always wins — this is the escape hatch that lets a local sub="${port_subdomains[0]}"
# LAN-only install still be given a federatable identity up front. [[ -z "$domain_full" ]] && return 1
if [[ -n "$CFG_MATRIX_SERVER_NAME" ]]; then if [[ "$sub" == "@" || "$sub" == "root" ]]; then
echo "$CFG_MATRIX_SERVER_NAME" echo "$domain_full"
return 0 elif [[ -n "$sub" ]]; then
echo "${sub}.${domain_full}"
else
echo "matrix.${domain_full}"
fi fi
if [[ -n "$domain_full" ]]; then
local sub="${port_subdomains[0]}"
if [[ "$sub" == "@" || "$sub" == "root" ]]; then
echo "$domain_full"
elif [[ -n "$sub" ]]; then
echo "${sub}.${domain_full}"
else
echo "matrix.${domain_full}"
fi
return 0
fi
# LAN / WireGuard only. Usable, never federatable.
echo "${public_ip_v4:-localhost}"
}
# How clients reach the homeserver, as opposed to what it calls itself.
#
# Depends on the allocated external port, so it is only correct once ports have
# been assigned — i.e. from install_post_start onward, not install_post_compose.
_matrixPublicBaseUrl()
{
local app_name="$1"
local compose="$containers_dir$app_name/docker-compose.yml"
# Real HTTPS only when Traefik is actually installed AND a domain exists;
# otherwise the router in the compose is decorative.
if [[ -d "${containers_dir}traefik" && -n "$domain_full" ]]; then
local host
host=$(tagsManagerGetTagContent "$compose" "DOMAINSUBNAME_TAG_1")
if [[ -n "$host" && "$host" != DOMAINSUBNAME_DATA* ]]; then
echo "https://${host}/"
return 0
fi
fi
local ports external
ports=$(tagsManagerGetTagContent "$compose" "PORTS_TAG_1")
external="${ports%%:*}"
if [[ -n "$external" && "$external" != PORTS_DATA* ]]; then
echo "http://${public_ip_v4:-localhost}:${external}/"
return 0
fi
# Ports not settled yet — the post-start pass will correct this.
echo "http://${public_ip_v4:-localhost}/"
} }
matrix_install_post_compose() matrix_install_post_compose()
@ -105,8 +55,8 @@ matrix_install_post_compose()
local server_name local server_name
server_name=$(_matrixServerName) server_name=$(_matrixServerName)
if [[ -z "$server_name" ]]; then if [[ -z "$server_name" ]]; then
isError "Could not determine a Matrix server_name and could not fall back to this host's address." isError "No domain is configured — cannot determine the Matrix server_name."
isNotice "Set CFG_MATRIX_SERVER_NAME in matrix.config, then reinstall." isNotice "Set CFG_DOMAIN_${CFG_MATRIX_DOMAIN:-1} (General → Network), then reinstall."
return 1 return 1
fi fi
@ -125,37 +75,6 @@ matrix_install_post_compose()
result=$(createFolders "loud" "$docker_install_user" "$data_dir" "$app_dir/element") result=$(createFolders "loud" "$docker_install_user" "$data_dir" "$app_dir/element")
checkSuccess "Creating $app_name data folders" checkSuccess "Creating $app_name data folders"
# Best guess now; the post-start pass recomputes it once the port is known
# and rewrites + restarts if it turns out different.
local base_url serve_wellknown="false"
base_url=$(_matrixPublicBaseUrl "$app_name")
[[ "$base_url" == https://* ]] && serve_wellknown="true"
# Element is a static bundle; config.json is the only thing that makes it
# point at this homeserver rather than matrix.org.
#
# Written before the signing key on purpose. config.json is bind-mounted as
# a FILE, and docker silently creates a directory at a missing mount source
# — so if an earlier step fails and returns, compose-up leaves a directory
# here that every later run then trips over. Doing it first means the file
# always exists; the rm handles a landmine left by an install that failed
# before this reordering.
[[ -d "$app_dir/element/config.json" ]] && runFileOp rm -rf "$app_dir/element/config.json"
result=$(copyResource "$app_name" "element-config.json" "element" | runInstallWrite -a "$logs_dir/$docker_log_file" 2>&1)
checkSuccess "Copying Element configuration to $app_dir/element"
result=$(runFileOp mv "$app_dir/element/element-config.json" "$app_dir/element/config.json")
checkSuccess "Renaming Element configuration to config.json"
# base_url is where the browser sends requests; server_name is only the
# label. On a LAN install these genuinely differ — http://10.0.0.5:8008 vs
# whatever permanent name the server answers to.
runFileOp sed -i "s|ELEMENT_BASEURL_PLACEHOLDER|${base_url%/}|g" "$app_dir/element/config.json"
runFileOp sed -i "s|ELEMENT_SERVERNAME_PLACEHOLDER|$server_name|g" "$app_dir/element/config.json"
runFileOp chown -R "$docker_install_user":"$docker_install_user" "$app_dir/element"
checkSuccess "Pointing Element at ${base_url%/}"
# Synapse signs every federated event with this key, and a peer that has # Synapse signs every federated event with this key, and a peer that has
# seen one key will reject events signed by a different one. So: generate it # seen one key will reject events signed by a different one. So: generate it
# exactly once, and never regenerate it over an existing install. # exactly once, and never regenerate it over an existing install.
@ -169,20 +88,7 @@ matrix_install_post_compose()
synapse_image=$(tagsManagerGetTagContent "$app_dir/docker-compose.yml" "MATRIX_VERSION_TAG") synapse_image=$(tagsManagerGetTagContent "$app_dir/docker-compose.yml" "MATRIX_VERSION_TAG")
synapse_image="matrixdotorg/synapse:${synapse_image:-latest}" synapse_image="matrixdotorg/synapse:${synapse_image:-latest}"
# UID/GID are the image's own knobs for who it writes /data as; left at
# their default of 991 the generated key lands owned by a host sub-UID
# that the install user does not own, and the mv/rm below fail with
# EACCES on files we just created. Pin them to the same identity the
# compose runs the container as (USER_TAG): container-root under
# rootless, the real uid:gid under rooted.
local gen_uid=0 gen_gid=0
if [[ "$CFG_DOCKER_INSTALL_TYPE" != "rootless" ]]; then
gen_uid=$(id -u "${docker_install_user:-$sudo_user_name}" 2>/dev/null || echo 0)
gen_gid=$(id -g "${docker_install_user:-$sudo_user_name}" 2>/dev/null || echo 0)
fi
result=$(runFileOp docker run --rm \ result=$(runFileOp docker run --rm \
-e UID="$gen_uid" -e GID="$gen_gid" \
-e SYNAPSE_SERVER_NAME="$server_name" \ -e SYNAPSE_SERVER_NAME="$server_name" \
-e SYNAPSE_REPORT_STATS=no \ -e SYNAPSE_REPORT_STATS=no \
-v "$data_dir":/data \ -v "$data_dir":/data \
@ -227,8 +133,6 @@ matrix_install_post_compose()
local enable_registration="false" local enable_registration="false"
[[ "$CFG_MATRIX_ENABLE_REGISTRATION" == "true" ]] && enable_registration="true" [[ "$CFG_MATRIX_ENABLE_REGISTRATION" == "true" ]] && enable_registration="true"
runFileOp sed -i "s|SYNAPSE_PUBLIC_BASEURL_PLACEHOLDER|$base_url|g" "$homeserver_file"
runFileOp sed -i "s|SYNAPSE_SERVE_WELLKNOWN_PLACEHOLDER|$serve_wellknown|g" "$homeserver_file"
runFileOp sed -i "s|SYNAPSE_SERVER_NAME_PLACEHOLDER|$server_name|g" "$homeserver_file" runFileOp sed -i "s|SYNAPSE_SERVER_NAME_PLACEHOLDER|$server_name|g" "$homeserver_file"
runFileOp sed -i "s|SYNAPSE_DB_PASSWORD_PLACEHOLDER|$db_password|g" "$homeserver_file" runFileOp sed -i "s|SYNAPSE_DB_PASSWORD_PLACEHOLDER|$db_password|g" "$homeserver_file"
runFileOp sed -i "s|SYNAPSE_REGISTRATION_SECRET_PLACEHOLDER|$registration_secret|g" "$homeserver_file" runFileOp sed -i "s|SYNAPSE_REGISTRATION_SECRET_PLACEHOLDER|$registration_secret|g" "$homeserver_file"
@ -241,32 +145,28 @@ matrix_install_post_compose()
runFileOp chown -R "$docker_install_user":"$docker_install_user" "$data_dir" runFileOp chown -R "$docker_install_user":"$docker_install_user" "$data_dir"
checkSuccess "Restricting permissions on the Synapse secrets" checkSuccess "Restricting permissions on the Synapse secrets"
# Element is a static bundle; config.json is the only thing that makes it
# point at this homeserver rather than matrix.org.
result=$(copyResource "$app_name" "element-config.json" "element" | runInstallWrite -a "$logs_dir/$docker_log_file" 2>&1)
checkSuccess "Copying Element configuration to $app_dir/element"
result=$(runFileOp mv "$app_dir/element/element-config.json" "$app_dir/element/config.json")
checkSuccess "Renaming Element configuration to config.json"
runFileOp sed -i "s|ELEMENT_HOMESERVER_PLACEHOLDER|$server_name|g" "$app_dir/element/config.json"
runFileOp chown -R "$docker_install_user":"$docker_install_user" "$app_dir/element"
checkSuccess "Pointing Element at https://$server_name"
} }
matrix_install_post_start() matrix_install_post_start()
{ {
local app_name="$1" local app_name="$1"
local app_dir="$containers_dir$app_name"
((menu_number++)) ((menu_number++))
echo "" echo ""
echo "---- $menu_number. Creating the first Matrix admin account" echo "---- $menu_number. Creating the first Matrix admin account"
echo "" echo ""
# Ports are only allocated during compose-up, so the base URL written by
# install_post_compose was a guess whenever there is no domain. Now that the
# real external port is known, correct it — and only restart if it actually
# changed, so a domain-backed install pays nothing for this.
local base_url current_base
base_url=$(_matrixPublicBaseUrl "$app_name")
current_base=$(runFileOp grep -oP '^public_baseurl:\s*"\K[^"]*' "$app_dir/data/homeserver.yaml" 2>/dev/null)
if [[ -n "$base_url" && "$base_url" != "$current_base" ]]; then
runFileOp sed -i "s|^public_baseurl: .*|public_baseurl: \"$base_url\"|" "$app_dir/data/homeserver.yaml"
runFileOp sed -i "s|\"base_url\": \".*\"|\"base_url\": \"${base_url%/}\"|" "$app_dir/element/config.json"
isSuccessful "Homeserver URL settled as ${base_url%/} (was ${current_base:-unset})"
dockerComposeRestart "$app_name"
fi
# Synapse runs its database migrations on first boot, which on an empty # Synapse runs its database migrations on first boot, which on an empty
# Postgres takes appreciably longer than the container takes to start. # Postgres takes appreciably longer than the container takes to start.
# /health answers only once it is actually serving. # /health answers only once it is actually serving.
@ -301,20 +201,14 @@ matrix_install_post_start()
# Idempotent in practice: on a reinstall over existing data the account # Idempotent in practice: on a reinstall over existing data the account
# already exists and register_new_matrix_user fails with "User ID already # already exists and register_new_matrix_user fails with "User ID already
# taken", which is not worth failing the install over. # taken", which is not worth failing the install over.
# rc is captured on its own line because the [[ ]] test below overwrites $? local result
# — checkSuccess would then report the *test's* result, which is how a
# perfectly successful registration got logged as an error.
local result rc
result=$(runFileOp docker exec matrix-synapse register_new_matrix_user \ result=$(runFileOp docker exec matrix-synapse register_new_matrix_user \
-u "$admin_user" -p "$admin_pass" -a \ -u "$admin_user" -p "$admin_pass" -a \
-c /data/homeserver.yaml http://localhost:8008 2>&1) -c /data/homeserver.yaml http://localhost:8008 2>&1)
rc=$?
if [[ "$result" == *"already taken"* ]]; then if [[ "$result" == *"already taken"* ]]; then
isNotice "Matrix admin '$admin_user' already exists — leaving the existing account alone." isNotice "Matrix admin '$admin_user' already exists — leaving the existing account alone."
elif (( rc == 0 )); then
isSuccessful "Created Matrix admin account '$admin_user'"
else else
isError "Creating Matrix admin account '$admin_user' failed: $result" checkSuccess "Creating Matrix admin account '$admin_user'"
fi fi
} }
@ -332,18 +226,10 @@ matrix_install_post()
echo " Your user ID : @${admin_user}:${server_name}" echo " Your user ID : @${admin_user}:${server_name}"
echo " Password : ${CFG_MATRIX_ADMIN_PASSWORD_1}" echo " Password : ${CFG_MATRIX_ADMIN_PASSWORD_1}"
echo "" echo ""
echo " Sign in through the Element web interface, or point any Matrix" echo " Sign in through the Element web interface, or any Matrix client"
echo " client (FluffyChat, Nheko, Element desktop) at the homeserver URL" echo " (Element mobile/desktop, FluffyChat, Nheko) using the server name"
echo " shown above." echo " above."
echo "" echo ""
if [[ "$(_matrixPublicBaseUrl "$app_name")" != https://* ]]; then
echo " This install serves plain HTTP, which is fine over the LAN or a"
echo " WireGuard tunnel. Two consequences worth knowing:"
echo " • It cannot federate — other Matrix servers need public DNS + TLS."
echo " • Element's mobile apps expect an HTTPS homeserver; desktop and"
echo " web clients are unaffected."
echo ""
fi
echo " Registration is ${CFG_MATRIX_ENABLE_REGISTRATION:-false}. To invite" echo " Registration is ${CFG_MATRIX_ENABLE_REGISTRATION:-false}. To invite"
echo " others while it stays closed, create their accounts with:" echo " others while it stays closed, create their accounts with:"
echo " docker exec -it matrix-synapse register_new_matrix_user \\" echo " docker exec -it matrix-synapse register_new_matrix_user \\"

View File

@ -43,97 +43,6 @@ _stoatDomain()
fi fi
} }
# Scheme + host the client bundle is built against, with no trailing slash.
#
# https://<host> when Traefik is installed and a domain is configured;
# otherwise http://<lan-ip>:<allocated-port>, which is a perfectly good Stoat
# instance for LAN or WireGuard use — it just cannot do camera or microphone,
# because browsers only grant those to a secure context.
#
# The port is only assigned during compose-up, so a call from
# install_post_compose returns a best guess and install_post_start corrects it.
_stoatBaseUrl()
{
local app_name="$1"
local compose="$containers_dir$app_name/docker-compose.yml"
if [[ -d "${containers_dir}traefik" && -n "$domain_full" ]]; then
local host
host=$(_stoatDomain)
[[ -n "$host" ]] && { echo "https://${host}"; return 0; }
fi
local ports external
ports=$(tagsManagerGetTagContent "$compose" "PORTS_TAG_1")
external="${ports%%:*}"
if [[ -n "$external" && "$external" != PORTS_DATA* ]]; then
echo "http://${public_ip_v4:-localhost}:${external}"
else
echo "http://${public_ip_v4:-localhost}"
fi
}
# Write the three files that carry the public URL. Called once with a guess
# before the stack starts (they are bind-mounted, so they must exist or docker
# would create directories in their place) and again once the port is known.
_stoatWriteUrlFiles()
{
local app_dir="$1" base="$2" video_enabled="$3"
# ws:// for http, wss:// for https — a wss:// URL on a plain-HTTP origin
# fails to connect and the client hangs on "connecting".
local ws_scheme="wss"
[[ "$base" == http://* ]] && ws_scheme="ws"
local hostport="${base#*://}"
runFileWrite "$app_dir/.env.web" <<EOF
HOSTNAME=:80
REVOLT_PUBLIC_URL=${base}/api
VITE_API_URL=${base}/api
VITE_WS_URL=${ws_scheme}://${hostport}/ws
VITE_MEDIA_URL=${base}/autumn
VITE_PROXY_URL=${base}/january
VITE_GIFBOX_URL=${base}/gifbox
VITE_CFG_ENABLE_VIDEO=${video_enabled}
EOF
printf '{"api":"%s/api"}' "$base" | runFileWrite "$app_dir/stoat.json"
runFileWrite "$app_dir/Revolt.toml" <<EOF
# Generated by LibrePortal at install time. Secrets live in secrets.env, not
# here. Reinstalling the app rewrites this file — put custom configuration in a
# copy and merge it back if you change anything.
[hosts]
app = "${base}"
api = "${base}/api"
events = "${ws_scheme}://${hostport}/ws"
autumn = "${base}/autumn"
january = "${base}/january"
gifbox = "${base}/gifbox"
[hosts.livekit]
worldwide = "${ws_scheme}://${hostport}/livekit"
[api.livekit.nodes.worldwide]
url = "http://livekit:7880"
lat = 0.0
lon = 0.0
EOF
if [[ -n "$video_enabled" ]]; then
runFileWrite -a "$app_dir/Revolt.toml" <<'EOF'
[features.limits.new_user]
video_resolution = [1920, 1080]
video_aspect_ratio = [0.3, 10]
[features.limits.default]
video_resolution = [1920, 1080]
video_aspect_ratio = [0.3, 10]
EOF
fi
}
# Generate secrets.env if it does not already exist. Returns without touching an # Generate secrets.env if it does not already exist. Returns without touching an
# existing file — see the warning at the top. # existing file — see the warning at the top.
_stoatWriteSecrets() _stoatWriteSecrets()
@ -186,6 +95,14 @@ stoat_install_post_compose()
echo "---- $menu_number. Generating the Stoat instance configuration" echo "---- $menu_number. Generating the Stoat instance configuration"
echo "" echo ""
local domain
domain=$(_stoatDomain)
if [[ -z "$domain" ]]; then
isError "No domain is configured — cannot determine the public host for Stoat."
isNotice "Set CFG_DOMAIN_${CFG_STOAT_DOMAIN:-1} (General → Network), then reinstall."
return 1
fi
local result local result
result=$(createFolders "loud" "$docker_install_user" \ result=$(createFolders "loud" "$docker_install_user" \
"$app_dir/data/db" "$app_dir/data/rabbit" "$app_dir/data/minio" \ "$app_dir/data/db" "$app_dir/data/rabbit" "$app_dir/data/minio" \
@ -205,17 +122,62 @@ stoat_install_post_compose()
return 1 return 1
fi fi
# HOSTNAME=:80 is what puts Caddy in plain-HTTP mode behind Traefik. The
# VITE_* values are compiled into the browser bundle, so they must be the
# public https:// URLs, not internal container addresses.
local video_enabled="" local video_enabled=""
[[ "$CFG_STOAT_ENABLE_VIDEO" != "false" ]] && video_enabled="true" [[ "$CFG_STOAT_ENABLE_VIDEO" != "false" ]] && video_enabled="true"
# The port is not allocated yet, so this is a guess whenever there is no runFileWrite "$app_dir/.env.web" <<EOF
# domain. The files still have to exist now: they are bind-mounted, and HOSTNAME=:80
# docker would silently create directories in their place otherwise. REVOLT_PUBLIC_URL=https://${domain}/api
# stoat_install_post_start rewrites them once the real port is known. VITE_API_URL=https://${domain}/api
local base VITE_WS_URL=wss://${domain}/ws
base=$(_stoatBaseUrl "$app_name") VITE_MEDIA_URL=https://${domain}/autumn
_stoatWriteUrlFiles "$app_dir" "$base" "$video_enabled" VITE_PROXY_URL=https://${domain}/january
checkSuccess "Writing .env.web, stoat.json and Revolt.toml for $base" VITE_GIFBOX_URL=https://${domain}/gifbox
VITE_CFG_ENABLE_VIDEO=${video_enabled}
EOF
checkSuccess "Writing .env.web for https://$domain"
# Client discovery document, served at /.well-known/stoat.
printf '{"api":"https://%s/api"}' "$domain" | runFileWrite "$app_dir/stoat.json"
checkSuccess "Writing stoat.json"
runFileWrite "$app_dir/Revolt.toml" <<EOF
# Generated by LibrePortal at install time. Secrets live in secrets.env, not
# here. Reinstalling the app rewrites this file — put custom configuration in a
# copy and merge it back if you change anything.
[hosts]
app = "https://${domain}"
api = "https://${domain}/api"
events = "wss://${domain}/ws"
autumn = "https://${domain}/autumn"
january = "https://${domain}/january"
gifbox = "https://${domain}/gifbox"
[hosts.livekit]
worldwide = "wss://${domain}/livekit"
[api.livekit.nodes.worldwide]
url = "http://livekit:7880"
lat = 0.0
lon = 0.0
EOF
if [[ -n "$video_enabled" ]]; then
runFileWrite -a "$app_dir/Revolt.toml" <<'EOF'
[features.limits.new_user]
video_resolution = [1920, 1080]
video_aspect_ratio = [0.3, 10]
[features.limits.default]
video_resolution = [1920, 1080]
video_aspect_ratio = [0.3, 10]
EOF
fi
checkSuccess "Writing Revolt.toml (video=${video_enabled:-false})"
# use_external_ip lets LiveKit discover the address to advertise for WebRTC. # use_external_ip lets LiveKit discover the address to advertise for WebRTC.
# The port range matches the literal UDP mapping in the compose file; change # The port range matches the literal UDP mapping in the compose file; change
@ -250,57 +212,18 @@ EOF
checkSuccess "Setting ownership on the $app_name install directory" checkSuccess "Setting ownership on the $app_name install directory"
} }
stoat_install_post_start()
{
local app_name="$1"
local app_dir="$containers_dir$app_name"
# Ports are assigned during compose-up, so on a domain-less install the URL
# baked in a moment ago was a guess. Correct it now and restart, but only if
# it actually changed — restarting sixteen containers for nothing is not
# free, and a domain-backed install guessed right the first time.
local base current
base=$(_stoatBaseUrl "$app_name")
current=$(runFileOp grep -oP '^VITE_API_URL=\K.*' "$app_dir/.env.web" 2>/dev/null)
current="${current%/api}"
[[ "$base" == "$current" ]] && return 0
((menu_number++))
echo ""
echo "---- $menu_number. Settling the Stoat public URL"
echo ""
local video_enabled=""
[[ "$CFG_STOAT_ENABLE_VIDEO" != "false" ]] && video_enabled="true"
_stoatWriteUrlFiles "$app_dir" "$base" "$video_enabled"
runFileOp chown -R "$docker_install_user":"$docker_install_user" "$app_dir"
isSuccessful "Public URL settled as $base (was ${current:-unset})"
# The web client compiles VITE_* at container start, so it has to come back
# up before the corrected URL reaches a browser.
dockerComposeRestart "$app_name"
}
stoat_install_post() stoat_install_post()
{ {
local app_name="$1" local app_name="$1"
local base local domain
base=$(_stoatBaseUrl "$app_name") domain=$(_stoatDomain)
echo "" echo ""
isNotice "Stoat first run:" isNotice "Stoat first run:"
echo "" echo ""
echo " Open ${base} and create an account — the first account" echo " Open https://${domain} and create an account — the first account"
echo " registered on a fresh instance becomes the instance owner." echo " registered on a fresh instance becomes the instance owner."
echo "" echo ""
if [[ "$base" == http://* ]]; then
echo " This install serves plain HTTP. Text chat, channels, roles and"
echo " uploads all work, but browsers refuse camera and microphone"
echo " access outside a secure context — so voice and video will not"
echo " work until it is served over HTTPS. A WireGuard tunnel does not"
echo " change that: the check is on the URL scheme, not the transport."
echo ""
fi
echo " Give it a few minutes on first boot: sixteen containers start in" echo " Give it a few minutes on first boot: sixteen containers start in"
echo " dependency order, and the API restarts until MongoDB and RabbitMQ" echo " dependency order, and the API restarts until MongoDB and RabbitMQ"
echo " both report healthy. 'docker compose ps' in the app directory" echo " both report healthy. 'docker compose ps' in the app directory"

View File

@ -10,17 +10,14 @@
# HEALTHCHECK = if true, default docker health checks for that container will be enabled # HEALTHCHECK = if true, default docker health checks for that container will be enabled
# AUTHELIA = if true, use Authelia authentication, if false turned off. # AUTHELIA = if true, use Authelia authentication, if false turned off.
# HEADSCALE = options : false, local, remote (see general config). e.g false or local,remote # HEADSCALE = options : false, local, remote (see general config). e.g false or local,remote
# ENABLE_VIDEO = if true, allow camera and screen sharing (needs an HTTPS origin to work in a browser) # ENABLE_VIDEO = if true, allow camera and screen sharing (voice always works)
# MONITORING = if true, export this app's metrics to Prometheus + Grafana (needs both apps installed) # MONITORING = if true, export this app's metrics to Prometheus + Grafana (needs both apps installed)
# #
CFG_STOAT_APP_NAME=stoat CFG_STOAT_APP_NAME=stoat
# No prerequisites. Stoat bakes its public URL into the client bundle, but that # Stoat bakes its public URL into the client bundle and into Revolt.toml at
# URL can just as well be http://<lan-ip>:<port> — text chat, channels, roles # install time, and voice needs real TLS, so a domain behind Traefik is a
# and uploads all work that way over a LAN or WireGuard tunnel. Only camera and # prerequisite rather than a nicety.
# microphone need an HTTPS origin, because browsers refuse getUserMedia outside CFG_STOAT_REQUIRES="domain,traefik"
# a secure context; a VPN does not change that, since the check is on the URL
# scheme. Any HTTPS satisfies it, including a self-signed Traefik certificate.
CFG_STOAT_REQUIRES=""
CFG_STOAT_BACKUP=true CFG_STOAT_BACKUP=true
CFG_STOAT_BACKUP_STRATEGY=auto CFG_STOAT_BACKUP_STRATEGY=auto
# Manual, deliberately. This is a sixteen-service stack whose components are # Manual, deliberately. This is a sixteen-service stack whose components are
@ -61,6 +58,7 @@ CFG_STOAT_DESCRIPTION="Discord-style Chat"
CFG_STOAT_LONG_DESCRIPTION="The closest open-source match to Discord — servers, channels, roles, and LiveKit voice and video. Heavy (16 containers) and does not federate" CFG_STOAT_LONG_DESCRIPTION="The closest open-source match to Discord — servers, channels, roles, and LiveKit voice and video. Heavy (16 containers) and does not federate"
CFG_STOAT_URL="https://github.com/stoatchat/self-hosted" CFG_STOAT_URL="https://github.com/stoatchat/self-hosted"
CFG_STOAT_ACTIONS="configure|install|restart|shutdown|uninstall" CFG_STOAT_ACTIONS="configure|install|restart|shutdown|uninstall"
CFG_STOAT_REQUIRES_SERVICE=traefik
# #
# ============================================================================= # =============================================================================
# NETWORK CONFIGURATION # NETWORK CONFIGURATION

View File

@ -77,7 +77,6 @@ declare -gA LP_FN_MAP=(
[_appReqServiceMsg]="checks/requirements/check_app_install.sh" [_appReqServiceMsg]="checks/requirements/check_app_install.sh"
[appScanAvailable]="app/app_scan_available.sh" [appScanAvailable]="app/app_scan_available.sh"
[appSetupComposeTags_gluetun]="gluetun/scripts/gluetun_compose_tags.sh" [appSetupComposeTags_gluetun]="gluetun/scripts/gluetun_compose_tags.sh"
[appSetupComposeTags_matrix]="matrix/scripts/matrix_compose_tags.sh"
[appSetupComposeTags_nextcloud]="nextcloud/scripts/nextcloud_compose_tags.sh" [appSetupComposeTags_nextcloud]="nextcloud/scripts/nextcloud_compose_tags.sh"
[appSetupComposeTags_pihole]="pihole/scripts/pihole_compose_tags.sh" [appSetupComposeTags_pihole]="pihole/scripts/pihole_compose_tags.sh"
[appSetupComposeTags_searxng]="searxng/scripts/searxng_compose_tags.sh" [appSetupComposeTags_searxng]="searxng/scripts/searxng_compose_tags.sh"
@ -652,7 +651,6 @@ declare -gA LP_FN_MAP=(
[matrix_install_post_compose]="matrix/scripts/matrix_install_hooks.sh" [matrix_install_post_compose]="matrix/scripts/matrix_install_hooks.sh"
[matrix_install_post_start]="matrix/scripts/matrix_install_hooks.sh" [matrix_install_post_start]="matrix/scripts/matrix_install_hooks.sh"
[matrix_install_pre]="matrix/scripts/matrix_install_hooks.sh" [matrix_install_pre]="matrix/scripts/matrix_install_hooks.sh"
[_matrixPublicBaseUrl]="matrix/scripts/matrix_install_hooks.sh"
[_matrixServerName]="matrix/scripts/matrix_install_hooks.sh" [_matrixServerName]="matrix/scripts/matrix_install_hooks.sh"
[mattermostToolsMenu]="menu/tools/manage_mattermost.sh" [mattermostToolsMenu]="menu/tools/manage_mattermost.sh"
[maybeRegenPoll]="task/crontab_task_processor.sh" [maybeRegenPoll]="task/crontab_task_processor.sh"
@ -887,14 +885,11 @@ declare -gA LP_FN_MAP=(
[startOther]="start/start_other.sh" [startOther]="start/start_other.sh"
[startPreInstall]="start/start_preinstall.sh" [startPreInstall]="start/start_preinstall.sh"
[startScan]="start/start_scan.sh" [startScan]="start/start_scan.sh"
[_stoatBaseUrl]="stoat/scripts/stoat_install_hooks.sh"
[_stoatDomain]="stoat/scripts/stoat_install_hooks.sh" [_stoatDomain]="stoat/scripts/stoat_install_hooks.sh"
[stoat_install_post]="stoat/scripts/stoat_install_hooks.sh" [stoat_install_post]="stoat/scripts/stoat_install_hooks.sh"
[stoat_install_post_compose]="stoat/scripts/stoat_install_hooks.sh" [stoat_install_post_compose]="stoat/scripts/stoat_install_hooks.sh"
[stoat_install_post_start]="stoat/scripts/stoat_install_hooks.sh"
[stoat_install_pre]="stoat/scripts/stoat_install_hooks.sh" [stoat_install_pre]="stoat/scripts/stoat_install_hooks.sh"
[_stoatWriteSecrets]="stoat/scripts/stoat_install_hooks.sh" [_stoatWriteSecrets]="stoat/scripts/stoat_install_hooks.sh"
[_stoatWriteUrlFiles]="stoat/scripts/stoat_install_hooks.sh"
[stopCrowdsec]="crowdsec/crowdsec.sh" [stopCrowdsec]="crowdsec/crowdsec.sh"
[switchMigrateBackupApps]="docker/type_switcher/swap_docker_type.sh" [switchMigrateBackupApps]="docker/type_switcher/swap_docker_type.sh"
[switchMigrateRestoreApps]="docker/type_switcher/swap_docker_type.sh" [switchMigrateRestoreApps]="docker/type_switcher/swap_docker_type.sh"
@ -1138,7 +1133,6 @@ declare -gA LP_FN_ROOT=(
[_appReqServiceMsg]="scripts" [_appReqServiceMsg]="scripts"
[appScanAvailable]="scripts" [appScanAvailable]="scripts"
[appSetupComposeTags_gluetun]="containers" [appSetupComposeTags_gluetun]="containers"
[appSetupComposeTags_matrix]="containers"
[appSetupComposeTags_nextcloud]="containers" [appSetupComposeTags_nextcloud]="containers"
[appSetupComposeTags_pihole]="containers" [appSetupComposeTags_pihole]="containers"
[appSetupComposeTags_searxng]="containers" [appSetupComposeTags_searxng]="containers"
@ -1713,7 +1707,6 @@ declare -gA LP_FN_ROOT=(
[matrix_install_post_compose]="containers" [matrix_install_post_compose]="containers"
[matrix_install_post_start]="containers" [matrix_install_post_start]="containers"
[matrix_install_pre]="containers" [matrix_install_pre]="containers"
[_matrixPublicBaseUrl]="containers"
[_matrixServerName]="containers" [_matrixServerName]="containers"
[mattermostToolsMenu]="scripts" [mattermostToolsMenu]="scripts"
[maybeRegenPoll]="scripts" [maybeRegenPoll]="scripts"
@ -1948,14 +1941,11 @@ declare -gA LP_FN_ROOT=(
[startOther]="scripts" [startOther]="scripts"
[startPreInstall]="scripts" [startPreInstall]="scripts"
[startScan]="scripts" [startScan]="scripts"
[_stoatBaseUrl]="containers"
[_stoatDomain]="containers" [_stoatDomain]="containers"
[stoat_install_post]="containers" [stoat_install_post]="containers"
[stoat_install_post_compose]="containers" [stoat_install_post_compose]="containers"
[stoat_install_post_start]="containers"
[stoat_install_pre]="containers" [stoat_install_pre]="containers"
[_stoatWriteSecrets]="containers" [_stoatWriteSecrets]="containers"
[_stoatWriteUrlFiles]="containers"
[stopCrowdsec]="containers" [stopCrowdsec]="containers"
[switchMigrateBackupApps]="scripts" [switchMigrateBackupApps]="scripts"
[switchMigrateRestoreApps]="scripts" [switchMigrateRestoreApps]="scripts"
@ -2233,7 +2223,6 @@ _appReqServiceInstalled() { unset -f _appReqServiceInstalled; __lpAutoload "${in
_appReqServiceMsg() { unset -f _appReqServiceMsg; __lpAutoload "${install_scripts_dir}checks/requirements/check_app_install.sh"; _appReqServiceMsg "$@"; } _appReqServiceMsg() { unset -f _appReqServiceMsg; __lpAutoload "${install_scripts_dir}checks/requirements/check_app_install.sh"; _appReqServiceMsg "$@"; }
appScanAvailable() { unset -f appScanAvailable; __lpAutoload "${install_scripts_dir}app/app_scan_available.sh"; appScanAvailable "$@"; } appScanAvailable() { unset -f appScanAvailable; __lpAutoload "${install_scripts_dir}app/app_scan_available.sh"; appScanAvailable "$@"; }
appSetupComposeTags_gluetun() { unset -f appSetupComposeTags_gluetun; __lpAutoload "${install_containers_dir}gluetun/scripts/gluetun_compose_tags.sh"; appSetupComposeTags_gluetun "$@"; } appSetupComposeTags_gluetun() { unset -f appSetupComposeTags_gluetun; __lpAutoload "${install_containers_dir}gluetun/scripts/gluetun_compose_tags.sh"; appSetupComposeTags_gluetun "$@"; }
appSetupComposeTags_matrix() { unset -f appSetupComposeTags_matrix; __lpAutoload "${install_containers_dir}matrix/scripts/matrix_compose_tags.sh"; appSetupComposeTags_matrix "$@"; }
appSetupComposeTags_nextcloud() { unset -f appSetupComposeTags_nextcloud; __lpAutoload "${install_containers_dir}nextcloud/scripts/nextcloud_compose_tags.sh"; appSetupComposeTags_nextcloud "$@"; } appSetupComposeTags_nextcloud() { unset -f appSetupComposeTags_nextcloud; __lpAutoload "${install_containers_dir}nextcloud/scripts/nextcloud_compose_tags.sh"; appSetupComposeTags_nextcloud "$@"; }
appSetupComposeTags_pihole() { unset -f appSetupComposeTags_pihole; __lpAutoload "${install_containers_dir}pihole/scripts/pihole_compose_tags.sh"; appSetupComposeTags_pihole "$@"; } appSetupComposeTags_pihole() { unset -f appSetupComposeTags_pihole; __lpAutoload "${install_containers_dir}pihole/scripts/pihole_compose_tags.sh"; appSetupComposeTags_pihole "$@"; }
appSetupComposeTags_searxng() { unset -f appSetupComposeTags_searxng; __lpAutoload "${install_containers_dir}searxng/scripts/searxng_compose_tags.sh"; appSetupComposeTags_searxng "$@"; } appSetupComposeTags_searxng() { unset -f appSetupComposeTags_searxng; __lpAutoload "${install_containers_dir}searxng/scripts/searxng_compose_tags.sh"; appSetupComposeTags_searxng "$@"; }
@ -2808,7 +2797,6 @@ matrix_install_post() { unset -f matrix_install_post; __lpAutoload "${install_co
matrix_install_post_compose() { unset -f matrix_install_post_compose; __lpAutoload "${install_containers_dir}matrix/scripts/matrix_install_hooks.sh"; matrix_install_post_compose "$@"; } matrix_install_post_compose() { unset -f matrix_install_post_compose; __lpAutoload "${install_containers_dir}matrix/scripts/matrix_install_hooks.sh"; matrix_install_post_compose "$@"; }
matrix_install_post_start() { unset -f matrix_install_post_start; __lpAutoload "${install_containers_dir}matrix/scripts/matrix_install_hooks.sh"; matrix_install_post_start "$@"; } matrix_install_post_start() { unset -f matrix_install_post_start; __lpAutoload "${install_containers_dir}matrix/scripts/matrix_install_hooks.sh"; matrix_install_post_start "$@"; }
matrix_install_pre() { unset -f matrix_install_pre; __lpAutoload "${install_containers_dir}matrix/scripts/matrix_install_hooks.sh"; matrix_install_pre "$@"; } matrix_install_pre() { unset -f matrix_install_pre; __lpAutoload "${install_containers_dir}matrix/scripts/matrix_install_hooks.sh"; matrix_install_pre "$@"; }
_matrixPublicBaseUrl() { unset -f _matrixPublicBaseUrl; __lpAutoload "${install_containers_dir}matrix/scripts/matrix_install_hooks.sh"; _matrixPublicBaseUrl "$@"; }
_matrixServerName() { unset -f _matrixServerName; __lpAutoload "${install_containers_dir}matrix/scripts/matrix_install_hooks.sh"; _matrixServerName "$@"; } _matrixServerName() { unset -f _matrixServerName; __lpAutoload "${install_containers_dir}matrix/scripts/matrix_install_hooks.sh"; _matrixServerName "$@"; }
mattermostToolsMenu() { unset -f mattermostToolsMenu; __lpAutoload "${install_scripts_dir}menu/tools/manage_mattermost.sh"; mattermostToolsMenu "$@"; } mattermostToolsMenu() { unset -f mattermostToolsMenu; __lpAutoload "${install_scripts_dir}menu/tools/manage_mattermost.sh"; mattermostToolsMenu "$@"; }
maybeRegenPoll() { unset -f maybeRegenPoll; __lpAutoload "${install_scripts_dir}task/crontab_task_processor.sh"; maybeRegenPoll "$@"; } maybeRegenPoll() { unset -f maybeRegenPoll; __lpAutoload "${install_scripts_dir}task/crontab_task_processor.sh"; maybeRegenPoll "$@"; }
@ -3043,14 +3031,11 @@ startLoad() { unset -f startLoad; __lpAutoload "${install_scripts_dir}start/star
startOther() { unset -f startOther; __lpAutoload "${install_scripts_dir}start/start_other.sh"; startOther "$@"; } startOther() { unset -f startOther; __lpAutoload "${install_scripts_dir}start/start_other.sh"; startOther "$@"; }
startPreInstall() { unset -f startPreInstall; __lpAutoload "${install_scripts_dir}start/start_preinstall.sh"; startPreInstall "$@"; } startPreInstall() { unset -f startPreInstall; __lpAutoload "${install_scripts_dir}start/start_preinstall.sh"; startPreInstall "$@"; }
startScan() { unset -f startScan; __lpAutoload "${install_scripts_dir}start/start_scan.sh"; startScan "$@"; } startScan() { unset -f startScan; __lpAutoload "${install_scripts_dir}start/start_scan.sh"; startScan "$@"; }
_stoatBaseUrl() { unset -f _stoatBaseUrl; __lpAutoload "${install_containers_dir}stoat/scripts/stoat_install_hooks.sh"; _stoatBaseUrl "$@"; }
_stoatDomain() { unset -f _stoatDomain; __lpAutoload "${install_containers_dir}stoat/scripts/stoat_install_hooks.sh"; _stoatDomain "$@"; } _stoatDomain() { unset -f _stoatDomain; __lpAutoload "${install_containers_dir}stoat/scripts/stoat_install_hooks.sh"; _stoatDomain "$@"; }
stoat_install_post() { unset -f stoat_install_post; __lpAutoload "${install_containers_dir}stoat/scripts/stoat_install_hooks.sh"; stoat_install_post "$@"; } stoat_install_post() { unset -f stoat_install_post; __lpAutoload "${install_containers_dir}stoat/scripts/stoat_install_hooks.sh"; stoat_install_post "$@"; }
stoat_install_post_compose() { unset -f stoat_install_post_compose; __lpAutoload "${install_containers_dir}stoat/scripts/stoat_install_hooks.sh"; stoat_install_post_compose "$@"; } stoat_install_post_compose() { unset -f stoat_install_post_compose; __lpAutoload "${install_containers_dir}stoat/scripts/stoat_install_hooks.sh"; stoat_install_post_compose "$@"; }
stoat_install_post_start() { unset -f stoat_install_post_start; __lpAutoload "${install_containers_dir}stoat/scripts/stoat_install_hooks.sh"; stoat_install_post_start "$@"; }
stoat_install_pre() { unset -f stoat_install_pre; __lpAutoload "${install_containers_dir}stoat/scripts/stoat_install_hooks.sh"; stoat_install_pre "$@"; } stoat_install_pre() { unset -f stoat_install_pre; __lpAutoload "${install_containers_dir}stoat/scripts/stoat_install_hooks.sh"; stoat_install_pre "$@"; }
_stoatWriteSecrets() { unset -f _stoatWriteSecrets; __lpAutoload "${install_containers_dir}stoat/scripts/stoat_install_hooks.sh"; _stoatWriteSecrets "$@"; } _stoatWriteSecrets() { unset -f _stoatWriteSecrets; __lpAutoload "${install_containers_dir}stoat/scripts/stoat_install_hooks.sh"; _stoatWriteSecrets "$@"; }
_stoatWriteUrlFiles() { unset -f _stoatWriteUrlFiles; __lpAutoload "${install_containers_dir}stoat/scripts/stoat_install_hooks.sh"; _stoatWriteUrlFiles "$@"; }
stopCrowdsec() { unset -f stopCrowdsec; __lpAutoload "${install_containers_dir}crowdsec/crowdsec.sh"; stopCrowdsec "$@"; } stopCrowdsec() { unset -f stopCrowdsec; __lpAutoload "${install_containers_dir}crowdsec/crowdsec.sh"; stopCrowdsec "$@"; }
switchMigrateBackupApps() { unset -f switchMigrateBackupApps; __lpAutoload "${install_scripts_dir}docker/type_switcher/swap_docker_type.sh"; switchMigrateBackupApps "$@"; } switchMigrateBackupApps() { unset -f switchMigrateBackupApps; __lpAutoload "${install_scripts_dir}docker/type_switcher/swap_docker_type.sh"; switchMigrateBackupApps "$@"; }
switchMigrateRestoreApps() { unset -f switchMigrateRestoreApps; __lpAutoload "${install_scripts_dir}docker/type_switcher/swap_docker_type.sh"; switchMigrateRestoreApps "$@"; } switchMigrateRestoreApps() { unset -f switchMigrateRestoreApps; __lpAutoload "${install_scripts_dir}docker/type_switcher/swap_docker_type.sh"; switchMigrateRestoreApps "$@"; }