diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 2b23ed3c..5a4cf4d7 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -13,22 +13,22 @@ jobs:
build:
strategy:
matrix:
- # The "reproducible" entry is used to build the project with the LTS Java version used in reproducible builds script.
+ # java="25" is the LTS Java version used in reproducible builds script (default in Containerfile).
# More Java versions can be added to test compatibility, eg. "26".
- java: ["reproducible", "26"]
+ java: ["25", "26"]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Build
run: |
- if [ "${{ matrix.java }}" != "reproducible" ]; then
+ if [ "${{ matrix.java }}" != "25" ]; then
export OVERRIDE_JAVA_VERSION="${{ matrix.java }}"
fi
./reproducible-builds/build.sh
- name: Upload build artifacts
uses: actions/upload-artifact@v7
with:
- name: signal-cli-${{ matrix.java }}-${{ github.job }}
+ name: signal-cli-archive-${{ matrix.java }}
path: dist/*
build-client:
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index a18b953f..621581f9 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -12,10 +12,11 @@ env:
IMAGE_REGISTRY: ghcr.io/asamk
REGISTRY_USER: ${{ github.actor }}
REGISTRY_PASSWORD: ${{ github.token }}
+ ARCHIVE_JAVA_VERSION: 25
jobs:
build:
- uses: AsamK/signal-cli/.github/workflows/build.yml@master
+ uses: ./.github/workflows/build.yml
release:
needs: build
@@ -31,7 +32,7 @@ jobs:
- name: Get signal-cli version
id: version
run: |
- mv ./signal-cli-reproducible-build/* .
+ mv ./signal-cli-archive-${{ env.ARCHIVE_JAVA_VERSION }}/* .
echo "version=$(cat VERSION)" >> $GITHUB_OUTPUT
- name: Create release
@@ -87,7 +88,7 @@ jobs:
- name: Move archive file
run: |
- tar xf ./signal-cli-reproducible-build/signal-cli-${{ needs.release.outputs.version }}.tar.gz
+ tar xf signal-cli-archive-${{ env.ARCHIVE_JAVA_VERSION }}/signal-cli-${{ needs.release.outputs.version }}.tar.gz
mkdir -p build/install/
mv ./signal-cli-"${{ needs.release.outputs.version }}"/ build/install/signal-cli
@@ -127,7 +128,7 @@ jobs:
- name: Move archive file
run: |
- tar xf ./signal-cli-reproducible-build/signal-cli-${{ needs.release.outputs.version }}-Linux-native.tar.gz
+ tar xf signal-cli-archive-${{ env.ARCHIVE_JAVA_VERSION }}/signal-cli-${{ needs.release.outputs.version }}-Linux-native.tar.gz
mkdir -p build/native/nativeCompile/
mv signal-cli build/native/nativeCompile/
chmod +x build/native/nativeCompile/signal-cli
@@ -168,7 +169,7 @@ jobs:
- name: Move archive file
run: |
- tar xf ./signal-cli-reproducible-build/signal-cli-${{ needs.release.outputs.version }}-Linux-client.tar.gz
+ tar xf signal-cli-archive-${{ env.ARCHIVE_JAVA_VERSION }}/signal-cli-${{ needs.release.outputs.version }}-Linux-client.tar.gz
mkdir -p client/target/release/
mv signal-cli-client client/target/release/
chmod +x client/target/release/signal-cli-client
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 44478806..90d02420 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,10 +1,22 @@
# Changelog
-## [Unreleased]
+## [0.14.3] - 2026-04-22
+
+### Fixed
+
+- Fix sender key re-distribution on every group message (Thanks @meinecke)
+
+### Improved
+
+- Performance improvement when assigning admin role to multiple group members
+- Increase disconnect timeout for websocket connections
+- Release builds are now reproducible
### Changed
- Send message results now surface server-advised retry time for plain rate-limit (HTTP 413) failures, not only for proof-required challenges. The `retryAfterSeconds` field in JSON-RPC `SendMessageResult` is populated whenever the server sends a `Retry-After` header. The canonical way to distinguish proof-required failures remains `token != null`. Text output includes "retry after N seconds" when known.
+- Add distinct JSON-RPC error code (6) for captcha rejection (Thanks @tonycpsu)
+- No longer sends busy call response to allow linked devices to accept call
## [0.14.2] - 2026-04-04
diff --git a/build.gradle.kts b/build.gradle.kts
index f1e10f4d..64e29294 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -10,7 +10,7 @@ plugins {
allprojects {
group = "org.asamk"
- version = "0.14.3-SNAPSHOT"
+ version = "0.14.3"
}
java {
diff --git a/data/org.asamk.SignalCli.metainfo.xml b/data/org.asamk.SignalCli.metainfo.xml
index c1730192..3bf04d8c 100644
--- a/data/org.asamk.SignalCli.metainfo.xml
+++ b/data/org.asamk.SignalCli.metainfo.xml
@@ -45,6 +45,9 @@
intense
+
+ https://github.com/AsamK/signal-cli/releases/tag/v0.14.3
+
https://github.com/AsamK/signal-cli/releases/tag/v0.14.2
diff --git a/libsignal-version b/libsignal-version
index ae02209b..da011ce4 100644
--- a/libsignal-version
+++ b/libsignal-version
@@ -1 +1 @@
-0.90.0
+0.92.1
diff --git a/reproducible-builds/build.Containerfile b/reproducible-builds/build.Containerfile
index 0e73ddc5..38626266 100644
--- a/reproducible-builds/build.Containerfile
+++ b/reproducible-builds/build.Containerfile
@@ -1,13 +1,12 @@
-ARG ZULU_TAG="25.0.2-jdk@sha256:9582df6c4415d9c770eb5ff8fce426ebba53631149c9eb083ee126568d32fab3"
+ARG ZULU_TAG="25-latest@sha256:8eca9375451a392bff01efe946f2e9263c50aa71a9d68423c068cc1061a41b7e"
FROM docker.io/azul/zulu-openjdk:$ZULU_TAG
-ENV SOURCE_DATE_EPOCH=1767225600
+ARG SOURCE_DATE_EPOCH="1776889382"
+ENV SOURCE_DATE_EPOCH=$SOURCE_DATE_EPOCH
ENV LANG=C.UTF-8
ENV LC_CTYPE=en_US.UTF-8
-ARG SNAPSHOT=20260101T000000Z
-RUN echo "deb http://snapshot.ubuntu.com/ubuntu/${SNAPSHOT}/ jammy main" > /etc/apt/sources.list \
- && echo "deb http://snapshot.ubuntu.com/ubuntu/${SNAPSHOT}/ jammy universe" >> /etc/apt/sources.list
-RUN apt update && apt install -y make asciidoc-base
+RUN SNAPSHOT="$(date -u -d "@$SOURCE_DATE_EPOCH" +%Y%m%dT%H%M%SZ)" \
+ && apt install -y make asciidoc-base --update --snapshot "$SNAPSHOT" --no-install-recommends --no-install-suggests
COPY --chmod=0700 reproducible-builds/entrypoint.sh /usr/local/bin/entrypoint.sh
WORKDIR /signal-cli
ENTRYPOINT [ "/usr/local/bin/entrypoint.sh", "build" ]
diff --git a/reproducible-builds/build.sh b/reproducible-builds/build.sh
index 8a3878ce..2a46edaa 100755
--- a/reproducible-builds/build.sh
+++ b/reproducible-builds/build.sh
@@ -18,11 +18,8 @@ fi
VERSION=$(sed -n 's/\s*version\s*=\s*"\(.*\)".*/\1/p' build.gradle.kts | tail -n1)
echo "$VERSION" >dist/VERSION
-$ENGINE build -t signal-cli:build ${OVERRIDE_JAVA_VERSION:+--build-arg ZULU_TAG=$OVERRIDE_JAVA_VERSION} -f reproducible-builds/build.Containerfile .
-$ENGINE build -t signal-cli:native -f reproducible-builds/native.Containerfile .
-$ENGINE build -t signal-cli:client -f reproducible-builds/client.Containerfile .
-
# Build jar
+$ENGINE build -t signal-cli:build ${OVERRIDE_JAVA_VERSION:+--build-arg ZULU_TAG=$OVERRIDE_JAVA_VERSION} -f reproducible-builds/build.Containerfile .
git clean -Xfd -e '!/dist/' -e '!/dist/**' -e '!/github/' -e '!/github/**'
# shellcheck disable=SC2086
$ENGINE run --pull=never --rm -v "$(pwd)":/signal-cli:Z -e VERSION="$VERSION" $USER signal-cli:build
@@ -34,12 +31,14 @@ if [ -n "${OVERRIDE_JAVA_VERSION:-}" ]; then
fi
# Build native-image
+$ENGINE build -t signal-cli:native -f reproducible-builds/native.Containerfile .
git clean -Xfd -e '!/dist/' -e '!/dist/**' -e '!/github/' -e '!/github/**'
# shellcheck disable=SC2086
$ENGINE run --pull=never --rm -v "$(pwd)":/signal-cli:Z -e VERSION="$VERSION" $USER signal-cli:native
mv build/signal-cli-*-Linux-native.tar.gz dist/
# Build rust client
+$ENGINE build -t signal-cli:client -f reproducible-builds/client.Containerfile .
git clean -Xfd -e '!/dist/' -e '!/dist/**' -e '!/github/' -e '!/github/**'
# shellcheck disable=SC2086
$ENGINE run --pull=never --rm -v "$(pwd)":/signal-cli:Z -e VERSION="$VERSION" $USER signal-cli:client
diff --git a/reproducible-builds/client.Containerfile b/reproducible-builds/client.Containerfile
index 8a8dfa34..59d85db8 100644
--- a/reproducible-builds/client.Containerfile
+++ b/reproducible-builds/client.Containerfile
@@ -1,5 +1,8 @@
-FROM docker.io/rust:1.94.1-slim-trixie@sha256:c6a474d7164ea2455e09b60a759b1edca38db7373c5689c1dae31780de4e71ac
-ENV SOURCE_DATE_EPOCH=1767225600
+ARG RUST_TAG="1-slim@sha256:715efd1ccdc4a63bd6a6e2f54387fff73f904b70e610d41b4d9d74ff38e13ad3"
+
+FROM docker.io/rust:$RUST_TAG
+ARG SOURCE_DATE_EPOCH="1776889382"
+ENV SOURCE_DATE_EPOCH=$SOURCE_DATE_EPOCH
ENV LANG=C.UTF-8
ENV LC_CTYPE=en_US.UTF-8
COPY --chmod=0700 reproducible-builds/entrypoint.sh /usr/local/bin/entrypoint.sh
diff --git a/reproducible-builds/native.Containerfile b/reproducible-builds/native.Containerfile
index 47cd5ce3..a242538d 100644
--- a/reproducible-builds/native.Containerfile
+++ b/reproducible-builds/native.Containerfile
@@ -1,5 +1,8 @@
-FROM container-registry.oracle.com/graalvm/native-image:25.0.2@sha256:4c0d5919f6840d89721274eb8cf81962faa2f870b816967e6732e2a151b150d8
-ENV SOURCE_DATE_EPOCH=1767225600
+ARG GRAALVM_TAG="25@sha256:38f835ccb37d4a106c37376a98e8713999077a8c8173d9876505f77da438332c"
+
+FROM container-registry.oracle.com/graalvm/native-image:$GRAALVM_TAG
+ARG SOURCE_DATE_EPOCH="1776889382"
+ENV SOURCE_DATE_EPOCH=$SOURCE_DATE_EPOCH
ENV LANG=C.UTF-8
ENV LC_CTYPE=en_US.UTF-8
COPY --chmod=0700 reproducible-builds/entrypoint.sh /usr/local/bin/entrypoint.sh
diff --git a/reproducible-builds/update-pinned-container-versions.sh b/reproducible-builds/update-pinned-container-versions.sh
new file mode 100755
index 00000000..f03c4333
--- /dev/null
+++ b/reproducible-builds/update-pinned-container-versions.sh
@@ -0,0 +1,57 @@
+#!/bin/bash
+
+set -euo pipefail
+
+ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/../"
+cd "$ROOT_DIR"
+
+if command -v podman >/dev/null; then
+ ENGINE=podman
+elif command -v docker >/dev/null; then
+ ENGINE=docker
+else
+ echo "error: neither podman nor docker is available" >&2
+ exit 1
+fi
+
+resolve_digest() {
+ local image_ref="$1"
+ "$ENGINE" pull "$image_ref" >/dev/null
+ "$ENGINE" image inspect --format '{{range .RepoDigests}}{{println .}}{{end}}' "$image_ref" \
+ | grep -m1 -E '@sha256:[0-9a-f]{64}$' \
+ | sed -E 's|.*(@sha256:[0-9a-f]{64})$|\1|'
+}
+
+update_arg_tag() {
+ local file="$1"
+ local arg_name="$2"
+ local image_prefix="$3"
+ local current
+ current="$(sed -n "s/^ARG ${arg_name}=\"\([^\"]*\)\"$/\\1/p" "$file")"
+ if [[ -z "$current" ]]; then
+ echo "error: could not find ARG ${arg_name} in $file" >&2
+ exit 1
+ fi
+ local tag
+ tag="${current%@*}"
+ local digest
+ digest="$(resolve_digest "${image_prefix}${tag}")"
+ sed -i -E "s|^ARG ${arg_name}=\"[^\"]+\"$|ARG ${arg_name}=\"${tag}${digest}\"|" "$file"
+ echo "updated $file -> ${tag}${digest}"
+}
+
+update_source_date_epoch() {
+ local file="$1"
+ local current_timestamp
+ current_timestamp="$(date +%s)"
+ sed -i -E "s|^ARG SOURCE_DATE_EPOCH=\"[^\"]+\"$|ARG SOURCE_DATE_EPOCH=\"${current_timestamp}\"|" "$file"
+ echo "updated $file SOURCE_DATE_EPOCH -> ${current_timestamp}"
+}
+
+update_arg_tag reproducible-builds/build.Containerfile ZULU_TAG docker.io/azul/zulu-openjdk:
+update_arg_tag reproducible-builds/native.Containerfile GRAALVM_TAG container-registry.oracle.com/graalvm/native-image:
+update_arg_tag reproducible-builds/client.Containerfile RUST_TAG docker.io/rust:
+
+update_source_date_epoch reproducible-builds/build.Containerfile
+update_source_date_epoch reproducible-builds/native.Containerfile
+update_source_date_epoch reproducible-builds/client.Containerfile