Library / SDK
signalapp/libsignal avatar
signalapp/libsignal

libsignal is one Rust workspace, three bridge layers and a support policy that stops at Signal

Home to the Signal Protocol as well as other cryptographic primitives which make Signal possible.

6,150 stars816 forksRustAGPL-3.0

At a glance

What is it?
libsignal holds the Signal Protocol and the cryptographic primitives around it, exposed to app languages as Java, Swift and TypeScript libraries. Its own build files are the most concrete part of the repository: 35 workspace crates, 10 built by default, and a justfile with one generator per language surface.
Who is it for?
libsignal is the right dependency for a team already building inside Signal's own ecosystem, for the Android, iOS, Desktop or server side, because the bridge generators, the pinned toolchains and the published org.signal packages line up with that one job.
Can I use it commercially?
Yes, with strict conditions. AGPL-3.0 is a network copyleft licence: if people use a modified version over a network, for example as a hosted service, you must offer them its source code under the same licence.
Is it still maintained?
Yes. The repository received new commits within the last day.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on November 13, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • signalapp/libsignal6,150 ★ · +117
Star history
Repository2026-09-232026-11-13
signalapp/libsignal6k6.2k

117 Stars in the last 51 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

Use outside Signal is unsupported, and the version number only hints at breakage

libsignal opens by stating the terms plainly. The repository holds the Signal Protocol and the cryptographic primitives that make Signal possible, its APIs are consumed by the official clients and servers, and use outside of Signal is unsupported. The products are the Java, Swift and TypeScript libraries that wrap the Rust implementations, which means the Rust crates are the implementation layer and not the interface most callers touch.

Stability is promised in two tiers, and the difference matters. Backwards-incompatible changes to the Java, Swift, TypeScript and non-bridge Rust APIs are meant to show up in the version number, on a best-effort basis, including when the minimum supported tools versions go up. The JNI, C and Node add-on bridge layers get no such promise, because all APIs and implementations are subject to change without notice. A pinned dependency therefore buys you different guarantees depending on which side of the bridge your call sits on.

cargo build at the root builds ten crates, not twenty five

Cargo.toml declares 35 workspace members and then narrows the default build to ten of them, which decides what a bare cargo build actually compiles.

code
default-members = [
    "rust/crypto",
    "rust/device-transfer",
    "rust/media",
    "rust/message-backup",
    "rust/account-keys",
    "rust/poksho",
    "rust/protocol",
    "rust/usernames",
    "rust/zkcredential",
    "rust/zkgroup",
]

The twenty-five left out are the bridge subtrees (rust/bridge/ffi, rust/bridge/jni, rust/bridge/node and rust/bridge/shared, with their impl, native_swift, native_kt, native_ts, macros, types or testing parts) plus rust/attest, rust/cli-utils, rust/core, rust/debug, rust/keytrans, rust/svrb, rust/message-backup/macros, rust/protocol/test-support and the four network crates rust/net, rust/net/chat, rust/net/grpc and rust/net/infra. So cargo build and cargo test at the repository root cover the protocol, crypto and zero-knowledge cores and skip both the code generation surfaces and the attestation code. The workspace sets the resolver to version 3, and the shared package block pins version 0.106.1, license AGPL-3.0-only, rust-version 1.93.1 and authors Signal Messenger LLC.

Three bridge generators, one recipe each, and a parallel alias

The justfile keeps one recipe per language surface and then fans out across all three.

code
generate-jni:
    cargo run -p libsignal-jni-native_kt

generate-ffi:
    swift/build_ffi.sh --generate-ffi

generate-node:
    cargo run -p libsignal-node-native_ts

# Regenerate bridge code for all three app languages.
[parallel]
generate-bridge: generate-jni generate-node generate-ffi

Each generator carries an alias named after the language rather than the bridge (generate-java, generate-swift, generate-ts), and the combined recipe is reachable as generate-all. When you expose new APIs to Java, the README says to run cargo run -p libsignal-jni-native_kt (or just generate-jni) in addition to rebuilding. So a full regeneration means a cargo run for the Kotlin bridge, a shell script inside swift/ for the FFI side, and a cargo run for the TypeScript node, all in one working tree. Alongside them the acknowledgment recipes run bin/regenerate_acknowledgments.sh, and the check variant passes --check, which is the one that belongs in CI.

The default just recipe exits 1 and tells you to run --list

Running just with no arguments is a deliberate failure, and the recipe says so.

code
_default:
    echo "no default recipe; run with --list to see all recipes"
    exit 1

The file's own comment sets the expectation that every new recipe ships a one-line comment describing the behavior shown by just --list. Two recipes exist only to install toolchains: install-stable reads the pinned version out of rust-toolchain and install-nightly reads .nightly-rust-version, both handing rustup the profile minimal plus the rustfmt and clippy components, with any extra arguments appended. The formatters then split three ways, and format-rust is the one that insists on the nightly toolchain because the options in .rustfmt.toml are still unstable. Java formatting runs Gradle's spotlessApply inside java, Swift formatting runs swift format in place and recursive inside swift, and node formatting runs npm run format. Three formatters, three top level config files, and a [workspace.lints.clippy] block in Cargo.toml that turns cast_possible_truncation into a warning for the whole workspace.

Rust, Clang, protoc and Python 3.9 or newer, and tools that must come from cargo

Building anything in the repository needs a toolchain list longer than a single crate would suggest: Rust, recent versions of Clang and libclang, CMake, Make, protoc, Python 3.11 or newer, and git.

shell
$ apt-get install clang libclang-dev cmake make protobuf-compiler libprotobuf-dev python3 git

That is the Debian-family route. On macOS the repository points at bin/mac_setup.sh, described as a best-effort maintained script for setting up the Rust toolchain. The Rust build pins a specific stable compiler that cargo downloads on its own, so cargo build and cargo test work without a separate rustup step.

The more consequential instruction comes next. Any Rust tool that can affect the build should be installed from cargo rather than from a system package manager such as apt or brew, because those sometimes carry versions old enough to break the build with incompatibility issues. The versions come from files in the tree:

shell
$ cargo +stable install --version "$(cat acknowledgments/cargo-about-version)" --locked cargo-about
$ cargo +stable install --version "$(cat .taplo-cli-version)" --locked taplo-cli
$ cargo +stable install cargo-fuzz

Four Android Rust targets and a JAVA_HOME export before gradlew runs

Android work adds its own cross-compilation targets and a fixed JDK version.

shell
rustup target add armv7-linux-androideabi aarch64-linux-android i686-linux-android x86_64-linux-android
export JAVA_HOME=$(/usr/libexec/java_home -v 21)

Those four targets must be added to the Rust compiler before anything Android related will compile, and the officially supported JDK for Android builds is JDK 21 with JAVA_HOME pointed at it. On macOS that is the /usr/libexec/java_home export above; on Debian-family Linux the way to do it varies by distribution and the README points at sudo update-alternatives --config java instead. Once the toolchain is right, the Java side is driven from the java directory, where ./gradlew test runs the tests and ./gradlew build produces the AAR outputs, with make offered as the Docker-based alternative in the same directory. Two Gradle properties change what you see: -P debugLevelLogs stops the build from filtering out debug and verbose level logs coming out of Rust, and -P jniTypeTagging turns on additional checks in the Rust JNI bridging code. One inconsistency to notice: the README refers to a .tools_version file for runtime version managers, while the repository top level lists .tool-versions.

Three org.signal packages from a build artifacts Maven repository

Signal publishes three Java packages for its own use: org.signal:libsignal-server, org.signal:libsignal-client and org.signal:libsignal-android. Native library coverage differs between them. The client and server packages carry native libraries for Debian-flavored x86_64 Linux, Windows on x86_64, and macOS on x86_64 and arm64. libsignal-android covers armeabi-v7a, arm64-v8a, x86 and x86_64 Android, and an Android build needs both libsignal-android and libsignal-client.

Everything published lands in a Maven repository at https://build-artifacts.signal.org/libraries/maven/, and older builds were published to Maven Central instead, so a Gradle build pointed at the wrong repository either resolves nothing or resolves an old native library. The whole instruction is this block:

code
maven {
  name = "SignalBuildArtifacts"
  // The "uri()" part is only necessary for Kotlin Gradle; Groovy Gradle accepts a bare string here.
  url = uri("https://build-artifacts.signal.org/libraries/maven/")
}

The Swift and node surfaces exist too, through Package.swift, the swift/ and node/ directories and .nvmrc.

Editorial conclusion

libsignal is the right dependency for a team already building inside Signal's own ecosystem, for the Android, iOS, Desktop or server side, because the bridge generators, the pinned toolchains and the published org.signal packages line up with that one job. It is the wrong dependency for anything else: the README states that use outside of Signal is unsupported, that bridge layers can change without notice, and that even the non-bridge APIs signal breaking changes in the version number only on a best-effort basis. Before pulling it in, read the two stability tiers again, confirm your Rust toolchain against rust-version 1.93.1 and JDK 21 for Android work, and settle the licensing question yourself, since the workspace declares AGPL-3.0-only.

Frequently asked questions

how to use libsignal

The repository is used by the Signal client apps and servers, and use outside of Signal is unsupported. For development the first steps are cargo build and cargo test, then the justfile recipe that regenerates the bridge for whichever language surface you are touching.

how to install libsignal client

Signal publishes org.signal:libsignal-server, org.signal:libsignal-client and org.signal:libsignal-android into a Maven repository at https://build-artifacts.signal.org/libraries/maven/, and older builds went to Maven Central. The client and server packages carry native libraries for Debian-flavored x86_64 Linux, Windows x86_64, and macOS x86_64 and arm64; libsignal-android carries Android native libraries.

Which language surfaces does libsignal expose?

The products are the Java, Swift and TypeScript libraries that wrap the Rust implementations, reached through JNI, C and Node add-on bridge layers. The underlying crates include libsignal-protocol, signal-crypto, device-transfer, attest, zkgroup, zkcredential, poksho, account-keys, usernames and media.

What toolchain does libsignal need for a first build?

Cargo.toml sets rust-version to 1.93.1 for the workspace, whose package version is 0.106.1 under license AGPL-3.0-only. The build also needs recent Clang, libclang, CMake, Make, protoc, Python 3.11 or newer, and git, plus JDK 21 and the Android NDK and SDK for Android targets.

What replaced libsignal-protocol-java and libsignal-metadata-java?

libsignal-protocol, a crate in this repository, implements the Signal protocol including the Double Ratchet algorithm and is described as the replacement for both libsignal-protocol-java and libsignal-metadata-java.

Official sources

  1. Issues
  2. License: AGPL-3.0
  3. README
  4. Releases
  5. signalapp/libsignal on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/signalapp-libsignal.svg)](https://hysenlabs.com/projects/signalapp-libsignal)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/signalapp-libsignal.svg)](https://hysenlabs.com/projects/signalapp-libsignal)

Was this useful?