Your conversations. Your privacy. Your freedom. A messenger that depends on no central operator. Chat privately, create groups and transfer files, all encrypted. Messages are encrypted and decrypted directly on the devices involved: servers carry the data without holding the keys to read it.
  • C# 96.8%
  • PowerShell 2.9%
  • Shell 0.3%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Thomas Gourgnier 94175f31e0 Initial commit
2026-10-21 15:27:39 +02:00
scripts Initial commit 2026-10-21 15:27:39 +02:00
sources Initial commit 2026-10-21 15:27:39 +02:00
.gitignore Initial commit 2026-10-21 15:27:39 +02:00
LICENSE Initial commit 2026-10-21 15:27:39 +02:00
README.fr.md Initial commit 2026-10-21 15:27:39 +02:00
README.md Initial commit 2026-10-21 15:27:39 +02:00

FreeSpeech: Chats FreeSpeech: Menu FreeSpeech: Chat

FreeSpeech

End-to-end encrypted messaging with no account, on servers that keep nothing.

Licence AGPL-3.0-or-later .NET 10 Android 7 and later

Website · Google Play · Protocol · Architecture · Français


FreeSpeech is a messenger you can use without giving anyone a phone number or an e-mail address. Your identity is computed on your phone from three phrases, and everything you send is signed and encrypted there before it leaves the device.

The servers do one thing: they pass messages between devices that are connected. They store nothing. When the recipient is offline, the message waits on the sender's phone and goes out as soon as the recipient is back.

You can be on several servers at once, public or private, and anyone can run one. The protocol is specified in this repository and does not depend on any particular framework, so a client or a server can be written in any language.

Features

FreeSpeech does what you expect from a messenger: text, replies, emoji reactions, editing and deleting a message (for yourself or for everyone), a typing indicator, read receipts that can be switched off, groups, photos, videos and files, and a mute switch per chat. A message whose first line is md is shown formatted (titles, lists, bold, italic, links).

A group has a single shared key. It changes every time someone joins, leaves or is removed, which is what stops an excluded person from reading what comes next. A member who was offline asks the others for what they missed, and the author of those messages does not have to be around. An invitation is never automatic: the person invited has to accept it, and chooses there the name and picture they will show in that group, different from the ones they use elsewhere: two groups cannot be linked by name or picture. A check marks, in a group, the members who are your contacts (gold once you have compared their safety number).

Files go up to 2 GiB; the app asks you to confirm before sending a large one (the size is yours to set), a received file shows its size before you fetch it, and a relay can cap the volume each person sends per day. Files are cut into 48 KiB chunks, each checked against a Merkle proof, and an interrupted transfer picks up where it stopped. In a group, anyone who already has the file can send part of it, so the sender's phone is not the only source. Photos are resized and videos recompressed before they leave.

Two people can compare a safety number to check that nobody tampered with their key exchange. The local database is encrypted and a PIN is optional. The three phrases can be printed on a sheet with a QR code, or kept in a file protected by a password.

The Android app adds contacts with a QR code or a one-time card, runs the same identity on several devices, accepts content shared from other apps, and is translated into French, English and Spanish. Notifications only wake the app up. They never contain the text of a message.

Design

Your three phrases go through Argon2id and HKDF and give two key pairs, one for encryption (X25519) and one for signing (Ed25519). The fingerprint of the public keys is your identifier. Nothing is registered anywhere to get it.

To become contacts, two people swap a card that holds their public keys, a nickname and the servers they use, either as a QR code or with a one-time code.

The server only comes in when you send something. The app signs the message, encrypts it with a fresh ephemeral key and hands it to a server over a WebSocket. The server can read the envelope, meaning who it is from and who it is for, and nothing else. If the recipient is connected, the server delivers the message, going through a neighbouring server when that is where the recipient is connected. If not, the message stays in the sender's outgoing queue. A notification wakes the recipient's phone, the app reconnects and the queue empties. The recipient checks the signature, then decrypts.

   Alice                                                  Bob
  (phone)                                                (phone)
      │ message signed, then encrypted                     ▲
      ▼                                                    │
 ┌──────────┐    link accepted by both        ┌──────────┐ │
 │ Relay A  │◄──────── administrators ───────►│ Relay B  │─┘
 └──────────┘                                 └──────────┘
   keeps nothing                                keeps nothing

A new app that knows no relay yet asks a directory once, on its very first connection, which public relays accept new accounts. The directory answers with a signed list and keeps nothing about clients. After that it is no longer involved. Two public servers become neighbours when both of their administrators accept the link. The diagrams are in sources/diagrams/; the files ending in -en are in English.

Cryptography

Use Primitive
Identity, derived from the 3 phrases Argon2id (32 MiB, t = 3), then HKDF-SHA256
Key agreement for each message Ephemeral X25519, HKDF-SHA256
Encryption ChaCha20-Poly1305
Signature Ed25519, bound to the sender, the recipient and the message type
Groups One shared key per epoch, renewed on every membership change
Files 48 KiB chunks, SHA-256 Merkle tree
Local database SQLite, content encrypted field by field
Linking two servers One ECDSA P-256 key per server, signed calls, a ±2 minute window, replay protection

The primitives come from BouncyCastle, which is managed code and runs on Android and iOS. Fixed test vectors check them.

Limits

  • A server sees who writes to whom, when, and when each device connects. It never sees the content.
  • There is no ratchet. If someone captured past traffic and later steals a recipient's long-term key, that traffic can be decrypted.
  • Losing the three phrases loses the identity. There is no recovery.
  • Deleting a message for everyone and editing a message are best effort. A modified client can ignore them, and the recipient may have copied the message already.
  • A group member can pass the current key to someone who was excluded, outside the protocol.
  • Received files are stored unencrypted in the app's protected storage.
  • The PIN protects the screen, not the encryption.

The security model is described in full in ARCHITECTURE.en.md, section "Security model and known limits".

Repository layout

Folder Content
sources/freespeech.relay The relay, in ASP.NET Core 10
sources/freespeech.client The client library (C#, .NET 10, no UI): identity, contacts, servers, messages, files, groups, storage
sources/freespeech.mobile The .NET MAUI app for Android, with a Windows build for working on the interface
sources/freespeech.desktop The Avalonia desktop client (solution sources/FreeSpeech.Desktop.slnx)
sources/freespeech.console A console client for testing, on top of the library (Windows)
sources/freespeech.push The notification gateway
sources/freespeech.directory The relay directory, used at the very first connection
sources/freespeech.ops Code shared by the relay and the gateway: administration command line, linking of servers, logs
sources/freespeech.tests xunit tests
sources/deploy/ Service files, nginx files and configuration examples
sources/diagrams/ Diagrams, in French and English
scripts/ End-to-end scenarios with real clients and real servers

Getting started

Use the app. Join the open test on Google Play. You can also download the APK from freespeech.rebisway.com/download and compare the SHA-256 fingerprint shown on that page with the file you got. In Settings > Servers, https://fs.rebisway.com is the reference server, and you can add your own.

Run a relay. INSTALL_RELAY.en.md goes step by step through Ubuntu 24, nginx, systemd and the certificate. The administration commands are in cli-relay.en.md.

Build and test. You need the .NET 10 SDK.

dotnet build sources/FreeSpeech.slnx
dotnet test sources/freespeech.tests

To try it locally, start a server and two clients, each with its own profile:

dotnet run --project sources/freespeech.relay --no-launch-profile --urls http://localhost:5299
dotnet run --project sources/freespeech.console -- alice http://localhost:5299
dotnet run --project sources/freespeech.console -- bob http://localhost:5299

The first time a profile starts, the console asks for three recovery phrases of at least 75 characters each, all different. help lists the commands. On Windows PowerShell, scripts\dev-run.ps1 opens the server and several clients at once, and scripts\test-groups.ps1, scripts\test-attachments.ps1 and scripts\test-multiserver.ps1 replay complete scenarios.

The mobile app has its own solution. It needs the .NET Android workload, and the repository must sit in a path without accented characters.

dotnet build sources/FreeSpeech.Mobile.slnx -f net10.0-android36.0
dotnet build sources/FreeSpeech.Mobile.slnx -f net10.0-windows10.0.19041.0   # run it on the PC

Default addresses. The mobile app and the desktop client start with example servers (relay.exemple.org, push.exemple.org). To build with your own, copy sources/freespeech.defaults.props.example to sources/freespeech.defaults.props and set FsDefaultServer there. The address is stored in the assembly at build time, and none is written in the source. The push gateway is not compiled into the app: each relay uses the gateways its administrator linked it with.

Documentation

Français English
Protocol PROTOCOL.fr.md PROTOCOL.en.md
Protocol API reference PROTOCOL_API.fr.md PROTOCOL_API.en.md
Architecture, encryption, storage ARCHITECTURE.fr.md ARCHITECTURE.en.md
Installing a relay INSTALL_RELAY.fr.md INSTALL_RELAY.en.md
Administering your servers ADMINISTRATION.fr.md ADMINISTRATION.en.md
Relay command line cli-relay.fr.md cli-relay.en.md
Push gateway command line cli-push.fr.md cli-push.en.md
Installing the push gateway INSTALL_PUSH.fr.md INSTALL_PUSH.en.md
Installing a relay directory INSTALL_DIRECTORY.fr.md INSTALL_DIRECTORY.en.md
Several servers on one machine INSTALL_HOST.fr.md INSTALL_HOST.en.md
Diagrams sources/diagrams/ (-fr) sources/diagrams/ (-en)
Class and database diagrams uml/, db/ uml/, db/

Contributing

Bug reports, reviews of the cryptography and of the protocol, tests and fixes are all welcome. Please read the protocol and the architecture before proposing a change. Code comments are in English, every source file carries the licence header, and each document comes in French and in English (a .fr.md file and a .en.md file).

To report a vulnerability, write to contact@rebisway.com.

Licence

FreeSpeech is free software under the GNU AGPL v3.0 or later (AGPL-3.0-or-later); see LICENSE. © 2024-2026 Thomas Gourgnier.

The server is a network service, so the AGPL requires anyone who runs a modified version to offer its source code to the users of that service. Contributions will be accepted under a contributor agreement (CLA) that allows a commercial licence to be offered alongside.