## File: README.md

# Overview

libsignal contains platform-agnostic APIs used by the official Signal clients and servers, exposed
as a Java, Swift, or TypeScript library. The underlying implementations are written in Rust:

- libsignal-protocol: Implements the Signal protocol, including the [Double Ratchet algorithm][]. A
  replacement for [libsignal-protocol-java][] and [libsignal-metadata-java][].
- signal-crypto: Cryptographic primitives such as AES-GCM. We use [RustCrypto][]'s where we can
  but sometimes have differing needs.
- device-transfer: Support logic for Signal's device-to-device transfer feature.
- attest: Functionality for remote attestation of [SGX enclaves][] and server-side [HSMs][].
- zkgroup: Functionality for [zero-knowledge groups][] and related features available in Signal.
- zkcredential: An abstraction for the sort of zero-knowledge credentials used by zkgroup, based on the paper "[The Signal Private Group System][]" by Chase, Perrin, and Zaverucha.
- poksho: Utilities for implementing zero-knowledge proofs (such as those used by zkgroup); stands for "proof-of-knowledge, stateful-hash-object".
- account-keys: Functionality for consistently using [PINs][] as passwords in Signal's Secure Value Recovery system, as well as other account-wide key operations.
- usernames: Functionality for username generation, hashing, and proofs.
- media: Utilities for manipulating media.

This repository is used by the Signal client apps ([Android][], [iOS][], and [Desktop][]) as well as
server-side. Use outside of Signal is unsupported. In particular, the products of this repository
are the Java, Swift, and TypeScript libraries that wrap the underlying Rust implementations. All
APIs and implementations are subject to change without notice, as are the JNI, C, and Node add-on
"bridge" layers. However, backwards-incompatible changes to the Java, Swift, TypeScript, and
non-bridge Rust APIs will be reflected in the version number on a best-effort basis, including
increases to the minimum supported tools versions.

[Double Ratchet algorithm]: https://signal.org/docs/
[libsignal-protocol-java]: https://github.com/signalapp/libsignal-protocol-java
[libsignal-metadata-java]: https://github.com/signalapp/libsignal-metadata-java
[RustCrypto]: https://github.com/RustCrypto
[Noise protocol]: http://noiseprotocol.org/
[SGX enclaves]: https://www.intel.com/content/www/us/en/architecture-and-technology/software-guard-extensions.html
[HSMs]: https://en.wikipedia.org/wiki/Hardware_security_module
[zero-knowledge groups]: https://signal.org/blog/signal-private-group-system/
[The Signal Private Group System]: https://eprint.iacr.org/2019/1416.pdf
[PINs]: https://signal.org/blog/signal-pins/
[Android]: https://github.com/signalapp/Signal-Android
[iOS]: https://github.com/signalapp/Signal-iOS
[Desktop]: https://github.com/signalapp/Signal-Desktop


# Building

### Toolchain Installation

To build anything in this repository you must have [Rust](https://rust-lang.org) installed, as well
as recent versions of Clang, libclang, [CMake](https://cmake.org), Make, protoc, Python (3.9+), and git.

#### Linux/Debian

On a Debian-like system, you can get these extra dependencies through `apt`:

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

#### macOS

On macOS, we have a best-effort maintained script to set up the Rust toolchain you can run by:

```shell
$ bin/mac_setup.sh
```

## Rust

### First Build and Test

The build currently uses a specific version of the Rust nightly compiler, which
will be downloaded automatically by cargo. To build and test the basic protocol
libraries:

```shell
$ cargo build
...
$ cargo test
...
```

### Additional Rust Tools

The basic tools above should get you set up for most libsignal Rust development.

Eventually, you may find that you need some additional Rust tools like `taplo` for code formatting.

You should always install any Rust tools you need that may affect the build from cargo rather than from your system
package manager (e.g. `apt` or `brew`). Package managers sometimes contain outdated versions of these tools that can break
the build with incompatibility issues (especially cbindgen).

To install the main Rust extra dependencies matching the versions we use, you can run the following commands:

```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
```

## Java/Android

### Toolchain Setup / Configuration

To build for Android you must install several additional packages including a JDK,
the Android NDK/SDK, and add the Android targets to the Rust compiler, using

```rustup target add armv7-linux-androideabi aarch64-linux-android i686-linux-android x86_64-linux-android```

Our officially supported JDK version for Android builds is JDK 21, so be sure to install e.g. OpenJDK 21, and then point JAVA_HOME to it.

You can easily do this on macOS via:

```shell
export JAVA_HOME=$(/usr/libexec/java_home -v 21)
```

On Linux, the way you do this varies by distribution. For Debian based distributions like Ubuntu, you can use:

```shell
sudo update-alternatives --config java
```

We also check-in a `.tools_version` file for use with runtime version managers.

### Building and Testing

To build the Java/Android ``jar`` and ``aar``, and run the tests:

```shell
$ cd java
$ ./gradlew test
$ ./gradlew build # if you need AAR outputs
```

You can pass `-P debugLevelLogs` to Gradle to build without filtering out debug- and verbose-level
logs from Rust, and `-P jniTypeTagging` to enable additional checks in the Rust JNI bridging code.

Alternately, a build system using Docker is available:

```shell
$ cd java
$ make
```

When exposing new APIs to Java, you will need to run `rust/bridge/jni/bin/gen_java_decl.py` in
addition to rebuilding. This requires installing the `cbindgen` Rust tool, as detailed above.

### Use as a library

Signal publishes Java packages for its own use, under the names org.signal:libsignal-server,
org.signal:libsignal-client, and org.signal:libsignal-android. libsignal-client and libsignal-server
contain native libraries for Debian-flavored x86_64 Linux as well as Windows (x86_64) and macOS
(x86_64 and arm64). libsignal-android contains native libraries for armeabi-v7a, arm64-v8a, x86, and
x86_64 Android. These are located in a Maven repository at
https://build-artifacts.signal.org/libraries/maven/; for use from Gradle, add the following to your
`repositories` block:

```
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/")
}
```

Older builds were published to [Maven Central](https://central.sonatype.org) instead.

When building for Android you need *both* libsignal-android and libsignal-client, but the Windows
and macOS libraries in libsignal-client won't automatically be excluded from your final app. You can
explicitly exclude them using `packaging`:

```
android {
  // ...
  packaging {
    resources {
      excludes += setOf("libsignal_jni*.dylib", "signal_jni*.dll")
    }
  }
  // ...
}
```

You can additionally exclude `libsignal_jni_testing.so` if you do not plan to use any of the APIs
intended for client testing.

### Testing a local build with Signal-Android

The Signal-Android gradle.properties file has a commented-out line to include libsignal as part of the build. Uncomment that and adjust the path; optionally, you can restrict the architectures you want to build for by adding `androidArchs=aarch64` to *libsignal's* gradle.properties. (The set of recognized architectures is in java/build_jni.sh.) If you're using an IDE, you'll need to re-import the Gradle structure at this point. When you're done, revert the changes to the Android app's gradle.properties and re-import once more.

Note that this does not import the *Rust* parts of the project into the IDE. Doing that in a multi-language IDE like IDEA is possible, but finicky; as of 2025 the most reliable way to do it is to open the Android project first, add the libsignal repo root directory as a Rust project second (only including the top-level directory), and only then make the changes to gradle.properties.


## Swift

To learn about the Swift build process see [``swift/README.md``](swift/)


## Node

You'll need Node installed to build. If you have [nvm][], you can run `nvm use` to select an
appropriate version automatically.

We use `npm` as our package manager, and a Python script to control building the Rust library, accessible as `npm run build`.

```shell
$ cd node
$ nvm use
$ npm install
$ npm run build
$ npm run tsc
$ npm run test
```

When testing changes locally, you can use `npm run build` to do an incremental rebuild of the Rust library. Alternately, `npm run build-with-debug-level-logs` will rebuild without filtering out debug- and verbose-level logs.

When exposing new APIs to Node, you will need to run `just generate-node` in
addition to rebuilding.

[nvm]: https://github.com/nvm-sh/nvm

### NPM

Signal publishes the NPM package `@signalapp/libsignal-client` for its own use, including native
libraries for Windows, macOS, and Debian-flavored Linux. Both x64 and arm64 builds are included for
all three platforms, but the arm64 builds for Windows and Linux are considered experimental, since
there are no official builds of Signal for those architectures.

### Testing a local build with Signal-Desktop

After running all the build commands above, adjust the `@signalapp/libsignal-client` dependency in the Desktop app's package.json to "link:path/to/libsignal/node" and run `pnpm install`. When you're done, revert the changes to package.json and run `pnpm install` again.


# Contributions

Signal does accept external contributions to this project. However unless the change is
simple and easily understood, for example fixing a bug or portability issue, adding a new
test, or improving performance, first open an issue to discuss your intended change as not
all changes can be accepted.

Contributions that will not be used directly by one of Signal's official client apps may still be
considered, but only if they do not pose an undue maintenance burden or conflict with the goals of
the project.

Signing a [CLA (Contributor License Agreement)](https://signal.org/cla/) is required for all contributions.

## Code Formatting and Acknowledgments

You can run the styler on the entire project by running:

```shell
just format-all
```

You can run more extensive tests as well as linters and clippy by running:

```shell
just check-pre-commit
```

When making a PR that adjusts dependencies, you'll need to regenerate our acknowledgments files. See [``acknowledgments/README.md``](acknowledgments/).

# Legal things
## Cryptography Notice

This distribution includes cryptographic software. The country in which you currently reside may have restrictions on
the import, possession, use, and/or re-export to another country, of encryption software.  BEFORE using any encryption
software, please check your country's laws, regulations and policies concerning the import, possession, or use, and
re-export of encryption software, to see if this is permitted.  See <http://www.wassenaar.org/> for more information.

The U.S. Government Department of Commerce, Bureau of Industry and Security (BIS), has classified this software as
Export Commodity Control Number (ECCN) 5D002.C.1, which includes information security software using or performing
cryptographic functions with asymmetric algorithms.  The form and manner of this distribution makes it eligible for
export under the License Exception ENC Technology Software Unrestricted (TSU) exception (see the BIS Export
Administration Regulations, Section 740.13) for both object code and source code.

## License

Copyright 2020-2026 Signal Messenger, LLC

Licensed under the GNU AGPLv3: https://www.gnu.org/licenses/agpl-3.0.html

---

## File: acknowledgments/README.md

This directory contains pre-generated acknowledgments for the Rust dependencies of libsignal. CI enforces that they are kept up to date.

## Updating

If you update libsignal's dependencies, you'll need to update this listing. Install [cargo-about][] if you haven't already:

```shell
cargo +stable install --locked cargo-about --version $(cat acknowledgments/cargo-about-version)
```

Then:

1. Run `bin/regenerate_acknowledgments.sh`.
2. Check the HTML output for new "synthesized" entries. This can indicate that the license for a particular dependency was not properly detected.
3. If there are any unaccounted-for "synthesized" entries, add new "[clarify][]" entries to about.toml.

Apart from the projects in this very repo, there are a few other crates that unavoidably have "synthesized" licenses based on their Cargo manifests:

- cesu8: Very old crate whose repository contains a license file for the Rust project itself, rather than the crate.
- curve25519-dalek **4.1.3**: used only as a dev-dependency; our real version has a proper license via about.toml
- hpke-rs-\*: Uploaded without a license file, though a license is listed in the Cargo.toml for each crate.

[cargo-about]: https://embarkstudios.github.io/cargo-about/
[clarify]: https://embarkstudios.github.io/cargo-about/cli/generate/config.html#the-clarify-field-optional

---

## File: doc/src/backups/README.md

# Backups

libsignal has a handful of APIs related to backups:

- [Account keys](#account-keys)
- [Backup validation](#backup-validation)
- [BackupAuthCredential](#backupauthcredential)

## Account keys

Going forward, a number of account keys, including backup keys, will be derived from an *account entropy pool,* a 64-character alphanumeric ASCII string. While the AEP is represented as a plain String in libsignal APIs, methods to work with it can be found on `AccountEntropyPool`, including generation, validation, and derivation of other keys.

Derived from the account entropy pool is the `BackupKey`, a strongly-typed 32-byte blob that is used for all aspects of backups. There are many keys and identifiers that are derivable from a BackupKey.

Finally, the key specifically used to encrypt backup files is the `MessageBackupKey`, another strongly-typed object that consists of an HMAC key and an AES key for signing and encrypting the backup, respectively.




## Backup validation

There are a few different APIs that work with backup files:

### MessageBackup: Bulk validation of an encrypted backup file stream

Takes in an encrypted input stream and produces validation results. Hard errors are thrown as exceptions, soft errors (unknown fields) are returned for manual checking or logging.

Validation makes two passes over the stream to verify its contained MAC both before and after parsing, so the relevant APIs take a callback to *produce* streams rather than a single stream object. There is no guarantee that the first stream has been fully consumed before the second is produced, so do not reuse the same stream object.

Provided by:

- `MessageBackup` in Java
- `validateMessageBackup` in Swift
- `validate` in the `MessageBackup` module in TypeScript


### OnlineBackupValidator: Validation of a backup as it's being made

Feed frames into the validator to check them one by one, but don't forget to finalize the backup (`close` in Java, `finalize` in Swift and TypeScript) to run the end-of-file checks!

This is usually going to be faster than the bulk validation because it skips the decryption and decompression steps, but of course this also means the encryption and compression of the backup being created aren't tested.

Only logs soft errors rather than returning them in a manipulatable form.


### ComparableBackup: "Canonicalization" of a backup for testing purposes

Takes an **unencrypted** backup as input and produces a JSON string as output, which can be formatted and diffed as JSON or just line-by-line. Don't forget to check for any unknown fields that get reported as well; they won't be included in the JSON and could invalidate any comparison.

Note that this format should not be considered stable (i.e. don't persist it or try to parse it). It's only intended for comparing two backups to each other; the usual way this is used is for an app to import Backup A and then immediately export its data as Backup B, then verify that the results are identical.


## BackupAuthCredential

The BackupAuthCredential types follow the usual zkgroup construction from the client's perspective, similar to the other AuthCredential variants:

```pseudocode
let requestContext = BackupAuthCredentialRequestContext.create(backupKey, aci)
let httpResponse = goRequestBackupAuthCredentialsFromServer(requestContext.getRequest())
for each response in httpResponse {
    let expectedRedemptionTime = start of each day
    let credential = requestContext.receive(response, expectedRedemptionTime, serverParams)
    log(credential.backupLevel, credential.type)
    goSaveCredential(credential, expectedRedemptionTime)
}
```

```pseudocode
let presentation = credential.present(serverParams)
goDoSomeBackupOperation(presentation)
```

More information on these credentials in the client/server docs.

---

## File: doc/src/README.md

# Introduction

libsignal contains platform-agnostic APIs used by the official Signal clients and servers, exposed as a Java, Swift, or TypeScript library. The underlying implementations are written in Rust.

This documentation is meant primarily for developers working at Signal who will be calling the libsignal APIs, and secondarily for those who maintain libsignal itself. It's meant to be a high-level guide to what's available, and generally won't contain implementation details or detailed API-by-API descriptions. It's *not* meant to be any sort of promise or commitment across versions of the library.

That is, if you're outside of Signal, please don't read too much into these.


## Viewing the book

First, [install mdBook](https://rust-lang.github.io/mdBook/guide/installation.html). Then, from the `doc` directory:

```console
% mdbook serve
2025-01-21 18:05:27 [INFO] (mdbook::book): Book building has started
2025-01-21 18:05:27 [INFO] (mdbook::book): Running the html backend
2025-01-21 18:05:27 [INFO] (mdbook::cmd::serve): Serving on: http://localhost:3000
2025-01-21 18:05:27 [INFO] (mdbook::cmd::watch::poller): Watching for changes...
2025-01-21 18:05:27 [INFO] (warp::server): Server::run; addr=[::1]:3000
2025-01-21 18:05:27 [INFO] (warp::server): listening on http://[::1]:3000
```

Now you can open the URL listed (probably <http://localhost:3000>) and view the rendered book. This is also a "watch" mode, which is convenient when editing the book---just save and watch the page reload.

---

## File: doc/src/SUMMARY.md

# Summary

[Introduction](README.md)

- [Backups](backups/README.md)
- [Networking](net/README.md)
  - [CDS]()
  - [Chat](net/chat.md)

---

## File: rust/protocol/fuzz/README.md

This directory contains fuzz targets used with `cargo fuzz`.

```
// In the parent directory (rust/protocol)
cargo install cargo-fuzz
cargo fuzz list
cargo fuzz run <fuzz-target>

// If you find a crash
RUST_BACKTRACE=1 cargo fuzz run -D <fuzz-target> <crash-artifact>
```

For more information, including how to check the coverage of the explored corpus, see <https://rust-fuzz.github.io>.

---

## File: rust/message-backup/README.md

This crate contains a validator for Signal's backup file format. Structurally, these files are an
encrypted series of length-delimited protos, following the rules described in [backup.proto](src/proto/backup.proto).


## Updating backup.proto

1. Install Rust and the other prerequisites as described in the top-level [README](../../README.md).

2. Copy in the new version of backup.proto.

3. Run `cargo test -p libsignal-message-backup` to find out what needs updating.

4. Fix all errors by copying from the logic for similar fields.

    - This will be easier if you have a Rust-capable IDE, like VS Code with the rust-analyzer extension, or RustRover / IDEA with the Rust plugin.

    - It's okay to not do any validation of new fields right away; just make sure they're populated in the corresponding Rust type.

    - Usually you'll change two files: the file that parses and validates the protobuf, and the scrambler file that walks the protobuf message tree.

    When the above command runs cleanly, you're done.

5. Mention the change in the [upcoming release notes](../../RELEASE_NOTES.md). [This can be pretty simple.](https://github.com/signalapp/libsignal/commit/4723fdbba1970c91795f8cdd8b7bcc4eafb66114) If there are other backup updates, group them together.

6. Open a PR and let a libsignal maintainer know what validation should be added later, if any.


## Implementation Details

The crate is used in some specific ways that have led to unusual design decisions, particularly concerning the model types in the `backup` submodule:

- We use the `protobuf-rs` crate instead of the more popular, better-supported, and used-elsewhere-in-libsignal `prost` because `prost` doesn't support doing anything with unknown fields. (We just want to report them, we don't need to store them in every message. But even that doesn't seem interesting to the mainline prost folks. See <https://github.com/tokio-rs/prost/issues/2>.)

- The model types roughly parallel the protobuf message types, but they use stronger types for their fields, representing the stronger constraints of a valid backup. For example, a contact's ACI will be represented using `libsignal_core::Aci` rather than a plain protobuf `bytes` (`Vec` in Rust).

- Since we want to be sure we don't ignore validity constraints, we don't use `..` when matching the protobuf struct types; we always want to explicitly enumerate all the fields. Similarly, since we want the model types to always represent *validated* data, even elsewhere in the crate, we often include a non-public `_limit_construction_to_module` member, forcing construction to go through the conversion from a protobuf message or reuse deliberately-constructed test data (even if they modify the value after).

- The client apps do a lightweight "validation" whenever they generate backups, but they also want to do a full deserialization in their tests (mostly using the "comparable" form described below). Therefore, many of the types nearer the root of the model are parameterized with a `Method`; the two methods are `Store` and `ValidateOnly`, where the latter minimizes memory use by throwing out most of what it reads once it's been validated.

- The model types implement `serde::Serialize`, but are deliberately *not* trying to be a fully value-preserving, round-trip mechanism. Rather, the Serialized form represents a "comparable" form, one that has gone through some canonicalization, so that disparate backup implementations can still be compared. A restore implementation should aim to satisfy `serialize(backup) == serialize(export(restoreFrom(backup)))`.

    This is exposed to the client apps using pretty-printed JSON, since getting good output from a structural diff algorithm is hard and the goal should be "no differences" anyway.

    (The fully value-preserving, round-trip mechanism for serializing a backup is to keep it in the pre-validated protobuf form.)

## Updating the test data

Sometimes, when introducing new required fields, test data will need to be updated. Most of the ".jsonproto" files can be updated automatically by setting an `OVERWRITE_EXPECTED_DATA=1` environment variable prior to running the tests.

This, however, is not the case for the tests in test-cases/ folder. ".jsonproto" files there will need to be updated manually.

If you find yourself needing to update a ".binproto" file, use the combination of "json_to_binproto" and "binproto_to_json" tools located in the examples/ folder.

In the worst case, when you need to update the ".binproto.encrypted" files, use the "encrypt_backup" tool providing it "--hmac-key" and "--aes-key" from the test output as well as the "--iv 49494949494949494949494949494949" (which corresponds to 16 characters 'I' for IV). BE AWARE that encrypt_backup tool will take ANY input you provide. Tests expect a serialized protobuf bytes (a.k.a "binproto") to be fed into encrypt_data in order to produce ".binproto.encrypted" files.

---

## File: rust/bridge/README.md

# Overview

In order for `libsignal` to be used from Java, Swift, and TypeScript, the Rust
code must be exposed, or "bridged" to those languages with compatible entry
points. That's the purpose of the crates in this directory.

# Bridged function definitions

[`libsignal-bridge`](./shared/) defines functions that are callable from Java,
Swift, and TypeScript. These definitions make use of the `#[bridge_fn]` and
`#[bridge_io]` macros to provide the language-specific glue code that makes the
functions visible from other languages.

[`libsignal-bridge-macros`](./shared/macros/) is a [proc-macro] crate that
defines the `#[bridge_fn]` and `#[bridge_io]` macros. Attaching one of these
macros to a function definition causes the macro to emit `#[cfg]`-guarded entry
point for each supported language. These entry points, before calling the actual
annotated Rust function, perform functionality like language-specific parsing
and [conversion](#type-conversions) of input arguments, panic trapping, and
spawning of tasks for `async` functions. They also handle return value
conversion and language-specific error handling.

The macros attach any needed code to the bridged functions to enable
language-specific mechanisms ([more below](#bridged-function-collection)) to
identify and collect the entry points and make them callable.

# Bridged types

The [`libsignal-bridge-types`](./shared/types/) crate defines traits and
impls that reduce the amount of boilerplate code required to write bridged
functions. These generally fall into three categories:

1. type conversions
2. runtime support
3. language utilities

## Type conversions

Input arguments are converted from their language-native form into Rust types
using the `ArgTypeInfo` traits defined for each language in the per-language
submodule ([`libsignal_bridge_types::ffi`], [`libsignal_bridge_types::jni`], and
[`libsignal_bridge_types::node`]). To make a type usable as an  argument to a
function annotated with `#[bridge_fn]` or `#[bridge_io]`, the three
`ArgTypeInfo` traits need to be implemented for that type, either directly or
indirectly. The type also needs to be added to the `ffi_arg_type`,
`jni_arg_type`, and `node_arg_type` macros.

Returned types are treated similarly, except that the language-specific traits
are named `ResultTypeInfo`, and the macros are `ffi_result_type` and similar.

## Runtime support

Some Rust features aren't exposed directly to other languages, but instead
require some runtime support. As an example, when `#[bridge_fn]` is attached to
an `async` function, the exposed entry point takes as an additional argument the
async runtime to be used. `libsignal_bridge_types` defines traits that support
this.

## Language utilities

Some languages require additional code to support bridged functions. The `jni`
module, for example, defines a function to be invoked when the library is loaded
that caches a class loader for use during later return type conversions. The
`node` module defines a function that registers a sequence of annotated
functions with a JavaScript runtime.

# Bridged function collection

To make the function entry points generated by the `#[bridge_fn]` and
`#[bridge_io]` macros visible to the target languages, the functions must be
collected and exposed. The mechanism for this is language-specific, but relies
on annotations attached to the entry points.

For Swift, and Java, [`cbindgen`] is used to process the
[`libsignal-ffi`](./ffi/) and [`libsignal-jni`](./jni/) crates to produce a list
of function prototypes. For Java, these prototypes are munged by the
[`gen_java_decl.py`](./jni/bin/gen_java_decl.py) and interpolated into the
[`Native.java.in`](./jni/bin/Native.java.in) file to produce a self-contained
class definition. For Swift, the `cbindgen` output is saved directly to a
C-style `.h` file that the Swift toolchain can consume.

For TypeScript, the [`libsignal-node`] crate is expanded and processed by
[`libsignal-node-native_ts`](./node/native_ts/src/main.rs) and the output is interpolated into
[`Native.ts.in`](./node/native_ts/src/Native.ts.in). The output, however, only
declares the function signatures; to make them accessible to the JavaScript
runtime, additional machinery is used. This takes the form of `#[linkme]`
annotations on to the generated entry points; the [`linkme`] crate is used to
collect all these entry points at link time for explicit registration at
runtime.


[`libsignal_bridge_types::ffi`]: ./shared/ffi/
[`libsignal_bridge_types::jni`]: ./shared/jni/
[`libsignal_bridge_types::node`]: ./shared/node/
[`libsignal-node`]: ./shared/node/
[proc-macro]: https://doc.rust-lang.org/reference/procedural-macros.html
[`linkme`]: https://crates.io/crates/linkme
[`cbindgen`]: https://crates.io/crates/cbindgen