Bitpart is a messaging tool that runs on top of Signal to support activists, journalists, and human rights defenders.
This repository contains three components: the Bitpart server, a command-line client for connecting to the Bitpart server, and a storage adapter used for storing Signal key information to Bitpart's database. In order to build all three components, make sure you have a 2024 edition of Rust and run:
cargo build --release
Visit the releases page to download the latest binaries for your operating system and architecture!
The Bitpart server expects certain configuration parameters. The following examples use the parameters defined below:
<BIND>: the IP address and port that Bitpart listens on for client connections. For example,127.0.0.1:3000would mean that Bitpart is listening on port 3000 on localhost. This is only used by the command-line client and is not necessary to expose to the public internet.<AUTH>: the token used to authenticate client connections on the above port. The token must match for both the server and the command-line client for them to be able to connect.<DATABASE>: the path to an SQLite database file where Bitpart stores its state. This file is created if it does not exist.<KEY>: the encryption key for the SQLite database. Bitpart uses an integrated copy of SQLCipher to encrypt its database. If Bitpart creates a new database file, it will be initialized with this key. The key must be the same between different runs of Bitpart or otherwise it will not be able to decrypt its database.
Assuming the bitpart binary is in your path, you can view the inline help for the Bitpart server:
bitpart --help
Or run it as follows:
bitpart --bind <BIND> --auth <AUTH> --database <DATABASE> --key <KEY>
Bitpart can also read configuration parameters from environment variables corresponding to its command-line parameters. For example, you could specify the encryption key via defining the environment variable BITPART_KEY.
Bitpart is available in a Docker-compatible container. For example, to run Bitpart on port 3000 and mounting a database from the current directory:
docker run -d --name bitpart -p 3000:3000 -v ./bitpart.sqlite:/bitpart.sqlite -e BITPART_BIND=127.0.0.1:3000 -e BITPART_DATABASE=/bitpart.sqlite -e BITPART_AUTH=connect to the Bitpart server<AUTH> -e BITPART_KEY=<KEY> ghcr.io/throneless-tech/bitpart:latest
Assuming the bitpart-cli binary is in your path, you can print out the inline help for the command-line client via:
bitpart-cli --help
Or print out the help for a given subcommand via:
bitpart-cli help <SUBCOMMAND>
For example, to list available bots on a Bitpart server listening at <BIND> and with authorization token <AUTH>:
bitpart-cli --auth <AUTH> --connect <BIND> list
When you have the server running as described above, and you have bitpart-cli able to connect to it at location <BIND> with authorization token <AUTH>, you can add a bot:
bitpart-cli --auth <AUTH> --connect <BIND> add --id <BOT_ID> ./<BASENAME>.csml
where <BOT_ID> is a unique name you choose for the bot, and <BASENAME>.csml is the bot's CSML script. You can include multiple CSML scripts; if you do, pass --default <BASENAME> to choose which one is used by default for new conversations. To find out more about CSML scripting, check out the example(s) in the examples directory in this repository.
To have new conversations with the bot use Signal disappearing messages, add --expire-timer <SECONDS> (for example 604800 for one week). The timer is set when a user first contacts the bot; after that, any change the user makes to the timer in their Signal app is left in place. When you add a new version of an existing bot without --expire-timer, it keeps the previous version's timer; use --expire-timer 0 to remove it.
If the bot's Signal account is added to a group, it only responds to messages that @mention it. Each group member has their own conversation with the bot in that group, separate from any direct conversation they have with it, and the bot's replies are sent to the group.
To link the bot you created to Signal so that it receives messages, you must open a channel between the bot and a Signal account. We recommend using a separate Signal account just for this purpose, since Bitpart will also receive and respond to the Signal messages sent to this account.
bitpart-cli --auth <AUTH> --connect <BIND> channel-link --id <CHANNEL_ID> --bot-id <BOT_ID> --device-name <DEVICE_NAME> [--profile-name <PROFILE_NAME>]
where <CHANNEL_ID> is a name you choose for this Signal account within the bot (for example, signal), <BOT_ID> is the id of the bot you're linking, and <DEVICE_NAME> is the name of the device as it will appear in the list of linked devices on Signal (for example, bitpart). If you pass --profile-name, the Signal profile name of the linked account is set to <PROFILE_NAME> once linking finishes; because the bot is linked as a device of that account, this changes the name shown for the account everywhere, including on the device you linked it from.
A bot can have several channels, each linked to a different Signal account; give each one its own <CHANNEL_ID>. Conversations are kept separately per channel, so someone who messages two of a bot's accounts has a separate conversation with each. To relink a channel whose Signal account was disconnected, run the same command again with the same <CHANNEL_ID>: only that channel is stopped and relinked, and the bot's other channels keep running. Relinking discards that channel's Signal keys and sessions, since Signal issues new ones for the new link, but its conversations with users are kept.
After you enter this command, a QR code will be displayed. In your Signal client, go to Settings -> Linked Devices -> Link new device and take a picture of the QR code. After a few seconds, your bot will finish linking with Signal. From another Signal device, send a message to the number or username associated with the bot (NOTE: unless you pass --profile-name, the bot will take on the profile information of the linked device) and it should reply!
To see a bot's definition and the state of each of its channels:
bitpart-cli --auth <AUTH> --connect <BIND> describe --id <BOT_ID>
After the bot definition, each channel is listed with its connection status (linked, failing or unlinked) and how many messages it has sent, received and dropped since the server started. If the channel's most recent send failed, a line underneath says why: Signal rate-limited it (with the time after which it can send again), Signal requires a captcha (with a challenge token), or the send failed for another reason. A dropped message is not retried.
Bitpart paces its own sending to stay under Signal's rate limits, so rate-limited sends should be rare.
Signal may decide that the bot's account is sending suspicious traffic and stop accepting its messages until someone proves a person controls the account. When that happens, describe reports that Signal requires a captcha and shows a challenge token. To unblock the channel:
- Open https://signalcaptchas.org/challenge/generate.html in a browser and solve the captcha.
- When it finishes, the browser offers to open a
signalcaptcha://link. Copy that link. - Submit it for the channel:
bitpart-cli --auth <AUTH> --connect <BIND> channel-challenge --id <CHANNEL_ID> --bot-id <BOT_ID> --captcha '<SIGNALCAPTCHA_LINK>'
The challenge token shown by describe is used automatically. If the Bitpart server has restarted since the challenge was reported, pass it explicitly with --token <TOKEN>.
Signal limits how often a challenge can be answered (at most 10 attempts per account in about 2.4 hours, and 2 successful answers in 12 hours), so a bot that keeps being challenged will need its sending reduced rather than repeatedly answering captchas. Messages dropped while the channel was blocked are not resent.
Bitpart's conversation logic is defined by scripts written in the open-source Conversational Standard Meta Language, or CSML. Visit the documentation from the CSML project to learn how to write a CSML conversation flow. Each instance of Bitpart can run one or more bots, where each bot processes incoming messages according to one or more CSML flows.
Bitpart is a free software project licensed under the GNU Affero General Public License v3.0 (AGPLv3). Parts of it are derived from code from the Presage and CSML projects, marked where appropriate.
