Compare commits
2 Commits
c7df07ffc2
...
ec98a83b48
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ec98a83b48 | ||
|
|
4eb5fa6434 |
@ -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=RANDOMIZEDPASSWORD1
|
CFG_GITEA_ADMIN_PASSWORD_1=RANDOMIZEDPASSWORD2
|
||||||
|
|||||||
@ -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=RANDOMIZEDPASSWORD1
|
CFG_INVIDIOUS_ADMIN_PASSWORD_1=RANDOMIZEDPASSWORD2
|
||||||
|
|||||||
@ -24,6 +24,15 @@ 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:
|
||||||
|
|||||||
@ -13,14 +13,15 @@
|
|||||||
# 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
|
||||||
# A domain and Traefik are hard requirements, not conveniences. Synapse bakes
|
# No prerequisites. Synapse runs perfectly well on a plain HTTP listener reached
|
||||||
# server_name into every event and user ID it has ever signed, and it CANNOT be
|
# by IP over the LAN or a WireGuard tunnel — that is the same setup as sitting
|
||||||
# changed afterwards without throwing the database away — so the homeserver must
|
# behind a reverse proxy, minus the proxy. Only *federation* needs a real domain
|
||||||
# know its real public name at install time, over real TLS.
|
# with public DNS and TLS, and federation is optional.
|
||||||
CFG_MATRIX_REQUIRES="domain,traefik"
|
CFG_MATRIX_REQUIRES=""
|
||||||
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
|
||||||
@ -37,6 +38,21 @@ 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
|
||||||
@ -62,7 +78,6 @@ 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
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"default_server_config": {
|
"default_server_config": {
|
||||||
"m.homeserver": {
|
"m.homeserver": {
|
||||||
"base_url": "https://ELEMENT_HOMESERVER_PLACEHOLDER",
|
"base_url": "ELEMENT_BASEURL_PLACEHOLDER",
|
||||||
"server_name": "ELEMENT_HOMESERVER_PLACEHOLDER"
|
"server_name": "ELEMENT_SERVERNAME_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_HOMESERVER_PLACEHOLDER", "matrix.org"]
|
"servers": ["ELEMENT_SERVERNAME_PLACEHOLDER"]
|
||||||
},
|
},
|
||||||
"setting_defaults": {
|
"setting_defaults": {
|
||||||
"breadcrumbs": true
|
"breadcrumbs": true
|
||||||
|
|||||||
@ -10,15 +10,20 @@
|
|||||||
# 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. Because server_name is the same host Traefik already terminates
|
# listening on. Only switched on for a real HTTPS deployment — advertising
|
||||||
# TLS for, this is all the delegation that is needed — nothing has to be
|
# <server_name>:443 from an install that is only reachable at http://<ip>:<port>
|
||||||
# published at the apex domain.
|
# would just invite other servers to connect somewhere that cannot answer.
|
||||||
serve_server_wellknown: true
|
serve_server_wellknown: SYNAPSE_SERVE_WELLKNOWN_PLACEHOLDER
|
||||||
|
|
||||||
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
|
||||||
|
|||||||
27
containers/matrix/scripts/matrix_compose_tags.sh
Normal file
27
containers/matrix/scripts/matrix_compose_tags.sh
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
#!/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,6 +15,14 @@ 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.
|
||||||
@ -30,8 +38,14 @@ 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
|
||||||
|
# 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
|
||||||
|
fi
|
||||||
|
if [[ -n "$domain_full" ]]; then
|
||||||
local sub="${port_subdomains[0]}"
|
local sub="${port_subdomains[0]}"
|
||||||
[[ -z "$domain_full" ]] && return 1
|
|
||||||
if [[ "$sub" == "@" || "$sub" == "root" ]]; then
|
if [[ "$sub" == "@" || "$sub" == "root" ]]; then
|
||||||
echo "$domain_full"
|
echo "$domain_full"
|
||||||
elif [[ -n "$sub" ]]; then
|
elif [[ -n "$sub" ]]; then
|
||||||
@ -39,6 +53,42 @@ _matrixServerName()
|
|||||||
else
|
else
|
||||||
echo "matrix.${domain_full}"
|
echo "matrix.${domain_full}"
|
||||||
fi
|
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()
|
||||||
@ -55,8 +105,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 "No domain is configured — cannot determine the Matrix server_name."
|
isError "Could not determine a Matrix server_name and could not fall back to this host's address."
|
||||||
isNotice "Set CFG_DOMAIN_${CFG_MATRIX_DOMAIN:-1} (General → Network), then reinstall."
|
isNotice "Set CFG_MATRIX_SERVER_NAME in matrix.config, then reinstall."
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@ -75,6 +125,37 @@ 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.
|
||||||
@ -88,7 +169,20 @@ 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 \
|
||||||
@ -133,6 +227,8 @@ 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"
|
||||||
@ -145,28 +241,32 @@ 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.
|
||||||
@ -201,14 +301,20 @@ 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.
|
||||||
local result
|
# 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
|
||||||
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
|
||||||
checkSuccess "Creating Matrix admin account '$admin_user'"
|
isError "Creating Matrix admin account '$admin_user' failed: $result"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -226,10 +332,18 @@ 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 any Matrix client"
|
echo " Sign in through the Element web interface, or point any Matrix"
|
||||||
echo " (Element mobile/desktop, FluffyChat, Nheko) using the server name"
|
echo " client (FluffyChat, Nheko, Element desktop) at the homeserver URL"
|
||||||
echo " above."
|
echo " shown 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 \\"
|
||||||
|
|||||||
@ -43,6 +43,97 @@ _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()
|
||||||
@ -95,14 +186,6 @@ 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" \
|
||||||
@ -122,62 +205,17 @@ 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"
|
||||||
|
|
||||||
runFileWrite "$app_dir/.env.web" <<EOF
|
# The port is not allocated yet, so this is a guess whenever there is no
|
||||||
HOSTNAME=:80
|
# domain. The files still have to exist now: they are bind-mounted, and
|
||||||
REVOLT_PUBLIC_URL=https://${domain}/api
|
# docker would silently create directories in their place otherwise.
|
||||||
VITE_API_URL=https://${domain}/api
|
# stoat_install_post_start rewrites them once the real port is known.
|
||||||
VITE_WS_URL=wss://${domain}/ws
|
local base
|
||||||
VITE_MEDIA_URL=https://${domain}/autumn
|
base=$(_stoatBaseUrl "$app_name")
|
||||||
VITE_PROXY_URL=https://${domain}/january
|
_stoatWriteUrlFiles "$app_dir" "$base" "$video_enabled"
|
||||||
VITE_GIFBOX_URL=https://${domain}/gifbox
|
checkSuccess "Writing .env.web, stoat.json and Revolt.toml for $base"
|
||||||
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
|
||||||
@ -212,18 +250,57 @@ 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 domain
|
local base
|
||||||
domain=$(_stoatDomain)
|
base=$(_stoatBaseUrl "$app_name")
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
isNotice "Stoat first run:"
|
isNotice "Stoat first run:"
|
||||||
echo ""
|
echo ""
|
||||||
echo " Open https://${domain} and create an account — the first account"
|
echo " Open ${base} 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"
|
||||||
|
|||||||
@ -10,14 +10,17 @@
|
|||||||
# 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 (voice always works)
|
# ENABLE_VIDEO = if true, allow camera and screen sharing (needs an HTTPS origin to work in a browser)
|
||||||
# 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
|
||||||
# Stoat bakes its public URL into the client bundle and into Revolt.toml at
|
# No prerequisites. Stoat bakes its public URL into the client bundle, but that
|
||||||
# install time, and voice needs real TLS, so a domain behind Traefik is a
|
# URL can just as well be http://<lan-ip>:<port> — text chat, channels, roles
|
||||||
# prerequisite rather than a nicety.
|
# and uploads all work that way over a LAN or WireGuard tunnel. Only camera and
|
||||||
CFG_STOAT_REQUIRES="domain,traefik"
|
# 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=""
|
||||||
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
|
||||||
@ -58,7 +61,6 @@ 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
|
||||||
|
|||||||
@ -77,6 +77,7 @@ 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"
|
||||||
@ -651,6 +652,7 @@ 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"
|
||||||
@ -885,11 +887,14 @@ 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"
|
||||||
@ -1133,6 +1138,7 @@ 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"
|
||||||
@ -1707,6 +1713,7 @@ 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"
|
||||||
@ -1941,11 +1948,14 @@ 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"
|
||||||
@ -2223,6 +2233,7 @@ _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 "$@"; }
|
||||||
@ -2797,6 +2808,7 @@ 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 "$@"; }
|
||||||
@ -3031,11 +3043,14 @@ 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 "$@"; }
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user