Compare commits

...

2 Commits

Author SHA1 Message Date
librelad
a1541ace23 refactor(secrets): slot-number every generated config key
Makes the convention uniform: if a config key holds a generated value, its name
ends in a slot number. 42 keys across the catalog, up from the 9 database ones
done previously — admin passwords, app keys, tokens, HMAC and auth secrets,
generated usernames and database names. An app needing a second credential of a
kind now just adds _2; nothing is registered anywhere, since the tag name is
derived from the key by tags_processor_app_config_values.

Keys holding an operator-chosen value (CFG_NEXTCLOUD_ADMIN_USER=admin) keep their
names — the slot number is what marks a value as generated.

The rename would have silently cost seven keys their WebUI field mapping. The
frontend resolver matches a mapping key against a config key by equality, _suffix
or prefix_ (apps-manager.js findMatchingCFGKey), so the generic "ADMIN_PASSWORD"
entry stops matching CFG_GITEA_ADMIN_PASSWORD_1 — it neither ends with
_ADMIN_PASSWORD nor starts with ADMIN_PASSWORD_. Rather than loosen the matcher
(PORT_1 relies on its numeric suffix being part of the name), add explicit
entries. Did the same for eight keys that were already unmapped before this
change, so all 42 now render with a label and, where appropriate, masked: the
only one typed as text is Mastodon's VAPID public key, which is public by design.

Verified by simulating the resolver against every app config, and by running each
app in the catalog through fill -> hook -> templating: every secret tag
substitutes, no RANDOMIZED placeholder survives, every compose still parses.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 19:51:27 +01:00
librelad
65167463f9 fix(chat apps): tag every service so its IP actually substitutes
Installing rocketchat failed with

    invalid IPv4 address: ParseAddr("IP_DATA_2"): unable to parse IP

ipUpdateComposeTags allocates one IP per SERVICE_TAG_N annotation and fills
IP_TAG_i only where SERVICE_TAG_i exists. The four new apps tagged only their
primary service, so every sidecar — matrix's postgres, mattermost's postgres,
rocketchat's mongo, and fifteen of stoat's sixteen — kept a literal IP_DATA_n
in the deployed compose and docker refused to create the container.

Tag every service that carries an ipv4_address, index-aligned with its IP_TAG.
For stoat that also meant moving caddy from SERVICE_TAG_1 to _6 so the indices
line up with the IPs rather than the reading order.

mastodon had the same latent break (IP_TAG_2 and _3 untagged) and is fixed the
same way — it would have failed on first install for the same reason.

SERVICE_TAG carries the compose *key*, not container_name: 'libreportal app
restart <app> <service>' passes it to 'docker compose restart', which only
understands keys.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 19:48:09 +01:00
51 changed files with 245 additions and 126 deletions

View File

@ -22,7 +22,7 @@ CFG_ADGUARD_HEALTHCHECK=true
CFG_ADGUARD_AUTHELIA=false
CFG_ADGUARD_HEADSCALE=false
CFG_ADGUARD_USER=admin
CFG_ADGUARD_PASSWORD=RANDOMIZEDPASSWORD1
CFG_ADGUARD_PASSWORD_1=RANDOMIZEDPASSWORD1
CFG_ADGUARD_MONITORING=false
#
# =============================================================================
@ -77,4 +77,4 @@ CFG_ADGUARD_PORT_6="adguard-exporter|metrics|9617:9617|disabled|tcp|false|false|
# AUTH_PROFILE = capability tier for the WebUI auth tools (single_password | user_password | multi_user)
CFG_ADGUARD_AUTH_PROFILE=single_password
CFG_ADGUARD_ADMIN_USER=
CFG_ADGUARD_ADMIN_PASSWORD=RANDOMIZEDPASSWORD2
CFG_ADGUARD_ADMIN_PASSWORD_1=RANDOMIZEDPASSWORD2

View File

@ -54,7 +54,7 @@ services:
# - ADGUARD_PROTOCOL=http
# - ADGUARD_HOSTNAME=adguard-service:PORT_INTERNAL_DATA_1 #LIBREPORTAL|PORT_INTERNAL_TAG_1|PORT_INTERNAL_DATA_1
# - ADGUARD_USERNAME=ADGUARD_USER_DATA #LIBREPORTAL|ADGUARD_USER_TAG|ADGUARD_USER_DATA
# - ADGUARD_PASSWORD=ADGUARD_PASSWORD_DATA #LIBREPORTAL|ADGUARD_PASSWORD_TAG|ADGUARD_PASSWORD_DATA
# - ADGUARD_PASSWORD=ADGUARD_PASSWORD_1_DATA #LIBREPORTAL|ADGUARD_PASSWORD_1_TAG|ADGUARD_PASSWORD_1_DATA
# - INTERVAL=30s
# - LOG_LIMIT=10000
# - SERVER_PORT=PORT_INTERNAL_DATA_6 #LIBREPORTAL|PORT_INTERNAL_TAG_6|PORT_INTERNAL_DATA_6

View File

@ -54,11 +54,11 @@ adguard_install_post_start()
isError "AdGuardHome admin endpoint did not respond on $adguard_setup_url within $((adguard_max_attempts * 2))s — open the URL and complete setup manually, then re-run the installer to apply the post-setup tweaks."
else
local adguard_user="${CFG_ADGUARD_USER:-admin}"
local adguard_pass="${CFG_ADGUARD_PASSWORD:-}"
local adguard_pass="${CFG_ADGUARD_PASSWORD_1:-}"
if [[ -z "$adguard_pass" ]]; then
adguard_pass=$(generateRandomPassword)
updateConfigOption "CFG_ADGUARD_PASSWORD" "$adguard_pass" >/dev/null 2>&1 || true
isNotice "Generated a random AdGuardHome admin password and saved it to CFG_ADGUARD_PASSWORD."
updateConfigOption "CFG_ADGUARD_PASSWORD_1" "$adguard_pass" >/dev/null 2>&1 || true
isNotice "Generated a random AdGuardHome admin password and saved it to CFG_ADGUARD_PASSWORD_1."
fi
# Internal container ports are fixed (3000 admin, 53 DNS); host
@ -138,5 +138,5 @@ adguard_install_message_data()
{
# Echo the admin user + password as space-separated tokens so they
# become $username $password positional args to menuShowFinalMessages.
echo "${CFG_ADGUARD_USER:-admin} $CFG_ADGUARD_PASSWORD"
echo "${CFG_ADGUARD_USER:-admin} $CFG_ADGUARD_PASSWORD_1"
}

View File

@ -26,7 +26,7 @@ CFG_AUTHELIA_AUTHELIA=false
CFG_AUTHELIA_HEADSCALE=false
CFG_AUTHELIA_THEME=dark
CFG_AUTHELIA_ADMIN_USERNAME=admin
CFG_AUTHELIA_ADMIN_PASSWORD=RANDOMIZEDPASSWORD1
CFG_AUTHELIA_ADMIN_PASSWORD_1=RANDOMIZEDPASSWORD1
CFG_AUTHELIA_MONITORING=false
#
# =============================================================================

View File

@ -57,7 +57,7 @@ authelia_install_post_start()
echo ""
local authelia_admin_user="${CFG_AUTHELIA_ADMIN_USERNAME:-admin}"
local authelia_admin_pass="${CFG_AUTHELIA_ADMIN_PASSWORD:-authelia}"
local authelia_admin_pass="${CFG_AUTHELIA_ADMIN_PASSWORD_1:-authelia}"
local authelia_users_file="$containers_dir$app_name/config/users_database.yml"
local authelia_attempts=0
while ((authelia_attempts < 30)); do
@ -101,7 +101,7 @@ authelia_install_post()
{
local app_name="$1"
local authelia_admin_user="${CFG_AUTHELIA_ADMIN_USERNAME:-admin}"
local authelia_admin_pass="${CFG_AUTHELIA_ADMIN_PASSWORD:-authelia}"
local authelia_admin_pass="${CFG_AUTHELIA_ADMIN_PASSWORD_1:-authelia}"
echo ""
isNotice "Authelia admin login:"
echo ""

View File

@ -26,11 +26,11 @@ CFG_BOOKSTACK_HEALTHCHECK=true
CFG_BOOKSTACK_AUTHELIA=false
CFG_BOOKSTACK_HEADSCALE=false
CFG_BOOKSTACK_ADMIN_EMAIL=admin@example.com
CFG_BOOKSTACK_ADMIN_PASSWORD=RANDOMIZEDPASSWORD3
CFG_BOOKSTACK_ADMIN_PASSWORD_1=RANDOMIZEDPASSWORD3
# Secrets below feed the compose via #LIBREPORTAL|BOOKSTACK_<KEY>_TAG| tags —
# auto-generated, and (unlike a RANDOMIZED* placeholder in the compose)
# preserved across reinstalls. DB_PASSWORD is shared by the app + db services.
CFG_BOOKSTACK_APP_KEY=RANDOMIZEDAPPKEY1
CFG_BOOKSTACK_APP_KEY_1=RANDOMIZEDAPPKEY1
CFG_BOOKSTACK_DB_PASSWORD_1=RANDOMIZEDPASSWORD1
CFG_BOOKSTACK_DB_ROOT_PASSWORD_1=RANDOMIZEDPASSWORD2
#

View File

@ -11,7 +11,7 @@ services:
- PGID=1000
- TZ=TIMEZONE_DATA #LIBREPORTAL|TIMEZONE_TAG|TIMEZONE_DATA
- APP_URL=APP_URL_DATA #LIBREPORTAL|APP_URL_TAG|APP_URL_DATA
- APP_KEY=BOOKSTACK_APP_KEY_DATA #LIBREPORTAL|BOOKSTACK_APP_KEY_TAG|BOOKSTACK_APP_KEY_DATA
- APP_KEY=BOOKSTACK_APP_KEY_1_DATA #LIBREPORTAL|BOOKSTACK_APP_KEY_1_TAG|BOOKSTACK_APP_KEY_1_DATA
- DB_HOST=bookstack_db
- DB_PORT=3306
- DB_USERNAME=bookstack

View File

@ -10,7 +10,7 @@ bookstack_install_post_start()
local app_name="$1"
local bookstack_target_email="${CFG_BOOKSTACK_ADMIN_EMAIL:-admin@admin.com}"
local bookstack_target_pass="${CFG_BOOKSTACK_ADMIN_PASSWORD:-password}"
local bookstack_target_pass="${CFG_BOOKSTACK_ADMIN_PASSWORD_1:-password}"
local bookstack_compose_file="$containers_dir$app_name/docker-compose.yml"
local bookstack_port_pair

View File

@ -41,7 +41,7 @@ services:
- TZ:TIMEZONE_DATA #LIBREPORTAL|TIMEZONE_TAG|TIMEZONE_DATA
# >>> libreportal-monitoring >>>
#- GITEA__metrics__ENABLED=true
#- GITEA__metrics__TOKEN=GITEA_METRICS_TOKEN_DATA #LIBREPORTAL|GITEA_METRICS_TOKEN_TAG|GITEA_METRICS_TOKEN_DATA
#- GITEA__metrics__TOKEN=GITEA_METRICS_TOKEN_1_DATA #LIBREPORTAL|GITEA_METRICS_TOKEN_1_TAG|GITEA_METRICS_TOKEN_1_DATA
# <<< libreportal-monitoring <<<
# GLUETUN_OFF_BEGIN
ports:

View File

@ -21,7 +21,7 @@ CFG_GITEA_HEALTHCHECK=true
CFG_GITEA_AUTHELIA=false
CFG_GITEA_HEADSCALE=false
CFG_GITEA_MONITORING=false
CFG_GITEA_METRICS_TOKEN=RANDOMIZEDPASSWORD1
CFG_GITEA_METRICS_TOKEN_1=RANDOMIZEDPASSWORD1
# =============================================================================
# METADATA
@ -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=RANDOMIZEDPASSWORD1
CFG_GITEA_ADMIN_PASSWORD_1=RANDOMIZEDPASSWORD1

View File

@ -1,6 +1,6 @@
#!/bin/bash
# Gitea install hooks — mirror CFG_GITEA_METRICS_TOKEN into the Prometheus
# Gitea install hooks — mirror CFG_GITEA_METRICS_TOKEN_1 into the Prometheus
# scrape fragment so the bearer token in the compose env matches what the
# Prometheus side sends.
@ -13,13 +13,13 @@ gitea_install_post_compose()
# Gitea's public web port and is token-protected; sync the token into
# the scrape config so the two sides agree.
if monitoringAppEnabled "$app_name"; then
if [[ -n "$CFG_GITEA_METRICS_TOKEN" ]]; then
if [[ -n "$CFG_GITEA_METRICS_TOKEN_1" ]]; then
local result
result=$(runFileOp sed -i "s|GITEA_METRICS_TOKEN_PLACEHOLDER|${CFG_GITEA_METRICS_TOKEN}|g" \
result=$(runFileOp sed -i "s|GITEA_METRICS_TOKEN_PLACEHOLDER|${CFG_GITEA_METRICS_TOKEN_1}|g" \
"$containers_dir$app_name/resources/monitoring/prometheus-scrape.yml")
checkSuccess "Synced Gitea /metrics token to the Prometheus scrape config"
else
isNotice "CFG_GITEA_METRICS_TOKEN is empty — Gitea /metrics scrape may 401."
isNotice "CFG_GITEA_METRICS_TOKEN_1 is empty — Gitea /metrics scrape may 401."
fi
fi
}

View File

@ -40,7 +40,7 @@ CFG_GLUETUN_OPENVPN_USER=
CFG_GLUETUN_OPENVPN_PASSWORD=
CFG_GLUETUN_WIREGUARD_PRIVATE_KEY=
CFG_GLUETUN_WIREGUARD_ADDRESSES=
CFG_GLUETUN_CONTROL_SERVER_API_KEY=RANDOMIZEDPASSWORD1
CFG_GLUETUN_CONTROL_SERVER_API_KEY_1=RANDOMIZEDPASSWORD1
# HEALTH_TARGETS = comma-separated host:port list pinged over HTTPS to
# confirm the VPN tunnel is healthy. Defaults are privacy-respecting
# (Mullvad — your VPN provider; EFF — privacy non-profit). Override

View File

@ -49,7 +49,7 @@ services:
- LOG_LEVEL=info # Log level. "DEBUG", "ERROR", "WARNING", or "INFO". Default "INFO"
# ENV for Basic Auth (Used only if AUTH_TYPE is "Basic"). Can be omitted if you aren't using Basic Auth
- BASIC_AUTH_USER=libreportal # Used for basic auth
- BASIC_AUTH_PASS=HEADSCALE_BASIC_AUTH_PASS_DATA #LIBREPORTAL|HEADSCALE_BASIC_AUTH_PASS_TAG|HEADSCALE_BASIC_AUTH_PASS_DATA
- BASIC_AUTH_PASS=HEADSCALE_BASIC_AUTH_PASS_1_DATA #LIBREPORTAL|HEADSCALE_BASIC_AUTH_PASS_1_TAG|HEADSCALE_BASIC_AUTH_PASS_1_DATA
# ENV for OIDC (Used only if AUTH_TYPE is "OIDC"). Can be omitted if you aren't using OIDC
#- OIDC_AUTH_URL=https://yourauthserver.com/application/o/headscale/.well-known/openid-configuration # URL for your OIDC issuer's well-known endpoint
#- OIDC_CLIENT_ID=your-auth-server-client-id-info-here # Your OIDC Issuer's Client ID for Headscale-WebUI

View File

@ -7,7 +7,7 @@
# BACKUP = if true, include this application in backup operations
# UPDATE_TYPE = auto: new image builds are applied automatically (a recovery snapshot is taken first), manual: only when you press Update
# HEALTHCHECK = if true, default docker health checks for that container will be enabled
# BASIC_AUTH_PASS = password for the headscale-ui basic auth; auto-generated, fed to the compose via HEADSCALE_BASIC_AUTH_PASS_TAG
# BASIC_AUTH_PASS = password for the headscale-ui basic auth; auto-generated, fed to the compose via HEADSCALE_BASIC_AUTH_PASS_1_TAG
# MONITORING = if true, export this app's metrics to Prometheus + Grafana (needs both apps installed)
#
CFG_HEADSCALE_APP_NAME=headscale
@ -16,7 +16,7 @@ CFG_HEADSCALE_BACKUP_STRATEGY=auto
CFG_HEADSCALE_UPDATE_TYPE=auto
CFG_HEADSCALE_COMPOSE_FILE=default
CFG_HEADSCALE_HEALTHCHECK=true
CFG_HEADSCALE_BASIC_AUTH_PASS=RANDOMIZEDPASSWORD1
CFG_HEADSCALE_BASIC_AUTH_PASS_1=RANDOMIZEDPASSWORD1
CFG_HEADSCALE_MONITORING=false
#
# =============================================================================

View File

@ -26,7 +26,7 @@ services:
# domain:
# https_only: false
# statistics_enabled: false
hmac_key: "INVIDIOUS_HMAC_KEY_DATA" #LIBREPORTAL|INVIDIOUS_HMAC_KEY_TAG|INVIDIOUS_HMAC_KEY_DATA
hmac_key: "INVIDIOUS_HMAC_KEY_1_DATA" #LIBREPORTAL|INVIDIOUS_HMAC_KEY_1_TAG|INVIDIOUS_HMAC_KEY_1_DATA
healthcheck:
test: wget -nv --tries=1 --spider http://127.0.0.1:3000/api/v1/comments/jNQXAC9IVRw || exit 1
interval: 30s

View File

@ -19,8 +19,8 @@ CFG_INVIDIOUS_HEALTHCHECK=false
CFG_INVIDIOUS_AUTHELIA=false
CFG_INVIDIOUS_HEADSCALE=false
# HMAC_KEY = signs Invidious tokens/links; auto-generated, fed to the compose
# via the INVIDIOUS_HMAC_KEY_TAG tag (preserved across reinstalls)
CFG_INVIDIOUS_HMAC_KEY=RANDOMIZEDPASSWORD1
# via the INVIDIOUS_HMAC_KEY_1_TAG tag (preserved across reinstalls)
CFG_INVIDIOUS_HMAC_KEY_1=RANDOMIZEDPASSWORD1
#
# =============================================================================
# METADATA
@ -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=RANDOMIZEDPASSWORD1
CFG_INVIDIOUS_ADMIN_PASSWORD_1=RANDOMIZEDPASSWORD1

View File

@ -15,14 +15,14 @@ services:
- TZ=TIMEZONE_DATA #LIBREPORTAL|TIMEZONE_TAG|TIMEZONE_DATA
- LOCAL_DOMAIN=DOMAINSUBNAME_DATA #LIBREPORTAL|DOMAINSUBNAME_TAG|DOMAINSUBNAME_DATA
- DB_HOST=mastodon-postgres
- DB_USER=MASTODON_DB_USER_DATA #LIBREPORTAL|MASTODON_DB_USER_TAG|MASTODON_DB_USER_DATA
- DB_USER=MASTODON_DB_USER_1_DATA #LIBREPORTAL|MASTODON_DB_USER_1_TAG|MASTODON_DB_USER_1_DATA
- DB_PASS=MASTODON_DB_PASSWORD_1_DATA #LIBREPORTAL|MASTODON_DB_PASSWORD_1_TAG|MASTODON_DB_PASSWORD_1_DATA
- DB_NAME=MASTODON_DB_NAME_DATA #LIBREPORTAL|MASTODON_DB_NAME_TAG|MASTODON_DB_NAME_DATA
- DB_NAME=MASTODON_DB_NAME_1_DATA #LIBREPORTAL|MASTODON_DB_NAME_1_TAG|MASTODON_DB_NAME_1_DATA
- REDIS_HOST=mastodon-redis
- SECRET_KEY_BASE=MASTODON_SECRET_KEY_BASE_DATA #LIBREPORTAL|MASTODON_SECRET_KEY_BASE_TAG|MASTODON_SECRET_KEY_BASE_DATA
- OTP_SECRET=MASTODON_OTP_SECRET_DATA #LIBREPORTAL|MASTODON_OTP_SECRET_TAG|MASTODON_OTP_SECRET_DATA
- VAPID_PRIVATE_KEY=MASTODON_VAPID_PRIVATE_KEY_DATA #LIBREPORTAL|MASTODON_VAPID_PRIVATE_KEY_TAG|MASTODON_VAPID_PRIVATE_KEY_DATA
- VAPID_PUBLIC_KEY=MASTODON_VAPID_PUBLIC_KEY_DATA #LIBREPORTAL|MASTODON_VAPID_PUBLIC_KEY_TAG|MASTODON_VAPID_PUBLIC_KEY_DATA
- SECRET_KEY_BASE=MASTODON_SECRET_KEY_BASE_1_DATA #LIBREPORTAL|MASTODON_SECRET_KEY_BASE_1_TAG|MASTODON_SECRET_KEY_BASE_1_DATA
- OTP_SECRET=MASTODON_OTP_SECRET_1_DATA #LIBREPORTAL|MASTODON_OTP_SECRET_1_TAG|MASTODON_OTP_SECRET_1_DATA
- VAPID_PRIVATE_KEY=MASTODON_VAPID_PRIVATE_KEY_1_DATA #LIBREPORTAL|MASTODON_VAPID_PRIVATE_KEY_1_TAG|MASTODON_VAPID_PRIVATE_KEY_1_DATA
- VAPID_PUBLIC_KEY=MASTODON_VAPID_PUBLIC_KEY_1_DATA #LIBREPORTAL|MASTODON_VAPID_PUBLIC_KEY_1_TAG|MASTODON_VAPID_PUBLIC_KEY_1_DATA
- SMTP_SERVER=
- SMTP_PORT=587
- SMTP_LOGIN=
@ -59,8 +59,8 @@ services:
image: postgres:15
container_name: mastodon-postgres
environment:
- POSTGRES_DB=MASTODON_DB_NAME_DATA #LIBREPORTAL|MASTODON_DB_NAME_TAG|MASTODON_DB_NAME_DATA
- POSTGRES_USER=MASTODON_DB_USER_DATA #LIBREPORTAL|MASTODON_DB_USER_TAG|MASTODON_DB_USER_DATA
- POSTGRES_DB=MASTODON_DB_NAME_1_DATA #LIBREPORTAL|MASTODON_DB_NAME_1_TAG|MASTODON_DB_NAME_1_DATA
- POSTGRES_USER=MASTODON_DB_USER_1_DATA #LIBREPORTAL|MASTODON_DB_USER_1_TAG|MASTODON_DB_USER_1_DATA
- POSTGRES_PASSWORD=MASTODON_DB_PASSWORD_1_DATA #LIBREPORTAL|MASTODON_DB_PASSWORD_1_TAG|MASTODON_DB_PASSWORD_1_DATA
volumes:
- ./postgres:/var/lib/postgresql/data

View File

@ -42,13 +42,13 @@ CFG_MASTODON_HEADSCALE=false
# substitutable: if key generation ever fails, Mastodon starts with push
# broken rather than with a literal placeholder as its key.
#
CFG_MASTODON_DB_NAME=RANDOMIZEDUSERNAME1
CFG_MASTODON_DB_USER=RANDOMIZEDUSERNAME2
CFG_MASTODON_DB_NAME_1=RANDOMIZEDUSERNAME1
CFG_MASTODON_DB_USER_1=RANDOMIZEDUSERNAME2
CFG_MASTODON_DB_PASSWORD_1=RANDOMIZEDPASSWORD1
CFG_MASTODON_SECRET_KEY_BASE=RANDOMIZEDHEX1
CFG_MASTODON_OTP_SECRET=RANDOMIZEDHEX2
CFG_MASTODON_VAPID_PRIVATE_KEY=RANDOMIZEDVAPID1
CFG_MASTODON_VAPID_PUBLIC_KEY=RANDOMIZEDVAPID2
CFG_MASTODON_SECRET_KEY_BASE_1=RANDOMIZEDHEX1
CFG_MASTODON_OTP_SECRET_1=RANDOMIZEDHEX2
CFG_MASTODON_VAPID_PRIVATE_KEY_1=RANDOMIZEDVAPID1
CFG_MASTODON_VAPID_PUBLIC_KEY_1=RANDOMIZEDVAPID2
#
# =============================================================================
# METADATA

View File

@ -36,8 +36,8 @@ mastodon_install_post_setup()
# retires the old values: two independent RANDOMIZEDVAPID strings are the
# wrong length for the public half, so an install carrying them regenerates
# once and is correct from then on.
if [[ "$CFG_MASTODON_VAPID_PRIVATE_KEY" =~ ^[A-Za-z0-9_-]{43}$ \
&& "$CFG_MASTODON_VAPID_PUBLIC_KEY" =~ ^[A-Za-z0-9_-]{87}$ ]]; then
if [[ "$CFG_MASTODON_VAPID_PRIVATE_KEY_1" =~ ^[A-Za-z0-9_-]{43}$ \
&& "$CFG_MASTODON_VAPID_PUBLIC_KEY_1" =~ ^[A-Za-z0-9_-]{87}$ ]]; then
return 0
fi
@ -65,7 +65,7 @@ mastodon_install_post_setup()
return 0
fi
updateConfigOption "CFG_MASTODON_VAPID_PRIVATE_KEY" "$vapid_private" "$config_file"
updateConfigOption "CFG_MASTODON_VAPID_PUBLIC_KEY" "$vapid_public" "$config_file"
updateConfigOption "CFG_MASTODON_VAPID_PRIVATE_KEY_1" "$vapid_private" "$config_file"
updateConfigOption "CFG_MASTODON_VAPID_PUBLIC_KEY_1" "$vapid_public" "$config_file"
isSuccessful "Generated a VAPID keypair for $app_name web push."
}

View File

@ -36,7 +36,7 @@ CFG_MATRIX_AUTHELIA=false
CFG_MATRIX_HEADSCALE=false
CFG_MATRIX_ENABLE_REGISTRATION=false
CFG_MATRIX_ADMIN_USERNAME=admin
CFG_MATRIX_ADMIN_PASSWORD=RANDOMIZEDPASSWORD1
CFG_MATRIX_ADMIN_PASSWORD_1=RANDOMIZEDPASSWORD1
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

View File

@ -178,7 +178,7 @@ matrix_install_post_start()
fi
local admin_user="${CFG_MATRIX_ADMIN_USERNAME:-admin}"
local admin_pass="${CFG_MATRIX_ADMIN_PASSWORD}"
local admin_pass="${CFG_MATRIX_ADMIN_PASSWORD_1}"
if [[ -z "$admin_pass" || "$admin_pass" == RANDOMIZEDPASSWORD* ]]; then
isNotice "No admin password is set in matrix.config — skipping admin account creation."
return 0
@ -210,7 +210,7 @@ matrix_install_post()
echo ""
echo " Server name : ${server_name}"
echo " Your user ID : @${admin_user}:${server_name}"
echo " Password : ${CFG_MATRIX_ADMIN_PASSWORD}"
echo " Password : ${CFG_MATRIX_ADMIN_PASSWORD_1}"
echo ""
echo " Sign in through the Element web interface, or any Matrix client"
echo " (Element mobile/desktop, FluffyChat, Nheko) using the server name"

View File

@ -15,8 +15,8 @@ services:
# GLUETUN_OFF_END
environment:
- TZ=TIMEZONE_DATA #LIBREPORTAL|TIMEZONE_TAG|TIMEZONE_DATA
# AUTH_SECRET is auto-generated (CFG_MONEYAPP_AUTH_SECRET=RANDOMIZEDHEX1).
- AUTH_SECRET=MONEYAPP_AUTH_SECRET_DATA #LIBREPORTAL|MONEYAPP_AUTH_SECRET_TAG|MONEYAPP_AUTH_SECRET_DATA
# AUTH_SECRET is auto-generated (CFG_MONEYAPP_AUTH_SECRET_1=RANDOMIZEDHEX1).
- AUTH_SECRET=MONEYAPP_AUTH_SECRET_1_DATA #LIBREPORTAL|MONEYAPP_AUTH_SECRET_1_TAG|MONEYAPP_AUTH_SECRET_1_DATA
- AUTH_URL=MONEYAPP_AUTH_URL_DATA #LIBREPORTAL|MONEYAPP_AUTH_URL_TAG|MONEYAPP_AUTH_URL_DATA
- DATABASE_FILE=/data/moneyapp.db
volumes:

View File

@ -20,7 +20,7 @@ CFG_MONEYAPP_COMPOSE_FILE=default
CFG_MONEYAPP_HEALTHCHECK=true
CFG_MONEYAPP_AUTHELIA=false
CFG_MONEYAPP_HEADSCALE=false
CFG_MONEYAPP_AUTH_SECRET=RANDOMIZEDHEX1
CFG_MONEYAPP_AUTH_SECRET_1=RANDOMIZEDHEX1
#
# =============================================================================
# METADATA

View File

@ -18,7 +18,7 @@ services:
- MYSQL_USER=NEXTCLOUD_DB_USER_DATA #LIBREPORTAL|NEXTCLOUD_DB_USER_TAG|NEXTCLOUD_DB_USER_DATA
- MYSQL_PASSWORD=NEXTCLOUD_DB_PASSWORD_1_DATA #LIBREPORTAL|NEXTCLOUD_DB_PASSWORD_1_TAG|NEXTCLOUD_DB_PASSWORD_1_DATA
- NEXTCLOUD_ADMIN_USER=NEXTCLOUD_ADMIN_USER_DATA #LIBREPORTAL|NEXTCLOUD_ADMIN_USER_TAG|NEXTCLOUD_ADMIN_USER_DATA
- NEXTCLOUD_ADMIN_PASSWORD=NEXTCLOUD_ADMIN_PASSWORD_DATA #LIBREPORTAL|NEXTCLOUD_ADMIN_PASSWORD_TAG|NEXTCLOUD_ADMIN_PASSWORD_DATA
- NEXTCLOUD_ADMIN_PASSWORD=NEXTCLOUD_ADMIN_PASSWORD_1_DATA #LIBREPORTAL|NEXTCLOUD_ADMIN_PASSWORD_1_TAG|NEXTCLOUD_ADMIN_PASSWORD_1_DATA
- NEXTCLOUD_TRUSTED_DOMAINS=NEXTCLOUD_TRUSTED_DOMAINS_DATA #LIBREPORTAL|NEXTCLOUD_TRUSTED_DOMAINS_TAG|NEXTCLOUD_TRUSTED_DOMAINS_DATA
- REDIS_HOST=nextcloud-redis
volumes:
@ -116,7 +116,7 @@ services:
# environment:
# - NEXTCLOUD_SERVER=http://nextcloud-web:PORT_INTERNAL_DATA_1 #LIBREPORTAL|PORT_INTERNAL_TAG_1|PORT_INTERNAL_DATA_1
# - NEXTCLOUD_USERNAME=NEXTCLOUD_ADMIN_USER_DATA #LIBREPORTAL|NEXTCLOUD_ADMIN_USER_TAG|NEXTCLOUD_ADMIN_USER_DATA
# - NEXTCLOUD_PASSWORD=NEXTCLOUD_ADMIN_PASSWORD_DATA #LIBREPORTAL|NEXTCLOUD_ADMIN_PASSWORD_TAG|NEXTCLOUD_ADMIN_PASSWORD_DATA
# - NEXTCLOUD_PASSWORD=NEXTCLOUD_ADMIN_PASSWORD_1_DATA #LIBREPORTAL|NEXTCLOUD_ADMIN_PASSWORD_1_TAG|NEXTCLOUD_ADMIN_PASSWORD_1_DATA
# - NEXTCLOUD_TLS_SKIP_VERIFY=true
# - NEXTCLOUD_LISTEN_ADDRESS=:PORT_INTERNAL_DATA_2 #LIBREPORTAL|PORT_INTERNAL_TAG_2|PORT_INTERNAL_DATA_2
# networks:

View File

@ -24,7 +24,7 @@ CFG_NEXTCLOUD_AUTHELIA=false
CFG_NEXTCLOUD_HEADSCALE=false
CFG_NEXTCLOUD_MONITORING=false
CFG_NEXTCLOUD_ADMIN_USER=admin
CFG_NEXTCLOUD_ADMIN_PASSWORD=RANDOMIZEDPASSWORD1
CFG_NEXTCLOUD_ADMIN_PASSWORD_1=RANDOMIZEDPASSWORD1
CFG_NEXTCLOUD_DB_NAME=nextcloud
CFG_NEXTCLOUD_DB_USER=nextcloud
CFG_NEXTCLOUD_DB_PASSWORD_1=RANDOMIZEDPASSWORD2

View File

@ -20,12 +20,12 @@ services:
- OWNCLOUD_DOMAIN=DOMAINSUBNAME_DATA #LIBREPORTAL|DOMAINSUBNAME_TAG|DOMAINSUBNAME_DATA
- OWNCLOUD_TRUSTED_DOMAINS=TRUSTED_DOMAINS_DATA #LIBREPORTAL|TRUSTED_DOMAINS_TAG|TRUSTED_DOMAINS_DATA
- OWNCLOUD_DB_TYPE=mysql
- OWNCLOUD_DB_NAME=OWNCLOUD_DB_NAME_DATA #LIBREPORTAL|OWNCLOUD_DB_NAME_TAG|OWNCLOUD_DB_NAME_DATA
- OWNCLOUD_DB_USERNAME=OWNCLOUD_DB_USER_DATA #LIBREPORTAL|OWNCLOUD_DB_USER_TAG|OWNCLOUD_DB_USER_DATA
- OWNCLOUD_DB_NAME=OWNCLOUD_DB_NAME_1_DATA #LIBREPORTAL|OWNCLOUD_DB_NAME_1_TAG|OWNCLOUD_DB_NAME_1_DATA
- OWNCLOUD_DB_USERNAME=OWNCLOUD_DB_USER_1_DATA #LIBREPORTAL|OWNCLOUD_DB_USER_1_TAG|OWNCLOUD_DB_USER_1_DATA
- OWNCLOUD_DB_PASSWORD=OWNCLOUD_DB_PASSWORD_1_DATA #LIBREPORTAL|OWNCLOUD_DB_PASSWORD_1_TAG|OWNCLOUD_DB_PASSWORD_1_DATA
- OWNCLOUD_DB_HOST=owncloud-mariadb
- OWNCLOUD_ADMIN_USERNAME=OWNCLOUD_ADMIN_USERNAME_DATA #LIBREPORTAL|OWNCLOUD_ADMIN_USERNAME_TAG|OWNCLOUD_ADMIN_USERNAME_DATA
- OWNCLOUD_ADMIN_PASSWORD=OWNCLOUD_ADMIN_PASSWORD_DATA #LIBREPORTAL|OWNCLOUD_ADMIN_PASSWORD_TAG|OWNCLOUD_ADMIN_PASSWORD_DATA
- OWNCLOUD_ADMIN_USERNAME=OWNCLOUD_ADMIN_USERNAME_1_DATA #LIBREPORTAL|OWNCLOUD_ADMIN_USERNAME_1_TAG|OWNCLOUD_ADMIN_USERNAME_1_DATA
- OWNCLOUD_ADMIN_PASSWORD=OWNCLOUD_ADMIN_PASSWORD_1_DATA #LIBREPORTAL|OWNCLOUD_ADMIN_PASSWORD_1_TAG|OWNCLOUD_ADMIN_PASSWORD_1_DATA
- OWNCLOUD_MYSQL_UTF8MB4=true
- OWNCLOUD_REDIS_ENABLED=true
- OWNCLOUD_REDIS_HOST=owncloud-redis
@ -74,9 +74,9 @@ services:
hostname: mariadb
environment:
- MYSQL_ROOT_PASSWORD=OWNCLOUD_DB_ROOT_PASSWORD_1_DATA #LIBREPORTAL|OWNCLOUD_DB_ROOT_PASSWORD_1_TAG|OWNCLOUD_DB_ROOT_PASSWORD_1_DATA
- MYSQL_USER=OWNCLOUD_DB_USER_DATA #LIBREPORTAL|OWNCLOUD_DB_USER_TAG|OWNCLOUD_DB_USER_DATA
- MYSQL_USER=OWNCLOUD_DB_USER_1_DATA #LIBREPORTAL|OWNCLOUD_DB_USER_1_TAG|OWNCLOUD_DB_USER_1_DATA
- MYSQL_PASSWORD=OWNCLOUD_DB_PASSWORD_1_DATA #LIBREPORTAL|OWNCLOUD_DB_PASSWORD_1_TAG|OWNCLOUD_DB_PASSWORD_1_DATA
- MYSQL_DATABASE=OWNCLOUD_DB_NAME_DATA #LIBREPORTAL|OWNCLOUD_DB_NAME_TAG|OWNCLOUD_DB_NAME_DATA
- MYSQL_DATABASE=OWNCLOUD_DB_NAME_1_DATA #LIBREPORTAL|OWNCLOUD_DB_NAME_1_TAG|OWNCLOUD_DB_NAME_1_DATA
command: ["--max-allowed-packet=128M", "--innodb-log-file-size=64M"]
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-u", "root", "--password=OWNCLOUD_DB_ROOT_PASSWORD_1_DATA"] #LIBREPORTAL|OWNCLOUD_DB_ROOT_PASSWORD_1_TAG|OWNCLOUD_DB_ROOT_PASSWORD_1_DATA

View File

@ -33,10 +33,10 @@ CFG_OWNCLOUD_HEADSCALE=false
# what keeps them in step with the MariaDB volume, and what makes the admin
# credentials shown in the WebUI the ones ownCloud was actually seeded with.
#
CFG_OWNCLOUD_ADMIN_USERNAME=RANDOMIZEDUSERNAME1
CFG_OWNCLOUD_ADMIN_PASSWORD=RANDOMIZEDPASSWORD1
CFG_OWNCLOUD_DB_NAME=RANDOMIZEDUSERNAME2
CFG_OWNCLOUD_DB_USER=RANDOMIZEDUSERNAME3
CFG_OWNCLOUD_ADMIN_USERNAME_1=RANDOMIZEDUSERNAME1
CFG_OWNCLOUD_ADMIN_PASSWORD_1=RANDOMIZEDPASSWORD1
CFG_OWNCLOUD_DB_NAME_1=RANDOMIZEDUSERNAME2
CFG_OWNCLOUD_DB_USER_1=RANDOMIZEDUSERNAME3
CFG_OWNCLOUD_DB_PASSWORD_1=RANDOMIZEDPASSWORD2
CFG_OWNCLOUD_DB_ROOT_PASSWORD_1=RANDOMIZEDPASSWORD3
#

View File

@ -36,8 +36,8 @@ owncloud_install_post_compose()
local result
result=$(runFileOp sed -i \
-e "s|OWNCLOUD_SETUP_VERSION|$owncloud_version|g" \
-e "s|OWNCLOUD_SETUP_ADMIN_USERNAME|$CFG_OWNCLOUD_ADMIN_USERNAME|g" \
-e "s|OWNCLOUD_SETUP_ADMIN_PASSWORD|$CFG_OWNCLOUD_ADMIN_PASSWORD|g" \
-e "s|OWNCLOUD_SETUP_ADMIN_USERNAME|$CFG_OWNCLOUD_ADMIN_USERNAME_1|g" \
-e "s|OWNCLOUD_SETUP_ADMIN_PASSWORD|$CFG_OWNCLOUD_ADMIN_PASSWORD_1|g" \
-e "s|OWNCLOUD_SETUP_HTTP_PORT|$usedport1|g" \
"$file_path")
checkSuccess "Updating $file_name for $app_name"

View File

@ -34,7 +34,7 @@ services:
# nothing — including the admin password.
environment:
- TZ=TIMEZONE_DATA #LIBREPORTAL|TIMEZONE_TAG|TIMEZONE_DATA
- FTLCONF_webserver_api_password=PIHOLE_ADMIN_PASSWORD_DATA #LIBREPORTAL|PIHOLE_ADMIN_PASSWORD_TAG|PIHOLE_ADMIN_PASSWORD_DATA
- FTLCONF_webserver_api_password=PIHOLE_ADMIN_PASSWORD_1_DATA #LIBREPORTAL|PIHOLE_ADMIN_PASSWORD_1_TAG|PIHOLE_ADMIN_PASSWORD_1_DATA
- FTLCONF_webserver_interface_theme=PIHOLE_WEB_THEME_DATA #LIBREPORTAL|PIHOLE_WEB_THEME_TAG|PIHOLE_WEB_THEME_DATA
# Upstream resolver. The old image bundled unbound and pointed here at
# 127.0.0.1#5335; the official image bundles nothing, so this must be a
@ -72,7 +72,7 @@ services:
# environment:
# - PIHOLE_HOSTNAME=pihole-service
# - PIHOLE_PORT=PORT_INTERNAL_DATA_1 #LIBREPORTAL|PORT_INTERNAL_TAG_1|PORT_INTERNAL_DATA_1
# - PIHOLE_PASSWORD=PIHOLE_ADMIN_PASSWORD_DATA #LIBREPORTAL|PIHOLE_ADMIN_PASSWORD_TAG|PIHOLE_ADMIN_PASSWORD_DATA
# - PIHOLE_PASSWORD=PIHOLE_ADMIN_PASSWORD_1_DATA #LIBREPORTAL|PIHOLE_ADMIN_PASSWORD_1_TAG|PIHOLE_ADMIN_PASSWORD_1_DATA
# - INTERVAL=30s
# - PORT=PORT_INTERNAL_DATA_6 #LIBREPORTAL|PORT_INTERNAL_TAG_6|PORT_INTERNAL_DATA_6
# networks:

View File

@ -27,7 +27,7 @@ CFG_PIHOLE_MONITORING=false
# ADMIN_PASSWORD = web interface admin password (will be generated if set to RANDOMIZEDPASSWORD)
# WEB_THEME = web interface theme (default-dark, default-light, or custom themes)
#
CFG_PIHOLE_ADMIN_PASSWORD=RANDOMIZEDPASSWORD1
CFG_PIHOLE_ADMIN_PASSWORD_1=RANDOMIZEDPASSWORD1
CFG_PIHOLE_WEB_THEME=default-dark
CFG_PIHOLE_UPSTREAM_DNS=9.9.9.9;149.112.112.112
#

View File

@ -88,6 +88,15 @@ services:
- ./mongo_config:/data/configdb
environment:
- TZ=TIMEZONE_DATA #LIBREPORTAL|TIMEZONE_TAG|TIMEZONE_DATA
# Required on Linux 6.19 and newer. The TCMalloc bundled with
# MongoDB 8.x registers restartable sequences in a way the newer
# kernel rejects; mongod then aborts on startup with "Linux kernel
# versions 6.19 and newer has a known incompatibility"
# (SERVER-121912). Letting glibc own the rseq registration avoids
# the conflict. Harmless once upstream ships a patched TCMalloc —
# this only sets a glibc tunable — so it can be dropped then rather
# than urgently.
- GLIBC_TUNABLES=glibc.pthread.rseq=1
networks:
DOCKER_NETWORK_DATA: #LIBREPORTAL|DOCKER_NETWORK_TAG|DOCKER_NETWORK_DATA
ipv4_address: IP_DATA_2 #LIBREPORTAL|IP_TAG_2|IP_DATA_2

View File

@ -18,7 +18,7 @@ services:
- PGID=1000
- TZ=TIMEZONE_DATA #LIBREPORTAL|TIMEZONE_TAG|TIMEZONE_DATA
# Guards the results page at /results/stats.php, same as before.
- PASSWORD=SPEEDTEST_PASSWORD_DATA #LIBREPORTAL|SPEEDTEST_PASSWORD_TAG|SPEEDTEST_PASSWORD_DATA
- PASSWORD=SPEEDTEST_PASSWORD_1_DATA #LIBREPORTAL|SPEEDTEST_PASSWORD_1_TAG|SPEEDTEST_PASSWORD_1_DATA
# Setting a DB type is what turns telemetry on. DB_NAME is deliberately
# left unset so the image picks its own path inside /config — guessing one
# risks a database that silently is not persisted by the volume below.

View File

@ -3,5 +3,5 @@
# App-specific compose tags for Speedtest Tracker.
appSetupComposeTags_speedtest() {
local full_file_path="$1"
tagsProcessorSpeedtestPass "$full_file_path" "$CFG_SPEEDTEST_PASSWORD_ENABLED" "$CFG_SPEEDTEST_PASSWORD"
tagsProcessorSpeedtestPass "$full_file_path" "$CFG_SPEEDTEST_PASSWORD_ENABLED" "$CFG_SPEEDTEST_PASSWORD_1"
}

View File

@ -25,7 +25,7 @@ CFG_SPEEDTEST_HEADSCALE=false
# PASSWORD = password protection for the speedtest interface (set to 'none' for no password)
#
CFG_SPEEDTEST_PASSWORD_ENABLED=true
CFG_SPEEDTEST_PASSWORD=RANDOMIZEDPASSWORD1
CFG_SPEEDTEST_PASSWORD_1=RANDOMIZEDPASSWORD1
#
# =============================================================================
# METADATA

View File

@ -53,7 +53,7 @@ services:
# Two annotations on one line — the tag manager scopes its sed per
# tag, so each placeholder needs its own marker (see ollama's
# OLLAMA_BASE_URL row for the same pattern).
- STALWART_RECOVERY_ADMIN=STALWART_ADMIN_USER_DATA:STALWART_ADMIN_PASSWORD_DATA #LIBREPORTAL|STALWART_ADMIN_USER_TAG|STALWART_ADMIN_USER_DATA #LIBREPORTAL|STALWART_ADMIN_PASSWORD_TAG|STALWART_ADMIN_PASSWORD_DATA
- STALWART_RECOVERY_ADMIN=STALWART_ADMIN_USER_DATA:STALWART_ADMIN_PASSWORD_1_DATA #LIBREPORTAL|STALWART_ADMIN_USER_TAG|STALWART_ADMIN_USER_DATA #LIBREPORTAL|STALWART_ADMIN_PASSWORD_1_TAG|STALWART_ADMIN_PASSWORD_1_DATA
labels:
libreportal.category: "CATEGORY_DATA" #LIBREPORTAL|CATEGORY_TAG|CATEGORY_DATA
libreportal.title: "TITLE_DATA" #LIBREPORTAL|TITLE_TAG|TITLE_DATA

View File

@ -14,7 +14,7 @@
# by the caller into positional args: user pass).
stalwart_install_message_data()
{
printf '%s %s' "${CFG_STALWART_ADMIN_USER:-admin}" "${CFG_STALWART_ADMIN_PASSWORD:-}"
printf '%s %s' "${CFG_STALWART_ADMIN_USER:-admin}" "${CFG_STALWART_ADMIN_PASSWORD_1:-}"
}
# Run the Stalwart CLI against our own container.
@ -32,7 +32,7 @@ stalwart_cli()
runFileOp docker run --rm -i --network "container:stalwart-service" \
-e STALWART_URL="http://localhost:8080" \
-e STALWART_USER="${CFG_STALWART_ADMIN_USER:-admin}" \
-e STALWART_PASSWORD="${CFG_STALWART_ADMIN_PASSWORD:-}" \
-e STALWART_PASSWORD="${CFG_STALWART_ADMIN_PASSWORD_1:-}" \
"ghcr.io/stalwartlabs/cli:${CFG_STALWART_CLI_VERSION:-1.0.12}" --no-color "$@"
}
@ -185,7 +185,7 @@ stalwart_install_first_mailbox()
# slot, not a list. Stalwart enforces password strength, so the password has
# to be a generated one, not a short hand-picked string.
plan=$(printf '{"@type":"upsert","object":"Domain","matchOn":["name"],"value":{"dom":{"name":"%s"}}}\n{"@type":"upsert","object":"Account","matchOn":["name"],"value":{"acc":{"@type":"User","name":"%s","domainId":"#dom","description":"First mailbox","credentials":{"0":{"@type":"Password","secret":"%s"}}}}}' \
"$mail_domain" "$mailbox" "${CFG_STALWART_FIRST_MAILBOX_PASSWORD:-}")
"$mail_domain" "$mailbox" "${CFG_STALWART_FIRST_MAILBOX_PASSWORD_1:-}")
if printf '%s\n' "$plan" | stalwart_cli apply --stdin >/dev/null 2>&1; then
isSuccessful "Created the first mailbox: ${mailbox}@${mail_domain}"

View File

@ -97,7 +97,7 @@ CFG_STALWART_PORT_9="stalwart-service|jmap|443:443|disabled|tcp|false|false|fals
# silently mangle the value. The generator only emits letters and digits.
CFG_STALWART_AUTH_PROFILE=user_password
CFG_STALWART_ADMIN_USER=admin
CFG_STALWART_ADMIN_PASSWORD=RANDOMIZEDPASSWORD1
CFG_STALWART_ADMIN_PASSWORD_1=RANDOMIZEDPASSWORD1
#
# =============================================================================
# AUTOMATIC SETUP
@ -122,7 +122,7 @@ CFG_STALWART_CLI_VERSION=1.0.12
# postmaster@<domain>, and if nothing creates it those reports bounce.
# Set empty to skip creating it.
CFG_STALWART_FIRST_MAILBOX=postmaster
CFG_STALWART_FIRST_MAILBOX_PASSWORD=RANDOMIZEDPASSWORD2
CFG_STALWART_FIRST_MAILBOX_PASSWORD_1=RANDOMIZEDPASSWORD2
#
# DNS_PROVIDER = 'manual', or a provider name to let Stalwart publish DNS itself.
# On 'manual' the installer prints the full record set — including the real

View File

@ -43,6 +43,12 @@ services:
- ./data/db:/data/db
environment:
- TZ=TIMEZONE_DATA #LIBREPORTAL|TIMEZONE_TAG|TIMEZONE_DATA
# Required on Linux 6.19 and newer — see SERVER-121912. MongoDB 8.x
# bundles a TCMalloc whose restartable-sequence registration the
# newer kernel rejects, and mongod aborts on startup rather than
# degrading. Letting glibc own the rseq registration sidesteps it.
# Upstream Stoat hit the same wall (stoatchat/self-hosted#268).
- GLIBC_TUNABLES=glibc.pthread.rseq=1
healthcheck:
test: echo 'db.runCommand("ping").ok' | mongosh localhost:27017/test --quiet
interval: 10s
@ -72,7 +78,7 @@ services:
environment:
- TZ=TIMEZONE_DATA #LIBREPORTAL|TIMEZONE_TAG|TIMEZONE_DATA
- RABBITMQ_DEFAULT_USER=stoat
- RABBITMQ_DEFAULT_PASS=STOAT_RABBITMQ_PASSWORD_DATA #LIBREPORTAL|STOAT_RABBITMQ_PASSWORD_TAG|STOAT_RABBITMQ_PASSWORD_DATA
- RABBITMQ_DEFAULT_PASS=STOAT_RABBITMQ_PASSWORD_1_DATA #LIBREPORTAL|STOAT_RABBITMQ_PASSWORD_1_TAG|STOAT_RABBITMQ_PASSWORD_1_DATA
volumes:
- ./data/rabbit:/var/lib/rabbitmq
healthcheck:
@ -100,7 +106,7 @@ services:
environment:
- TZ=TIMEZONE_DATA #LIBREPORTAL|TIMEZONE_TAG|TIMEZONE_DATA
- MINIO_ROOT_USER=stoatminio
- MINIO_ROOT_PASSWORD=STOAT_MINIO_PASSWORD_DATA #LIBREPORTAL|STOAT_MINIO_PASSWORD_TAG|STOAT_MINIO_PASSWORD_DATA
- MINIO_ROOT_PASSWORD=STOAT_MINIO_PASSWORD_1_DATA #LIBREPORTAL|STOAT_MINIO_PASSWORD_1_TAG|STOAT_MINIO_PASSWORD_1_DATA
- MINIO_DOMAIN=minio
networks:
DOCKER_NETWORK_DATA: #LIBREPORTAL|DOCKER_NETWORK_TAG|DOCKER_NETWORK_DATA
@ -131,7 +137,7 @@ services:
# leaves it for the container's shell instead of interpolating it here.
environment:
- MC_USER=stoatminio
- MC_PASS=STOAT_MINIO_PASSWORD_DATA #LIBREPORTAL|STOAT_MINIO_PASSWORD_TAG|STOAT_MINIO_PASSWORD_DATA
- MC_PASS=STOAT_MINIO_PASSWORD_1_DATA #LIBREPORTAL|STOAT_MINIO_PASSWORD_1_TAG|STOAT_MINIO_PASSWORD_1_DATA
entrypoint: >
/bin/sh -c "
while ! /usr/bin/mc ready minio; do

View File

@ -38,8 +38,8 @@ CFG_STOAT_MONITORING=false
# its user into ./data/rabbit on first boot and ignores the env afterwards, so a
# regenerated password would leave the broker unreachable. MinIO's root password
# is shared by the object store and the bucket-creation job, which have to agree.
CFG_STOAT_RABBITMQ_PASSWORD=RANDOMIZEDPASSWORD1
CFG_STOAT_MINIO_PASSWORD=RANDOMIZEDPASSWORD2
CFG_STOAT_RABBITMQ_PASSWORD_1=RANDOMIZEDPASSWORD1
CFG_STOAT_MINIO_PASSWORD_1=RANDOMIZEDPASSWORD2
#
# =============================================================================
# METADATA

View File

@ -88,4 +88,4 @@ CFG_TRAEFIK_PORT_3="traefik-service|http|80:80|disabled|tcp|false|false|false|HT
# AUTH_PROFILE = capability tier for the WebUI auth tools (single_password | user_password | multi_user)
CFG_TRAEFIK_AUTH_PROFILE=single_password
CFG_TRAEFIK_ADMIN_USER=
CFG_TRAEFIK_ADMIN_PASSWORD=RANDOMIZEDPASSWORD1
CFG_TRAEFIK_ADMIN_PASSWORD_1=RANDOMIZEDPASSWORD1

View File

@ -20,7 +20,7 @@ services:
- LOGIN_RATELIMIT_MAX_BURST=10
- LOGIN_RATELIMIT_SECONDS=60
- DOMAIN=https://DOMAINSUBNAME_DATA #LIBREPORTAL|DOMAINSUBNAME_TAG|DOMAINSUBNAME_DATA
- ADMIN_TOKEN=VAULTWARDEN_ADMIN_TOKEN_DATA #LIBREPORTAL|VAULTWARDEN_ADMIN_TOKEN_TAG|VAULTWARDEN_ADMIN_TOKEN_DATA
- ADMIN_TOKEN=VAULTWARDEN_ADMIN_TOKEN_1_DATA #LIBREPORTAL|VAULTWARDEN_ADMIN_TOKEN_1_TAG|VAULTWARDEN_ADMIN_TOKEN_1_DATA
- SIGNUPS_ALLOWED=VAULTWARDEN_SIGNUPS_ALLOWED_DATA #LIBREPORTAL|VAULTWARDEN_SIGNUPS_ALLOWED_TAG|VAULTWARDEN_SIGNUPS_ALLOWED_DATA
labels:
libreportal.category: "CATEGORY_DATA" #LIBREPORTAL|CATEGORY_TAG|CATEGORY_DATA
@ -56,7 +56,7 @@ services:
# - vaultwarden-service
# environment:
# - VW_URL=http://vaultwarden-service:PORT_INTERNAL_DATA_1 #LIBREPORTAL|PORT_INTERNAL_TAG_1|PORT_INTERNAL_DATA_1
# - VW_TOKEN=VAULTWARDEN_ADMIN_TOKEN_DATA #LIBREPORTAL|VAULTWARDEN_ADMIN_TOKEN_TAG|VAULTWARDEN_ADMIN_TOKEN_DATA
# - VW_TOKEN=VAULTWARDEN_ADMIN_TOKEN_1_DATA #LIBREPORTAL|VAULTWARDEN_ADMIN_TOKEN_1_TAG|VAULTWARDEN_ADMIN_TOKEN_1_DATA
# - VW_LISTEN_ADDR=:PORT_INTERNAL_DATA_2 #LIBREPORTAL|PORT_INTERNAL_TAG_2|PORT_INTERNAL_DATA_2
# networks:
# DOCKER_NETWORK_DATA: #LIBREPORTAL|DOCKER_NETWORK_TAG|DOCKER_NETWORK_DATA

View File

@ -3,6 +3,6 @@
# App-specific compose tags for Vaultwarden.
appSetupComposeTags_vaultwarden() {
local full_file_path="$1"
tagsManagerUpdateUniversalTag "$full_file_path" "VAULTWARDEN_ADMIN_TOKEN_TAG" "$CFG_VAULTWARDEN_ADMIN_TOKEN"
tagsManagerUpdateUniversalTag "$full_file_path" "VAULTWARDEN_ADMIN_TOKEN_1_TAG" "$CFG_VAULTWARDEN_ADMIN_TOKEN_1"
tagsManagerUpdateUniversalTag "$full_file_path" "VAULTWARDEN_SIGNUPS_ALLOWED_TAG" "$CFG_VAULTWARDEN_SIGNUPS_ALLOWED"
}

View File

@ -27,7 +27,7 @@ CFG_VAULTWARDEN_MONITORING=false
# ADMIN_TOKEN = admin token for accessing the admin interface (set to 'none' to disable)
# SIGNUPS_ALLOWED = if true, new user registrations are allowed
#
CFG_VAULTWARDEN_ADMIN_TOKEN=RANDOMIZEDPASSWORD1
CFG_VAULTWARDEN_ADMIN_TOKEN_1=RANDOMIZEDPASSWORD1
CFG_VAULTWARDEN_SIGNUPS_ALLOWED=true
#
# =============================================================================

View File

@ -9,7 +9,7 @@ services:
restart: unless-stopped
hostname: wireguard
environment:
- PASSWORD_HASH=WIREGUARD_PASSWORD_DATA #LIBREPORTAL|WIREGUARD_PASSWORD_TAG|WIREGUARD_PASSWORD_DATA
- PASSWORD_HASH=WIREGUARD_PASSWORD_1_DATA #LIBREPORTAL|WIREGUARD_PASSWORD_1_TAG|WIREGUARD_PASSWORD_1_DATA
- WG_HOST=PUBLIC_IP_DATA #LIBREPORTAL|PUBLIC_IP_TAG|PUBLIC_IP_DATA
- WG_DEFAULT_ADDRESS=WIREGUARD_SUBNET_DATA #LIBREPORTAL|WIREGUARD_SUBNET_TAG|WIREGUARD_SUBNET_DATA
- WG_MTU=NETWORK_MTU_DATA #LIBREPORTAL|NETWORK_MTU_TAG|NETWORK_MTU_DATA

View File

@ -4,6 +4,6 @@
appSetupComposeTags_wireguard() {
local full_file_path="$1"
local bcrypt_hash
bcrypt_hash=$(hashPassword "$CFG_WIREGUARD_PASSWORD")
tagsManagerUpdateUniversalTag "$full_file_path" "WIREGUARD_PASSWORD_TAG" "$bcrypt_hash"
bcrypt_hash=$(hashPassword "$CFG_WIREGUARD_PASSWORD_1")
tagsManagerUpdateUniversalTag "$full_file_path" "WIREGUARD_PASSWORD_1_TAG" "$bcrypt_hash"
}

View File

@ -31,7 +31,7 @@ CFG_WIREGUARD_MONITORING=false
# WG_MTU = MTU size for VPN connections
# WG_ALLOWED_IPS = allowed IP ranges for VPN clients
#
CFG_WIREGUARD_PASSWORD=RANDOMIZEDPASSWORD1
CFG_WIREGUARD_PASSWORD_1=RANDOMIZEDPASSWORD1
#
# =============================================================================
# METADATA

View File

@ -156,13 +156,23 @@ initialised with. That is the same desync the old mechanism caused on every
reinstall; the difference is that it now happens at most once, because the values
are preserved from then on.
### Database password keys are now numbered slots
### Every generated secret is now a numbered slot
`CFG_<APP>_DB_PASSWORD` became `CFG_<APP>_DB_PASSWORD_1`, and
`CFG_<APP>_DB_ROOT_PASSWORD` became `CFG_<APP>_DB_ROOT_PASSWORD_1`, so a database
credential is always a numbered slot and an app that needs a second one just adds
`_2`. This also touches **nextcloud** and **bookstack**, which were otherwise
unaffected by the change above.
The convention is now uniform: **if a config key holds a generated value, its name
ends in a slot number.** `CFG_<APP>_DB_PASSWORD` became
`CFG_<APP>_DB_PASSWORD_1`, `CFG_TRAEFIK_ADMIN_PASSWORD` became
`CFG_TRAEFIK_ADMIN_PASSWORD_1`, and so on — 42 keys across the catalog. An app
that needs a second credential of the same kind just adds `_2`; nothing has to be
registered, because the tag name is derived from the key.
Keys holding a value you chose rather than one the installer generated (say
`CFG_NEXTCLOUD_ADMIN_USER=admin`) are unchanged — the slot number is what marks a
value as generated.
This reaches apps that had nothing to do with the compose-side generator problem
above, including **adguard, authelia, bookstack, gitea, gluetun, headscale,
invidious, moneyapp, nextcloud, pihole, speedtest, stalwart, traefik, vaultwarden**
and **wireguard**.
A rename is a delete plus an add as far as config reconciliation is concerned: the
old key is dropped and the new one arrives holding its `RANDOMIZED*` placeholder,
@ -172,10 +182,14 @@ reinstalling — reconciliation leaves the previous file as a hidden
`.<app>.config.bak` sibling, so the old value is still there:
```bash
grep DB_PASSWORD <containers-dir>/<app>/.<app>.config.bak
grep -E '=RANDOMIZED|PASSWORD|SECRET|TOKEN|KEY' <containers-dir>/<app>/.<app>.config.bak
```
Paste each value into the matching `_1` key in `<containers-dir>/<app>/<app>.config`.
For a Nextcloud install that means `CFG_NEXTCLOUD_ADMIN_PASSWORD_1`,
`CFG_NEXTCLOUD_DB_PASSWORD_1` and `CFG_NEXTCLOUD_DB_ROOT_PASSWORD_1` — the two
database ones are the pair that matters, because MariaDB will keep rejecting a
regenerated password.
### Mastodon web push

View File

@ -4,7 +4,7 @@
#
# Fills #LIBREPORTAL|<APP>_<KEY>_TAG|... tags in an app's compose from the
# matching CFG_<APP>_<KEY> variable. The tag name is derived mechanically from
# the config key (CFG_GITEA_METRICS_TOKEN -> GITEA_METRICS_TOKEN_TAG), so there
# the config key (CFG_GITEA_METRICS_TOKEN_1 -> GITEA_METRICS_TOKEN_1_TAG), so there
# is no hand-maintained list to drift out of sync — any value an app needs in
# its compose just works by adding the CFG_ key plus the matching tag.
#

View File

@ -15,8 +15,8 @@ tagsProcessorSpeedtestPass()
# match indented YAML), we just write the right value: the actual
# password when enabled, empty when disabled.
if [[ "$password_enabled" == "true" ]]; then
tagsManagerUpdateUniversalTag "$full_file_path" "SPEEDTEST_PASSWORD_TAG" "$password_value"
tagsManagerUpdateUniversalTag "$full_file_path" "SPEEDTEST_PASSWORD_1_TAG" "$password_value"
else
tagsManagerUpdateUniversalTag "$full_file_path" "SPEEDTEST_PASSWORD_TAG" ""
tagsManagerUpdateUniversalTag "$full_file_path" "SPEEDTEST_PASSWORD_1_TAG" ""
fi
}

View File

@ -71,7 +71,7 @@ localDnsAppHosts() {
localDnsApplyAdguard() {
local ip="$1"; shift
local user="${CFG_ADGUARD_ADMIN_USER:-admin}"
local pass="${CFG_ADGUARD_ADMIN_PASSWORD:-${CFG_ADGUARD_PASSWORD:-}}"
local pass="${CFG_ADGUARD_ADMIN_PASSWORD_1:-${CFG_ADGUARD_PASSWORD_1:-}}"
local hostport base d entry payload
hostport=$(dockerCommandRun "docker port adguard-service 3000/tcp" 2>/dev/null | head -1 | sed 's/.*://')
if [[ -z "$hostport" ]]; then

View File

@ -259,13 +259,13 @@ PORTEOF
"type": "password",
"tooltip": "API key for AbuseIPDB integration"
},
"PIHOLE_ADMIN_PASSWORD": {
"PIHOLE_ADMIN_PASSWORD_1": {
"category": "general",
"label": "Pi-hole Admin Password",
"type": "password",
"tooltip": "Password for accessing the Pi-hole admin interface"
},
"WIREGUARD_PASSWORD": {
"WIREGUARD_PASSWORD_1": {
"category": "general",
"label": "WireGuard Admin Password",
"type": "password",
@ -296,19 +296,19 @@ PORTEOF
"tooltip": "Enable password protection for the speedtest interface",
"default": false
},
"SPEEDTEST_PASSWORD": {
"SPEEDTEST_PASSWORD_1": {
"category": "general",
"label": "Speedtest Password",
"type": "password",
"tooltip": "Password for accessing the speedtest interface (when enabled)"
},
"OWNCLOUD_ADMIN_USERNAME": {
"OWNCLOUD_ADMIN_USERNAME_1": {
"category": "general",
"label": "OwnCloud Admin Username",
"type": "text",
"tooltip": "Username for the OwnCloud administrator account"
},
"OWNCLOUD_ADMIN_PASSWORD": {
"OWNCLOUD_ADMIN_PASSWORD_1": {
"category": "general",
"label": "OwnCloud Admin Password",
"type": "password",
@ -321,7 +321,7 @@ PORTEOF
"tooltip": "Username seeded into Nextcloud on first boot",
"default": "admin"
},
"NEXTCLOUD_ADMIN_PASSWORD": {
"NEXTCLOUD_ADMIN_PASSWORD_1": {
"category": "general",
"label": "Nextcloud Admin Password",
"type": "password",
@ -357,14 +357,14 @@ PORTEOF
"tooltip": "MariaDB root password (auto-generated; persists across reinstalls)",
"advanced": true
},
"MASTODON_DB_NAME": {
"MASTODON_DB_NAME_1": {
"category": "advanced",
"label": "Database Name",
"type": "text",
"tooltip": "Postgres database Mastodon uses (internal to the docker network)",
"advanced": true
},
"MASTODON_DB_USER": {
"MASTODON_DB_USER_1": {
"category": "advanced",
"label": "Database User",
"type": "text",
@ -378,42 +378,42 @@ PORTEOF
"tooltip": "Postgres password for the Mastodon role (auto-generated; persists across reinstalls)",
"advanced": true
},
"MASTODON_SECRET_KEY_BASE": {
"MASTODON_SECRET_KEY_BASE_1": {
"category": "advanced",
"label": "Secret Key Base",
"type": "password",
"tooltip": "Signs and encrypts session cookies (auto-generated). Changing it logs every user out.",
"advanced": true
},
"MASTODON_OTP_SECRET": {
"MASTODON_OTP_SECRET_1": {
"category": "advanced",
"label": "OTP Secret",
"type": "password",
"tooltip": "Protects stored two-factor enrolments (auto-generated). Changing it resets everyone's 2FA.",
"advanced": true
},
"MASTODON_VAPID_PRIVATE_KEY": {
"MASTODON_VAPID_PRIVATE_KEY_1": {
"category": "advanced",
"label": "VAPID Private Key",
"type": "password",
"tooltip": "Web Push identity (auto-generated). Changing it makes browsers re-subscribe.",
"advanced": true
},
"MASTODON_VAPID_PUBLIC_KEY": {
"MASTODON_VAPID_PUBLIC_KEY_1": {
"category": "advanced",
"label": "VAPID Public Key",
"type": "text",
"tooltip": "Web Push identity (auto-generated). Changing it makes browsers re-subscribe.",
"advanced": true
},
"OWNCLOUD_DB_NAME": {
"OWNCLOUD_DB_NAME_1": {
"category": "advanced",
"label": "Database Name",
"type": "text",
"tooltip": "MariaDB schema ownCloud uses (internal to the docker network)",
"advanced": true
},
"OWNCLOUD_DB_USER": {
"OWNCLOUD_DB_USER_1": {
"category": "advanced",
"label": "Database User",
"type": "text",
@ -448,21 +448,111 @@ PORTEOF
"tooltip": "Postgres password for the synapse role (auto-generated; persists across reinstalls)",
"advanced": true
},
"STOAT_RABBITMQ_PASSWORD": {
"STOAT_RABBITMQ_PASSWORD_1": {
"category": "advanced",
"label": "RabbitMQ Password",
"type": "password",
"tooltip": "Broker password (auto-generated; persists across reinstalls)",
"advanced": true
},
"STOAT_MINIO_PASSWORD": {
"STOAT_MINIO_PASSWORD_1": {
"category": "advanced",
"label": "MinIO Root Password",
"type": "password",
"tooltip": "Object store root password (auto-generated), shared by MinIO and the bucket-creation job.",
"advanced": true
},
"VAULTWARDEN_ADMIN_TOKEN": {
"ADGUARD_ADMIN_PASSWORD_1": {
"category": "general",
"label": "AdGuard Admin Password",
"type": "password",
"tooltip": "Password for the AdGuard Home admin account (auto-generated; persists across reinstalls)"
},
"GITEA_ADMIN_PASSWORD_1": {
"category": "general",
"label": "Gitea Admin Password",
"type": "password",
"tooltip": "Password for the Gitea administrator account (auto-generated; persists across reinstalls)"
},
"GITEA_METRICS_TOKEN_1": {
"category": "advanced",
"label": "Metrics Token",
"type": "password",
"tooltip": "Bearer token Prometheus uses to scrape Gitea's metrics endpoint (auto-generated)",
"advanced": true
},
"HEADSCALE_BASIC_AUTH_PASS_1": {
"category": "general",
"label": "Basic Auth Password",
"type": "password",
"tooltip": "Password protecting the Headscale endpoint (auto-generated; persists across reinstalls)"
},
"INVIDIOUS_ADMIN_PASSWORD_1": {
"category": "general",
"label": "Invidious Admin Password",
"type": "password",
"tooltip": "Password for the Invidious administrator account (auto-generated; persists across reinstalls)"
},
"INVIDIOUS_HMAC_KEY_1": {
"category": "advanced",
"label": "HMAC Key",
"type": "password",
"tooltip": "Signs Invidious session tokens (auto-generated). Changing it logs every user out.",
"advanced": true
},
"MATRIX_ADMIN_PASSWORD_1": {
"category": "general",
"label": "Matrix Admin Password",
"type": "password",
"tooltip": "Password for the first Synapse admin account, created on install (auto-generated)"
},
"MONEYAPP_AUTH_SECRET_1": {
"category": "advanced",
"label": "Auth Secret",
"type": "password",
"tooltip": "Signs session tokens (auto-generated). Changing it logs every user out.",
"advanced": true
},
"STALWART_ADMIN_PASSWORD_1": {
"category": "general",
"label": "Stalwart Admin Password",
"type": "password",
"tooltip": "Password for the Stalwart administrator account (auto-generated; persists across reinstalls)"
},
"STALWART_FIRST_MAILBOX_PASSWORD_1": {
"category": "general",
"label": "First Mailbox Password",
"type": "password",
"tooltip": "Password for the mailbox created on install (auto-generated; persists across reinstalls)"
},
"TRAEFIK_ADMIN_PASSWORD_1": {
"category": "general",
"label": "Traefik Admin Password",
"type": "password",
"tooltip": "Password for the Traefik dashboard account (auto-generated; persists across reinstalls)"
},
"BOOKSTACK_APP_KEY_1": {
"category": "advanced",
"label": "Application Key",
"type": "password",
"tooltip": "Laravel APP_KEY — encrypts session data (auto-generated). Changing it invalidates existing sessions.",
"advanced": true
},
"BOOKSTACK_DB_PASSWORD_1": {
"category": "advanced",
"label": "Database Password",
"type": "password",
"tooltip": "MariaDB password for the BookStack user (auto-generated; persists across reinstalls)",
"advanced": true
},
"BOOKSTACK_DB_ROOT_PASSWORD_1": {
"category": "advanced",
"label": "Database Root Password",
"type": "password",
"tooltip": "MariaDB root password (auto-generated; kept separate from the app user password)",
"advanced": true
},
"VAULTWARDEN_ADMIN_TOKEN_1": {
"category": "general",
"label": "Vaultwarden Admin Token",
"type": "password",
@ -482,7 +572,7 @@ PORTEOF
"tooltip": "Username for the AdGuardHome admin web UI",
"default": "admin"
},
"ADGUARD_PASSWORD": {
"ADGUARD_PASSWORD_1": {
"category": "general",
"label": "AdGuard Admin Password",
"type": "password",
@ -495,7 +585,7 @@ PORTEOF
"tooltip": "Email used for the Bookstack admin account",
"default": "admin@admin.com"
},
"BOOKSTACK_ADMIN_PASSWORD": {
"BOOKSTACK_ADMIN_PASSWORD_1": {
"category": "general",
"label": "Bookstack Admin Password",
"type": "password",
@ -508,13 +598,13 @@ PORTEOF
"tooltip": "Username for the seeded Authelia admin account",
"default": "admin"
},
"AUTHELIA_ADMIN_PASSWORD": {
"AUTHELIA_ADMIN_PASSWORD_1": {
"category": "general",
"label": "Authelia Admin Password",
"type": "password",
"tooltip": "Password for the seeded Authelia admin account"
},
"GLUETUN_CONTROL_SERVER_API_KEY": {
"GLUETUN_CONTROL_SERVER_API_KEY_1": {
"category": "general",
"label": "Control Server API Key",
"type": "password",