Compare commits
No commits in common. "ec98a83b48e126f04dc2abc8b409b17803f93600" and "c7df07ffc24f38521d8ca3b414b48c53b759b62a" have entirely different histories.
ec98a83b48
...
c7df07ffc2
@ -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_ADMIN_USER=
|
||||
CFG_GITEA_ADMIN_EMAIL=
|
||||
CFG_GITEA_ADMIN_PASSWORD_1=RANDOMIZEDPASSWORD2
|
||||
CFG_GITEA_ADMIN_PASSWORD_1=RANDOMIZEDPASSWORD1
|
||||
|
||||
@ -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_ADMIN_USER=
|
||||
CFG_INVIDIOUS_ADMIN_EMAIL=
|
||||
CFG_INVIDIOUS_ADMIN_PASSWORD_1=RANDOMIZEDPASSWORD2
|
||||
CFG_INVIDIOUS_ADMIN_PASSWORD_1=RANDOMIZEDPASSWORD1
|
||||
|
||||
@ -24,15 +24,6 @@ services:
|
||||
environment:
|
||||
- TZ=TIMEZONE_DATA #LIBREPORTAL|TIMEZONE_TAG|TIMEZONE_DATA
|
||||
- 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:
|
||||
- matrix-postgres
|
||||
labels:
|
||||
|
||||
@ -13,15 +13,14 @@
|
||||
# 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_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)
|
||||
#
|
||||
CFG_MATRIX_APP_NAME=matrix
|
||||
# No prerequisites. Synapse runs perfectly well on a plain HTTP listener reached
|
||||
# by IP over the LAN or a WireGuard tunnel — that is the same setup as sitting
|
||||
# behind a reverse proxy, minus the proxy. Only *federation* needs a real domain
|
||||
# with public DNS and TLS, and federation is optional.
|
||||
CFG_MATRIX_REQUIRES=""
|
||||
# A domain and Traefik are hard requirements, not conveniences. Synapse bakes
|
||||
# server_name into every event and user ID it has ever signed, and it CANNOT be
|
||||
# changed afterwards without throwing the database away — so the homeserver must
|
||||
# know its real public name at install time, over real TLS.
|
||||
CFG_MATRIX_REQUIRES="domain,traefik"
|
||||
CFG_MATRIX_BACKUP=true
|
||||
CFG_MATRIX_BACKUP_STRATEGY=auto
|
||||
CFG_MATRIX_UPDATE_TYPE=auto
|
||||
@ -38,21 +37,6 @@ CFG_MATRIX_HEADSCALE=false
|
||||
CFG_MATRIX_ENABLE_REGISTRATION=false
|
||||
CFG_MATRIX_ADMIN_USERNAME=admin
|
||||
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
|
||||
# Postgres password for the `synapse` role, fed to the compose via
|
||||
# #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_URL="https://github.com/element-hq/synapse"
|
||||
CFG_MATRIX_ACTIONS="configure|install|restart|shutdown|uninstall"
|
||||
CFG_MATRIX_REQUIRES_SERVICE=traefik
|
||||
#
|
||||
# =============================================================================
|
||||
# NETWORK CONFIGURATION
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
{
|
||||
"default_server_config": {
|
||||
"m.homeserver": {
|
||||
"base_url": "ELEMENT_BASEURL_PLACEHOLDER",
|
||||
"server_name": "ELEMENT_SERVERNAME_PLACEHOLDER"
|
||||
"base_url": "https://ELEMENT_HOMESERVER_PLACEHOLDER",
|
||||
"server_name": "ELEMENT_HOMESERVER_PLACEHOLDER"
|
||||
}
|
||||
},
|
||||
"brand": "Element",
|
||||
@ -13,7 +13,7 @@
|
||||
"default_country_code": "GB",
|
||||
"show_labs_settings": true,
|
||||
"room_directory": {
|
||||
"servers": ["ELEMENT_SERVERNAME_PLACEHOLDER"]
|
||||
"servers": ["ELEMENT_HOMESERVER_PLACEHOLDER", "matrix.org"]
|
||||
},
|
||||
"setting_defaults": {
|
||||
"breadcrumbs": true
|
||||
|
||||
@ -10,20 +10,15 @@
|
||||
# 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.
|
||||
server_name: "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"
|
||||
public_baseurl: "https://SYNAPSE_SERVER_NAME_PLACEHOLDER/"
|
||||
pid_file: /data/homeserver.pid
|
||||
|
||||
# Serve /.well-known/matrix/server ourselves, advertising port 443. Federation
|
||||
# otherwise defaults to port 8448 on server_name, which Traefik is not
|
||||
# listening on. Only switched on for a real HTTPS deployment — advertising
|
||||
# <server_name>:443 from an install that is only reachable at http://<ip>:<port>
|
||||
# would just invite other servers to connect somewhere that cannot answer.
|
||||
serve_server_wellknown: SYNAPSE_SERVE_WELLKNOWN_PLACEHOLDER
|
||||
# listening on. Because server_name is the same host Traefik already terminates
|
||||
# TLS for, this is all the delegation that is needed — nothing has to be
|
||||
# published at the apex domain.
|
||||
serve_server_wellknown: true
|
||||
|
||||
listeners:
|
||||
# Port 8008 is the container-internal port and is deliberately hardcoded: it
|
||||
|
||||
@ -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"
|
||||
}
|
||||
@ -15,14 +15,6 @@ matrix_install_pre()
|
||||
matrix=n
|
||||
return 1
|
||||
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.
|
||||
@ -38,57 +30,15 @@ matrix_install_pre()
|
||||
# here and the Traefik rule generated later cannot drift apart.
|
||||
_matrixServerName()
|
||||
{
|
||||
# An explicit choice always wins — this is the escape hatch that lets a
|
||||
# LAN-only install still be given a federatable identity up front.
|
||||
if [[ -n "$CFG_MATRIX_SERVER_NAME" ]]; then
|
||||
echo "$CFG_MATRIX_SERVER_NAME"
|
||||
return 0
|
||||
local sub="${port_subdomains[0]}"
|
||||
[[ -z "$domain_full" ]] && return 1
|
||||
if [[ "$sub" == "@" || "$sub" == "root" ]]; then
|
||||
echo "$domain_full"
|
||||
elif [[ -n "$sub" ]]; then
|
||||
echo "${sub}.${domain_full}"
|
||||
else
|
||||
echo "matrix.${domain_full}"
|
||||
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()
|
||||
@ -105,8 +55,8 @@ matrix_install_post_compose()
|
||||
local server_name
|
||||
server_name=$(_matrixServerName)
|
||||
if [[ -z "$server_name" ]]; then
|
||||
isError "Could not determine a Matrix server_name and could not fall back to this host's address."
|
||||
isNotice "Set CFG_MATRIX_SERVER_NAME in matrix.config, then reinstall."
|
||||
isError "No domain is configured — cannot determine the Matrix server_name."
|
||||
isNotice "Set CFG_DOMAIN_${CFG_MATRIX_DOMAIN:-1} (General → Network), then reinstall."
|
||||
return 1
|
||||
fi
|
||||
|
||||
@ -125,37 +75,6 @@ matrix_install_post_compose()
|
||||
result=$(createFolders "loud" "$docker_install_user" "$data_dir" "$app_dir/element")
|
||||
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
|
||||
# seen one key will reject events signed by a different one. So: generate it
|
||||
# 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="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 \
|
||||
-e UID="$gen_uid" -e GID="$gen_gid" \
|
||||
-e SYNAPSE_SERVER_NAME="$server_name" \
|
||||
-e SYNAPSE_REPORT_STATS=no \
|
||||
-v "$data_dir":/data \
|
||||
@ -227,8 +133,6 @@ matrix_install_post_compose()
|
||||
local enable_registration="false"
|
||||
[[ "$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_DB_PASSWORD_PLACEHOLDER|$db_password|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"
|
||||
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()
|
||||
{
|
||||
local app_name="$1"
|
||||
local app_dir="$containers_dir$app_name"
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Creating the first Matrix admin account"
|
||||
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
|
||||
# Postgres takes appreciably longer than the container takes to start.
|
||||
# /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
|
||||
# already exists and register_new_matrix_user fails with "User ID already
|
||||
# taken", which is not worth failing the install over.
|
||||
# rc is captured on its own line because the [[ ]] test below overwrites $?
|
||||
# — checkSuccess would then report the *test's* result, which is how a
|
||||
# perfectly successful registration got logged as an error.
|
||||
local result rc
|
||||
local result
|
||||
result=$(runFileOp docker exec matrix-synapse register_new_matrix_user \
|
||||
-u "$admin_user" -p "$admin_pass" -a \
|
||||
-c /data/homeserver.yaml http://localhost:8008 2>&1)
|
||||
rc=$?
|
||||
if [[ "$result" == *"already taken"* ]]; then
|
||||
isNotice "Matrix admin '$admin_user' already exists — leaving the existing account alone."
|
||||
elif (( rc == 0 )); then
|
||||
isSuccessful "Created Matrix admin account '$admin_user'"
|
||||
else
|
||||
isError "Creating Matrix admin account '$admin_user' failed: $result"
|
||||
checkSuccess "Creating Matrix admin account '$admin_user'"
|
||||
fi
|
||||
}
|
||||
|
||||
@ -332,18 +226,10 @@ matrix_install_post()
|
||||
echo " Your user ID : @${admin_user}:${server_name}"
|
||||
echo " Password : ${CFG_MATRIX_ADMIN_PASSWORD_1}"
|
||||
echo ""
|
||||
echo " Sign in through the Element web interface, or point any Matrix"
|
||||
echo " client (FluffyChat, Nheko, Element desktop) at the homeserver URL"
|
||||
echo " shown above."
|
||||
echo " Sign in through the Element web interface, or any Matrix client"
|
||||
echo " (Element mobile/desktop, FluffyChat, Nheko) using the server name"
|
||||
echo " above."
|
||||
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 " others while it stays closed, create their accounts with:"
|
||||
echo " docker exec -it matrix-synapse register_new_matrix_user \\"
|
||||
|
||||
@ -43,97 +43,6 @@ _stoatDomain()
|
||||
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
|
||||
# existing file — see the warning at the top.
|
||||
_stoatWriteSecrets()
|
||||
@ -186,6 +95,14 @@ stoat_install_post_compose()
|
||||
echo "---- $menu_number. Generating the Stoat instance configuration"
|
||||
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
|
||||
result=$(createFolders "loud" "$docker_install_user" \
|
||||
"$app_dir/data/db" "$app_dir/data/rabbit" "$app_dir/data/minio" \
|
||||
@ -205,17 +122,62 @@ stoat_install_post_compose()
|
||||
return 1
|
||||
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=""
|
||||
[[ "$CFG_STOAT_ENABLE_VIDEO" != "false" ]] && video_enabled="true"
|
||||
|
||||
# The port is not allocated yet, so this is a guess whenever there is no
|
||||
# domain. The files still have to exist now: they are bind-mounted, and
|
||||
# docker would silently create directories in their place otherwise.
|
||||
# stoat_install_post_start rewrites them once the real port is known.
|
||||
local base
|
||||
base=$(_stoatBaseUrl "$app_name")
|
||||
_stoatWriteUrlFiles "$app_dir" "$base" "$video_enabled"
|
||||
checkSuccess "Writing .env.web, stoat.json and Revolt.toml for $base"
|
||||
runFileWrite "$app_dir/.env.web" <<EOF
|
||||
HOSTNAME=:80
|
||||
REVOLT_PUBLIC_URL=https://${domain}/api
|
||||
VITE_API_URL=https://${domain}/api
|
||||
VITE_WS_URL=wss://${domain}/ws
|
||||
VITE_MEDIA_URL=https://${domain}/autumn
|
||||
VITE_PROXY_URL=https://${domain}/january
|
||||
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.
|
||||
# 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"
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
local app_name="$1"
|
||||
local base
|
||||
base=$(_stoatBaseUrl "$app_name")
|
||||
local domain
|
||||
domain=$(_stoatDomain)
|
||||
|
||||
echo ""
|
||||
isNotice "Stoat first run:"
|
||||
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 ""
|
||||
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 " dependency order, and the API restarts until MongoDB and RabbitMQ"
|
||||
echo " both report healthy. 'docker compose ps' in the app directory"
|
||||
|
||||
@ -10,17 +10,14 @@
|
||||
# HEALTHCHECK = if true, default docker health checks for that container will be enabled
|
||||
# AUTHELIA = if true, use Authelia authentication, if false turned off.
|
||||
# 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)
|
||||
#
|
||||
CFG_STOAT_APP_NAME=stoat
|
||||
# No prerequisites. Stoat bakes its public URL into the client bundle, but that
|
||||
# URL can just as well be http://<lan-ip>:<port> — text chat, channels, roles
|
||||
# and uploads all work that way over a LAN or WireGuard tunnel. Only camera and
|
||||
# microphone need an HTTPS origin, because browsers refuse getUserMedia outside
|
||||
# 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=""
|
||||
# Stoat bakes its public URL into the client bundle and into Revolt.toml at
|
||||
# install time, and voice needs real TLS, so a domain behind Traefik is a
|
||||
# prerequisite rather than a nicety.
|
||||
CFG_STOAT_REQUIRES="domain,traefik"
|
||||
CFG_STOAT_BACKUP=true
|
||||
CFG_STOAT_BACKUP_STRATEGY=auto
|
||||
# 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_URL="https://github.com/stoatchat/self-hosted"
|
||||
CFG_STOAT_ACTIONS="configure|install|restart|shutdown|uninstall"
|
||||
CFG_STOAT_REQUIRES_SERVICE=traefik
|
||||
#
|
||||
# =============================================================================
|
||||
# NETWORK CONFIGURATION
|
||||
|
||||
@ -77,7 +77,6 @@ declare -gA LP_FN_MAP=(
|
||||
[_appReqServiceMsg]="checks/requirements/check_app_install.sh"
|
||||
[appScanAvailable]="app/app_scan_available.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_pihole]="pihole/scripts/pihole_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_start]="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"
|
||||
[mattermostToolsMenu]="menu/tools/manage_mattermost.sh"
|
||||
[maybeRegenPoll]="task/crontab_task_processor.sh"
|
||||
@ -887,14 +885,11 @@ declare -gA LP_FN_MAP=(
|
||||
[startOther]="start/start_other.sh"
|
||||
[startPreInstall]="start/start_preinstall.sh"
|
||||
[startScan]="start/start_scan.sh"
|
||||
[_stoatBaseUrl]="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_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"
|
||||
[_stoatWriteSecrets]="stoat/scripts/stoat_install_hooks.sh"
|
||||
[_stoatWriteUrlFiles]="stoat/scripts/stoat_install_hooks.sh"
|
||||
[stopCrowdsec]="crowdsec/crowdsec.sh"
|
||||
[switchMigrateBackupApps]="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"
|
||||
[appScanAvailable]="scripts"
|
||||
[appSetupComposeTags_gluetun]="containers"
|
||||
[appSetupComposeTags_matrix]="containers"
|
||||
[appSetupComposeTags_nextcloud]="containers"
|
||||
[appSetupComposeTags_pihole]="containers"
|
||||
[appSetupComposeTags_searxng]="containers"
|
||||
@ -1713,7 +1707,6 @@ declare -gA LP_FN_ROOT=(
|
||||
[matrix_install_post_compose]="containers"
|
||||
[matrix_install_post_start]="containers"
|
||||
[matrix_install_pre]="containers"
|
||||
[_matrixPublicBaseUrl]="containers"
|
||||
[_matrixServerName]="containers"
|
||||
[mattermostToolsMenu]="scripts"
|
||||
[maybeRegenPoll]="scripts"
|
||||
@ -1948,14 +1941,11 @@ declare -gA LP_FN_ROOT=(
|
||||
[startOther]="scripts"
|
||||
[startPreInstall]="scripts"
|
||||
[startScan]="scripts"
|
||||
[_stoatBaseUrl]="containers"
|
||||
[_stoatDomain]="containers"
|
||||
[stoat_install_post]="containers"
|
||||
[stoat_install_post_compose]="containers"
|
||||
[stoat_install_post_start]="containers"
|
||||
[stoat_install_pre]="containers"
|
||||
[_stoatWriteSecrets]="containers"
|
||||
[_stoatWriteUrlFiles]="containers"
|
||||
[stopCrowdsec]="containers"
|
||||
[switchMigrateBackupApps]="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 "$@"; }
|
||||
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_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_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 "$@"; }
|
||||
@ -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_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 "$@"; }
|
||||
_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 "$@"; }
|
||||
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 "$@"; }
|
||||
@ -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 "$@"; }
|
||||
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 "$@"; }
|
||||
_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 "$@"; }
|
||||
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_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 "$@"; }
|
||||
_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 "$@"; }
|
||||
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 "$@"; }
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user