From be2944dda11dcfe1b08d031ce24f176b7de4bda9 Mon Sep 17 00:00:00 2001 From: Andreas Dueren Date: Thu, 15 Jan 2026 16:38:10 -0600 Subject: [PATCH] Initial mautrix-signal Cloudron package --- CHANGELOG.md | 11 ++ CloudronManifest.json | 32 ++++++ DESCRIPTION.md | 24 ++++ Dockerfile.cloudron | 44 ++++++++ README.md | 95 +++++++++++++++- logo.png | Bin 0 -> 51283 bytes start.sh | 255 ++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 460 insertions(+), 1 deletion(-) create mode 100644 CHANGELOG.md create mode 100644 CloudronManifest.json create mode 100644 DESCRIPTION.md create mode 100644 Dockerfile.cloudron create mode 100644 logo.png create mode 100644 start.sh diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..51fb21d --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,11 @@ +# Changelog + +## 1.0.0 - 2026-01-15 + +- Initial Cloudron package release +- Based on mautrix-signal v0.2512.0 +- PostgreSQL database support +- Automatic configuration for Cloudron environment +- End-to-bridge encryption support +- Cleanup on logout functionality +- Health check endpoint diff --git a/CloudronManifest.json b/CloudronManifest.json new file mode 100644 index 0000000..763cb06 --- /dev/null +++ b/CloudronManifest.json @@ -0,0 +1,32 @@ +{ + "version": "1.0.0", + "upstreamVersion": "0.2512.0", + "id": "dev.maunium.signal.cloudronapp", + "title": "Matrix Signal Bridge", + "author": "Tulir Asokan ", + "description": "file://DESCRIPTION.md", + "tagline": "Bridge between Matrix and Signal", + "healthCheckPath": "/health", + "httpPort": 29328, + "icon": "logo.png", + "addons": { + "localstorage": {}, + "postgresql": {}, + "tls": {} + }, + "manifestVersion": 2, + "website": "https://docs.mau.fi/bridges/go/signal/index.html", + "contactEmail": "support@cloudron.io", + "tags": [ + "matrix", + "signal", + "chat", + "bridge", + "communication" + ], + "minBoxVersion": "7.1.0", + "postInstallMessage": "Mautrix-Signal bridge is installed!\n\nPlease note you will need to:\n1. Configure your homeserver by setting up the registration\n2. Configure the bridge in the data directory\n3. Link your Signal account using QR code or phone number\n\nVisit https://docs.mau.fi/bridges/go/signal/index.html for detailed instructions.", + "changelog": "file://CHANGELOG.md", + "documentationUrl": "https://docs.mau.fi/bridges/go/signal/index.html", + "forumUrl": "https://matrix.to/#/#signal:maunium.net" +} diff --git a/DESCRIPTION.md b/DESCRIPTION.md new file mode 100644 index 0000000..679ade5 --- /dev/null +++ b/DESCRIPTION.md @@ -0,0 +1,24 @@ +# Matrix Signal Bridge + +A Matrix-Signal puppeting bridge that allows you to use Signal Messenger through Matrix. + +## Features + +- **Two-way messaging**: Send and receive messages between Matrix and Signal +- **Media support**: Share images, videos, documents, and other files +- **Group chats**: Bridge Signal groups to Matrix rooms +- **Reactions**: React to messages across platforms +- **Read receipts**: Sync read status between Matrix and Signal +- **Typing indicators**: See when someone is typing +- **End-to-bridge encryption**: Optional encryption for messages between Matrix and the bridge + +## Setup + +After installation: + +1. Copy the registration file from `/app/data/registration.yaml` to your Matrix homeserver +2. Add the registration to your homeserver configuration and restart it +3. Start a chat with `@signalbot:yourdomain.com` in Matrix +4. Follow the authentication instructions to link your Signal account + +For detailed setup instructions, visit the [official documentation](https://docs.mau.fi/bridges/go/signal/index.html). diff --git a/Dockerfile.cloudron b/Dockerfile.cloudron new file mode 100644 index 0000000..51e05f5 --- /dev/null +++ b/Dockerfile.cloudron @@ -0,0 +1,44 @@ +FROM cloudron/base:5.0.0@sha256:04fd70dbd8ad6149c19de39e35718e024417c3e01dc9c6637eaf4a41ec4e596c + +# Install dependencies +RUN apt-get update && apt-get install -y \ + curl \ + ca-certificates \ + netcat-openbsd \ + bash \ + jq \ + yq \ + git \ + build-essential \ + libolm-dev \ + gosu \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +# Install Go +ENV GO_VERSION=1.24.0 +RUN curl -LO https://golang.org/dl/go${GO_VERSION}.linux-amd64.tar.gz \ + && tar -C /usr/local -xzf go${GO_VERSION}.linux-amd64.tar.gz \ + && rm go${GO_VERSION}.linux-amd64.tar.gz +ENV PATH="/usr/local/go/bin:$PATH" + +# Create app directories +RUN mkdir -p /app/code /app/pkg /app/data +WORKDIR /app/code + +# Download and build mautrix-signal +RUN git clone --branch v0.2512.0 https://github.com/mautrix/signal.git . \ + && go build -o /app/pkg/mautrix-signal ./cmd/mautrix-signal + +# Copy startup script +COPY start.sh /app/pkg/ + +# Set volumes and environment +VOLUME /app/data +ENV UID=1337 GID=1337 + +# Add health check endpoint - check if the appservice port is responding +HEALTHCHECK --interval=60s --timeout=10s --start-period=30s --retries=3 \ + CMD nc -z localhost 29328 || exit 1 + +CMD ["/app/pkg/start.sh"] diff --git a/README.md b/README.md index e6e318c..295fb80 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,95 @@ -# mautrix-signal-cloudron +# Mautrix-Signal Bridge for Cloudron +This is a Cloudron package for the [mautrix-signal](https://github.com/mautrix/signal) Matrix bridge. + +## Features + +- Bridge between Matrix and Signal Messenger +- Two-way messaging with media support +- Group chat support +- End-to-bridge encryption +- Automatic Cloudron integration + +## Building + +Build the package using the Cloudron build service: + +```bash +cloudron build --set-build-service builder.docker.due.ren \ + --build-service-token e3265de06b1d0e7bb38400539012a8433a74c2c96a17955e \ + --set-repository andreasdueren/mautrix-signal \ + --tag 1.0.0 +``` + +## Installing + +Install on your Cloudron: + +```bash +cloudron install --location signal.matrix.due.ren \ + --image andreasdueren/mautrix-signal:1.0.0 +``` + +## Setup + +After installation: + +1. Access the app data directory to get the registration file: + ```bash + cloudron exec --app signal.matrix.due.ren + cat /app/data/registration.yaml + ``` + +2. Copy the registration file content to your Matrix homeserver's configuration directory + +3. Add the registration to your homeserver configuration (e.g., in Synapse's `homeserver.yaml`): + ```yaml + app_service_config_files: + - /path/to/signal-registration.yaml + ``` + +4. Restart your Matrix homeserver + +5. Start a chat with `@signalbot:due.ren` in your Matrix client + +6. Follow the authentication instructions to link your Signal account + +## Viewing Logs + +To view the application logs: + +```bash +cloudron logs --app signal.matrix.due.ren -f +``` + +## Configuration + +The bridge configuration is located at `/app/data/config.yaml` inside the app. You can edit it by: + +```bash +cloudron exec --app signal.matrix.due.ren +nano /app/data/config.yaml +``` + +After making changes, restart the app from the Cloudron dashboard. + +## Troubleshooting + +### Bridge not connecting to homeserver + +Check that: +- The registration file is correctly configured in your homeserver +- The homeserver has been restarted after adding the registration +- The app is running (check logs) + +### Authentication issues + +Make sure you're messaging `@signalbot:due.ren` (replace `due.ren` with your actual base domain). + +## Documentation + +For more information, visit the [official mautrix-signal documentation](https://docs.mau.fi/bridges/go/signal/index.html). + +## License + +This Cloudron package is licensed under the same terms as mautrix-signal (AGPL-3.0). diff --git a/logo.png b/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..d92688ef040508d17dcab8689852174834fc94d0 GIT binary patch literal 51283 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

%g-}>R%rffdlv(Z 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 N002ovPDHLkV1nZ9-GKlA literal 0 HcmV?d00001 diff --git a/start.sh b/start.sh new file mode 100644 index 0000000..8eeb726 --- /dev/null +++ b/start.sh @@ -0,0 +1,255 @@ +#!/bin/bash + +set -eu + +echo "=> Setting up mautrix-signal bridge" + +# Create necessary directories +mkdir -p /app/data +mkdir -p /app/data/logs + +# For Cloudron, we run as the cloudron user +UID=$(id -u cloudron) +GID=$(id -g cloudron) + +# Set ownership early to avoid permission issues +chown -R cloudron:cloudron /app/data + +# Configuration file paths +CONFIG_PATH="/app/data/config.yaml" +REGISTRATION_PATH="/app/data/registration.yaml" +BACKUP_PATH="/app/data/config.yaml.bak" + +# Try to generate config from built-in template +if [ ! -f "$CONFIG_PATH" ]; then + echo "=> Attempting to generate configuration using mautrix-signal" + # Run as cloudron user to avoid permission issues and change to data directory + cd /app/data + gosu cloudron:cloudron /app/pkg/mautrix-signal -e -c "$CONFIG_PATH" + + # Configure basic settings first before generating registration + if [ -f "$CONFIG_PATH" ]; then + echo "=> Applying basic Cloudron configuration to generated config" + + # Configure for Cloudron environment + if [ -n "${CLOUDRON_POSTGRESQL_URL:-}" ]; then + echo "=> Configuring PostgreSQL database: $CLOUDRON_POSTGRESQL_URL" + # Add SSL mode disable to Cloudron PostgreSQL URL if not already present + if [[ "$CLOUDRON_POSTGRESQL_URL" == *"sslmode="* ]]; then + DB_URL="$CLOUDRON_POSTGRESQL_URL" + else + DB_URL="$CLOUDRON_POSTGRESQL_URL?sslmode=disable" + fi + yq -i -y '.database.uri = "'"$DB_URL"'"' "$CONFIG_PATH" || echo "=> ERROR: Could not configure database" + fi + + if [ -n "${CLOUDRON_APP_DOMAIN:-}" ]; then + echo "=> Configuring homeserver and appservice settings for domain: $CLOUDRON_APP_DOMAIN" + # Extract base domain (e.g., signal.matrix.due.ren -> due.ren) + BASE_DOMAIN=$(echo "$CLOUDRON_APP_DOMAIN" | rev | cut -d. -f1-2 | rev) + echo "=> Base domain extracted: $BASE_DOMAIN" + + # Update homeserver configuration + yq -i -y '.homeserver.address = "https://matrix.'"$BASE_DOMAIN"'"' "$CONFIG_PATH" || echo "=> ERROR: Could not configure homeserver address" + yq -i -y '.homeserver.domain = "'"$BASE_DOMAIN"'"' "$CONFIG_PATH" || echo "=> ERROR: Could not configure homeserver domain" + + # Update appservice configuration + yq -i -y '.appservice.address = "https://'"$CLOUDRON_APP_DOMAIN"'"' "$CONFIG_PATH" || echo "=> ERROR: Could not configure appservice address" + yq -i -y '.appservice.public_address = "https://'"$CLOUDRON_APP_DOMAIN"'"' "$CONFIG_PATH" || echo "=> ERROR: Could not configure appservice public_address" + yq -i -y '.appservice.hostname = "0.0.0.0"' "$CONFIG_PATH" || echo "=> ERROR: Could not configure appservice hostname" + yq -i -y '.appservice.port = 29328' "$CONFIG_PATH" || echo "=> ERROR: Could not configure appservice port" + yq -i -y '.appservice.bot_username = "signalbot"' "$CONFIG_PATH" || echo "=> ERROR: Could not configure appservice bot_username" + + # Configure permissions for base domain users + yq -i -y '.bridge.permissions."'"$BASE_DOMAIN"'" = "user"' "$CONFIG_PATH" || echo "=> ERROR: Could not configure permissions" + + # Configure cleanup on logout to delete everything + yq -i -y '.bridge.cleanup_on_logout.enabled = true' "$CONFIG_PATH" || echo "=> ERROR: Could not configure cleanup enabled" + yq -i -y '.bridge.cleanup_on_logout.manual.private = "delete"' "$CONFIG_PATH" || echo "=> ERROR: Could not configure cleanup manual private" + yq -i -y '.bridge.cleanup_on_logout.manual.relayed = "delete"' "$CONFIG_PATH" || echo "=> ERROR: Could not configure cleanup manual relayed" + yq -i -y '.bridge.cleanup_on_logout.manual.shared_no_users = "delete"' "$CONFIG_PATH" || echo "=> ERROR: Could not configure cleanup manual shared_no_users" + yq -i -y '.bridge.cleanup_on_logout.manual.shared_has_users = "delete"' "$CONFIG_PATH" || echo "=> ERROR: Could not configure cleanup manual shared_has_users" + yq -i -y '.bridge.cleanup_on_logout.bad_credentials.private = "delete"' "$CONFIG_PATH" || echo "=> ERROR: Could not configure cleanup bad_credentials private" + yq -i -y '.bridge.cleanup_on_logout.bad_credentials.relayed = "delete"' "$CONFIG_PATH" || echo "=> ERROR: Could not configure cleanup bad_credentials relayed" + yq -i -y '.bridge.cleanup_on_logout.bad_credentials.shared_no_users = "delete"' "$CONFIG_PATH" || echo "=> ERROR: Could not configure cleanup bad_credentials shared_no_users" + yq -i -y '.bridge.cleanup_on_logout.bad_credentials.shared_has_users = "delete"' "$CONFIG_PATH" || echo "=> ERROR: Could not configure cleanup bad_credentials shared_has_users" + + # Configure end-to-bridge encryption with best practices + yq -i -y '.encryption.allow = true' "$CONFIG_PATH" || echo "=> ERROR: Could not configure encryption allow" + yq -i -y '.encryption.default = false' "$CONFIG_PATH" || echo "=> ERROR: Could not configure encryption default" + yq -i -y '.encryption.require = false' "$CONFIG_PATH" || echo "=> ERROR: Could not configure encryption require" + yq -i -y '.encryption.appservice = false' "$CONFIG_PATH" || echo "=> ERROR: Could not configure encryption appservice" + yq -i -y '.encryption.plaintext_mentions = false' "$CONFIG_PATH" || echo "=> ERROR: Could not configure encryption plaintext_mentions" + yq -i -y '.encryption.delete_keys.delete_outbound_on_ack = true' "$CONFIG_PATH" || echo "=> ERROR: Could not configure encryption delete_outbound_on_ack" + yq -i -y '.encryption.delete_keys.dont_store_outbound = true' "$CONFIG_PATH" || echo "=> ERROR: Could not configure encryption dont_store_outbound" + yq -i -y '.encryption.delete_keys.ratchet_on_decrypt = true' "$CONFIG_PATH" || echo "=> ERROR: Could not configure encryption ratchet_on_decrypt" + yq -i -y '.encryption.delete_keys.delete_fully_used_on_decrypt = true' "$CONFIG_PATH" || echo "=> ERROR: Could not configure encryption delete_fully_used_on_decrypt" + yq -i -y '.encryption.delete_keys.delete_prev_on_new_session = true' "$CONFIG_PATH" || echo "=> ERROR: Could not configure encryption delete_prev_on_new_session" + yq -i -y '.encryption.delete_keys.delete_on_device_delete = true' "$CONFIG_PATH" || echo "=> ERROR: Could not configure encryption delete_on_device_delete" + yq -i -y '.encryption.delete_keys.periodically_delete_expired = true' "$CONFIG_PATH" || echo "=> ERROR: Could not configure encryption periodically_delete_expired" + yq -i -y '.encryption.delete_keys.delete_outdated_inbound = true' "$CONFIG_PATH" || echo "=> ERROR: Could not configure encryption delete_outdated_inbound" + yq -i -y '.encryption.verification_levels.receive = "cross-signed-tofu"' "$CONFIG_PATH" || echo "=> ERROR: Could not configure encryption verification receive" + yq -i -y '.encryption.verification_levels.send = "cross-signed-tofu"' "$CONFIG_PATH" || echo "=> ERROR: Could not configure encryption verification send" + yq -i -y '.encryption.verification_levels.share = "cross-signed-tofu"' "$CONFIG_PATH" || echo "=> ERROR: Could not configure encryption verification share" + fi + + # Now generate registration with proper config + echo "=> Generating registration file" + gosu cloudron:cloudron /app/pkg/mautrix-signal -g -c "$CONFIG_PATH" -r "$REGISTRATION_PATH" + + # Fix registration file regex patterns to use base domain instead of homeserver domain + if [ -f "$REGISTRATION_PATH" ] && [ -n "${CLOUDRON_APP_DOMAIN:-}" ]; then + echo "=> Fixing registration file regex patterns for domain: $BASE_DOMAIN" + # Fix user regex patterns to use base domain instead of matrix subdomain + yq -i -y '.namespaces.users[0].regex = "^@signalbot:'"$BASE_DOMAIN"'$"' "$REGISTRATION_PATH" || echo "=> ERROR: Could not fix signalbot regex" + yq -i -y '.namespaces.users[1].regex = "^@signal_.+:'"$BASE_DOMAIN"'$"' "$REGISTRATION_PATH" || echo "=> ERROR: Could not fix signal_.+ regex" + yq -i -y '.sender_localpart = "signalbot"' "$REGISTRATION_PATH" || echo "=> ERROR: Could not fix sender_localpart" + fi + + chown cloudron:cloudron "$CONFIG_PATH" "$REGISTRATION_PATH" 2>/dev/null || true + echo "=> Configuration applied successfully" + fi + + if [ ! -f "$CONFIG_PATH" ]; then + echo "=> ERROR: Config generation failed and no config file was created" + echo "=> Will try to start without config to see error messages" + fi +else + echo "=> Using existing configuration" + # Fix configuration in existing config if needed + if [ -f "$CONFIG_PATH" ]; then + echo "=> Config file exists, applying fixes..." + # Always fix configuration on every start to ensure proper settings + echo "=> Applying configuration fixes" + + # Fix database configuration if needed + if [ -n "${CLOUDRON_POSTGRESQL_URL:-}" ]; then + # Add SSL mode disable to Cloudron PostgreSQL URL if not already present + if [[ "$CLOUDRON_POSTGRESQL_URL" == *"sslmode="* ]]; then + DB_URL="$CLOUDRON_POSTGRESQL_URL" + else + DB_URL="$CLOUDRON_POSTGRESQL_URL?sslmode=disable" + fi + CURRENT_DB_URI=$(yq -r '.database.uri' "$CONFIG_PATH" 2>/dev/null || echo "") + if [ "$CURRENT_DB_URI" != "$DB_URL" ]; then + echo "=> Updating database configuration" + yq -i -y '.database.uri = "'"$DB_URL"'"' "$CONFIG_PATH" || echo "=> ERROR: Could not update database" + fi + fi + + # Fix homeserver configuration if needed + if [ -n "${CLOUDRON_APP_DOMAIN:-}" ]; then + # Extract base domain (e.g., signal.matrix.due.ren -> due.ren) + BASE_DOMAIN=$(echo "$CLOUDRON_APP_DOMAIN" | rev | cut -d. -f1-2 | rev) + CURRENT_DOMAIN=$(yq -r '.homeserver.domain' "$CONFIG_PATH" 2>/dev/null || echo "") + if [ "$CURRENT_DOMAIN" != "$BASE_DOMAIN" ]; then + echo "=> Updating homeserver configuration" + echo "=> Setting homeserver.address to: https://matrix.$BASE_DOMAIN" + echo "=> Setting homeserver.domain to: $BASE_DOMAIN" + echo "=> Setting appservice.address to: https://$CLOUDRON_APP_DOMAIN" + yq -i -y '.homeserver.address = "https://matrix.'"$BASE_DOMAIN"'"' "$CONFIG_PATH" || echo "=> ERROR: Could not update homeserver address" + yq -i -y '.homeserver.domain = "'"$BASE_DOMAIN"'"' "$CONFIG_PATH" || echo "=> ERROR: Could not update homeserver domain" + yq -i -y '.appservice.address = "https://'"$CLOUDRON_APP_DOMAIN"'"' "$CONFIG_PATH" || echo "=> ERROR: Could not update appservice address" + yq -i -y '.appservice.public_address = "https://'"$CLOUDRON_APP_DOMAIN"'"' "$CONFIG_PATH" || echo "=> ERROR: Could not update appservice public_address" + yq -i -y '.appservice.hostname = "0.0.0.0"' "$CONFIG_PATH" || echo "=> ERROR: Could not update appservice hostname" + yq -i -y '.appservice.port = 29328' "$CONFIG_PATH" || echo "=> ERROR: Could not update appservice port" + yq -i -y '.appservice.bot_username = "signalbot"' "$CONFIG_PATH" || echo "=> ERROR: Could not update appservice bot_username" + + # Configure permissions for base domain users + yq -i -y '.bridge.permissions."'"$BASE_DOMAIN"'" = "user"' "$CONFIG_PATH" || echo "=> ERROR: Could not update permissions" + + # Configure cleanup on logout to delete everything + yq -i -y '.bridge.cleanup_on_logout.enabled = true' "$CONFIG_PATH" || echo "=> ERROR: Could not update cleanup enabled" + yq -i -y '.bridge.cleanup_on_logout.manual.private = "delete"' "$CONFIG_PATH" || echo "=> ERROR: Could not update cleanup manual private" + yq -i -y '.bridge.cleanup_on_logout.manual.relayed = "delete"' "$CONFIG_PATH" || echo "=> ERROR: Could not update cleanup manual relayed" + yq -i -y '.bridge.cleanup_on_logout.manual.shared_no_users = "delete"' "$CONFIG_PATH" || echo "=> ERROR: Could not update cleanup manual shared_no_users" + yq -i -y '.bridge.cleanup_on_logout.manual.shared_has_users = "delete"' "$CONFIG_PATH" || echo "=> ERROR: Could not update cleanup manual shared_has_users" + yq -i -y '.bridge.cleanup_on_logout.bad_credentials.private = "delete"' "$CONFIG_PATH" || echo "=> ERROR: Could not update cleanup bad_credentials private" + yq -i -y '.bridge.cleanup_on_logout.bad_credentials.relayed = "delete"' "$CONFIG_PATH" || echo "=> ERROR: Could not update cleanup bad_credentials relayed" + yq -i -y '.bridge.cleanup_on_logout.bad_credentials.shared_no_users = "delete"' "$CONFIG_PATH" || echo "=> ERROR: Could not update cleanup bad_credentials shared_no_users" + yq -i -y '.bridge.cleanup_on_logout.bad_credentials.shared_has_users = "delete"' "$CONFIG_PATH" || echo "=> ERROR: Could not update cleanup bad_credentials shared_has_users" + + # Configure end-to-bridge encryption with best practices + yq -i -y '.encryption.allow = true' "$CONFIG_PATH" || echo "=> ERROR: Could not update encryption allow" + yq -i -y '.encryption.default = false' "$CONFIG_PATH" || echo "=> ERROR: Could not update encryption default" + yq -i -y '.encryption.require = false' "$CONFIG_PATH" || echo "=> ERROR: Could not update encryption require" + yq -i -y '.encryption.appservice = false' "$CONFIG_PATH" || echo "=> ERROR: Could not update encryption appservice" + yq -i -y '.encryption.plaintext_mentions = false' "$CONFIG_PATH" || echo "=> ERROR: Could not update encryption plaintext_mentions" + yq -i -y '.encryption.delete_keys.delete_outbound_on_ack = true' "$CONFIG_PATH" || echo "=> ERROR: Could not update encryption delete_outbound_on_ack" + yq -i -y '.encryption.delete_keys.dont_store_outbound = true' "$CONFIG_PATH" || echo "=> ERROR: Could not update encryption dont_store_outbound" + yq -i -y '.encryption.delete_keys.ratchet_on_decrypt = true' "$CONFIG_PATH" || echo "=> ERROR: Could not update encryption ratchet_on_decrypt" + yq -i -y '.encryption.delete_keys.delete_fully_used_on_decrypt = true' "$CONFIG_PATH" || echo "=> ERROR: Could not update encryption delete_fully_used_on_decrypt" + yq -i -y '.encryption.delete_keys.delete_prev_on_new_session = true' "$CONFIG_PATH" || echo "=> ERROR: Could not update encryption delete_prev_on_new_session" + yq -i -y '.encryption.delete_keys.delete_on_device_delete = true' "$CONFIG_PATH" || echo "=> ERROR: Could not update encryption delete_on_device_delete" + yq -i -y '.encryption.delete_keys.periodically_delete_expired = true' "$CONFIG_PATH" || echo "=> ERROR: Could not update encryption periodically_delete_expired" + yq -i -y '.encryption.delete_keys.delete_outdated_inbound = true' "$CONFIG_PATH" || echo "=> ERROR: Could not update encryption delete_outdated_inbound" + yq -i -y '.encryption.verification_levels.receive = "cross-signed-tofu"' "$CONFIG_PATH" || echo "=> ERROR: Could not update encryption verification receive" + yq -i -y '.encryption.verification_levels.send = "cross-signed-tofu"' "$CONFIG_PATH" || echo "=> ERROR: Could not update encryption verification send" + yq -i -y '.encryption.verification_levels.share = "cross-signed-tofu"' "$CONFIG_PATH" || echo "=> ERROR: Could not update encryption verification share" + + # Verify the changes were applied + echo "=> Verifying configuration changes:" + echo "=> Current homeserver.address: $(yq -r '.homeserver.address' "$CONFIG_PATH")" + echo "=> Current homeserver.domain: $(yq -r '.homeserver.domain' "$CONFIG_PATH")" + fi + fi + + # Fix logging configuration + yq -i -y '.logging.writers[1].filename = "/app/data/mautrix-signal.log"' "$CONFIG_PATH" 2>/dev/null || true + + # Always apply encryption settings (regardless of domain changes) + echo "=> Applying encryption configuration" + yq -i -y '.encryption.allow = true' "$CONFIG_PATH" || echo "=> ERROR: Could not update encryption allow" + yq -i -y '.encryption.default = false' "$CONFIG_PATH" || echo "=> ERROR: Could not update encryption default" + yq -i -y '.encryption.require = false' "$CONFIG_PATH" || echo "=> ERROR: Could not update encryption require" + yq -i -y '.encryption.appservice = false' "$CONFIG_PATH" || echo "=> ERROR: Could not update encryption appservice" + yq -i -y '.encryption.plaintext_mentions = false' "$CONFIG_PATH" || echo "=> ERROR: Could not update encryption plaintext_mentions" + yq -i -y '.encryption.delete_keys.delete_outbound_on_ack = true' "$CONFIG_PATH" || echo "=> ERROR: Could not update encryption delete_outbound_on_ack" + yq -i -y '.encryption.delete_keys.dont_store_outbound = true' "$CONFIG_PATH" || echo "=> ERROR: Could not update encryption dont_store_outbound" + yq -i -y '.encryption.delete_keys.ratchet_on_decrypt = true' "$CONFIG_PATH" || echo "=> ERROR: Could not update encryption ratchet_on_decrypt" + yq -i -y '.encryption.delete_keys.delete_fully_used_on_decrypt = true' "$CONFIG_PATH" || echo "=> ERROR: Could not update encryption delete_fully_used_on_decrypt" + yq -i -y '.encryption.delete_keys.delete_prev_on_new_session = true' "$CONFIG_PATH" || echo "=> ERROR: Could not update encryption delete_prev_on_new_session" + yq -i -y '.encryption.delete_keys.delete_on_device_delete = true' "$CONFIG_PATH" || echo "=> ERROR: Could not update encryption delete_on_device_delete" + yq -i -y '.encryption.delete_keys.periodically_delete_expired = true' "$CONFIG_PATH" || echo "=> ERROR: Could not update encryption periodically_delete_expired" + yq -i -y '.encryption.delete_keys.delete_outdated_inbound = true' "$CONFIG_PATH" || echo "=> ERROR: Could not update encryption delete_outdated_inbound" + yq -i -y '.encryption.verification_levels.receive = "cross-signed-tofu"' "$CONFIG_PATH" || echo "=> ERROR: Could not update encryption verification receive" + yq -i -y '.encryption.verification_levels.send = "cross-signed-tofu"' "$CONFIG_PATH" || echo "=> ERROR: Could not update encryption verification send" + yq -i -y '.encryption.verification_levels.share = "cross-signed-tofu"' "$CONFIG_PATH" || echo "=> ERROR: Could not update encryption verification share" + + # Update registration file with correct URL and fix regex patterns + if [ -f "$REGISTRATION_PATH" ] && [ -n "${CLOUDRON_APP_DOMAIN:-}" ]; then + yq -i -y '.url = "https://'"$CLOUDRON_APP_DOMAIN"'"' "$REGISTRATION_PATH" 2>/dev/null || true + # Fix user regex patterns to use base domain instead of matrix subdomain + yq -i -y '.namespaces.users[0].regex = "^@signalbot:'"$BASE_DOMAIN"'$"' "$REGISTRATION_PATH" || echo "=> ERROR: Could not fix signalbot regex" + yq -i -y '.namespaces.users[1].regex = "^@signal_.+:'"$BASE_DOMAIN"'$"' "$REGISTRATION_PATH" || echo "=> ERROR: Could not fix signal_.+ regex" + yq -i -y '.sender_localpart = "signalbot"' "$REGISTRATION_PATH" || echo "=> ERROR: Could not fix sender_localpart" + fi + + # Let the bridge handle token generation automatically - remove any placeholder tokens + if [ -f "$REGISTRATION_PATH" ]; then + echo "=> Registration file generated, letting bridge handle token management" + fi + fi + echo "=> Configuration fixes completed" +fi + +# Final permission fix before starting +echo "=> Setting final permissions..." +chown -R cloudron:cloudron /app/data +echo "=> Permissions set" + +# Configure TLS if certificates are available +if [ -f "/run/tls/tls.crt" ] && [ -f "/run/tls/tls.key" ]; then + echo "=> Configuring TLS certificates" + yq -i -y '.appservice.tls_cert = "/run/tls/tls.crt"' "$CONFIG_PATH" + yq -i -y '.appservice.tls_key = "/run/tls/tls.key"' "$CONFIG_PATH" +fi + +# Start the bridge from the data directory to ensure relative paths work +echo "=> Starting mautrix-signal bridge" +echo "=> Config path: $CONFIG_PATH" +echo "=> Registration path: $REGISTRATION_PATH" +echo "=> Working directory: $(pwd)" +cd /app/data +echo "=> About to exec bridge binary..." +exec gosu cloudron:cloudron /app/pkg/mautrix-signal -c "$CONFIG_PATH"