Two small terminal programs:
rendezvous_server: presence/discovery server (never relays chat content)p2p_chat_gui: Qt6 desktop chat client (friends list + chat pane)
Suggested packages:
sudo apt-get update
sudo apt-get install -y build-essential pkg-config libboost-system-dev libssl-devNotes:
- JSON uses a vendored
nlohmann/json.hppinthird_party/nlohmann/json.hpp(no system package required).
Build:
make -j-march=native:
- Enabled by default for local
makebuilds. - Auto-disabled on GitHub Actions (
GITHUB_ACTIONS=true). - Override with
make NATIVE_ARCH=0 -j(disable) ormake NATIVE_ARCH=1 -j(enable).
Qt GUI build (Qt6 Widgets):
sudo apt-get install -y qt6-base-dev qt6-base-dev-tools qt6-multimedia-dev libopus-dev
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j
./build/p2p_chat_guiCMake also enables -march=native by default for local non-CI, non-cross builds.
Override with -DP2PCHAT_ENABLE_MARCH_NATIVE=OFF.
Most reliable: build on Windows (or CI Windows runner). Cross-compiling from Linux works best with MXE (MinGW + Qt).
High-level MXE steps:
- Build MXE toolchain + deps (Qt6, OpenSSL, Boost).
- Configure this project with MXE’s
mxe-conf.cmake. - Build
p2p_chat_gui.exe(and optionallyrendezvous_server.exe). - Bundle Qt DLLs via
windeployqt(from the same Qt toolchain).
Example (MXE, 64-bit, shared):
git clone https://github.com/mxe/mxe.git ~/mxe
cd ~/mxe
make MXE_TARGETS='x86_64-w64-mingw32.shared' qt6-qtbase openssl boost
cd /path/to/p2p-chat
cmake -S . -B build-win \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_TOOLCHAIN_FILE=~/mxe/usr/x86_64-w64-mingw32.shared/share/cmake/mxe-conf.cmake
cmake --build build-win -jAfter build, run MXE’s windeployqt on p2p_chat_gui.exe to collect required Qt DLLs into a distributable folder (zip that folder to send to friends).
This is often simpler than cross-compiling:
- Install MSYS2, open MSYS2 MinGW x64 shell
- Install deps:
pacman -Syu
pacman -S --needed \
mingw-w64-x86_64-gcc mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja \
mingw-w64-x86_64-qt6-base mingw-w64-x86_64-qt6-multimedia mingw-w64-x86_64-opus \
mingw-w64-x86_64-boost mingw-w64-x86_64-openssl \
mingw-w64-x86_64-pkgconf- Build + bundle:
cmake -S . -B build-win -G Ninja -DCMAKE_BUILD_TYPE=Release
cmake --build build-win
mkdir dist
cp build-win/p2p_chat_gui.exe dist/
windeployqt --release --no-translations dist/p2p_chat_gui.exeServer (public VPS):
./rendezvous_server 0.0.0.0 5555Clients:
./build/p2p_chat_gui
# or force runtime debug logs
P2PCHAT_DEBUG=1 ./build/p2p_chat_gui
# or
./build/p2p_chat_gui --debugProfiles:
- On startup, the GUI shows a profile picker (qTox-style): select existing profile or create a new one.
- New profiles can be created with optional password protection (encrypts
identity.pemat rest). - Legacy single-profile data in
~/.config/p2p-chatis auto-migrated to~/.config/p2p-chat/profiles/<name>/. - Headless migration only:
QT_QPA_PLATFORM=offscreen ./build/p2p_chat_gui --migrate-profilesCLI profile selection:
./build/p2p_chat --profile default
./build/p2p_chat --profile secure --profile-password 'your-password'Both GUI and CLI read profile data from the same profile directory and therefore share the same ID/key for that profile.
Voice calls:
- Requires Qt6 Multimedia + Opus.
- Use the top-right
Callbutton in a chat; configure devices/bitrate viaOptions -> Audio Settings....
IDs and names:
- Each user has a cryptographic shareable ID (
Your ID:) derived from their Ed25519 public key. - Each profile stores its identity key as
identity.pemin its own profile directory. - Your name is only shared with peers after they accept your friend request and you establish a direct P2P connection.
Encryption:
- P2P traffic is end-to-end encrypted and authenticated (X25519 + HKDF-SHA256 + ChaCha20-Poly1305), with peer authentication via Ed25519 signatures (your ID).
- The rendezvous server channel is not encrypted (it never relays chat content, only discovery + requests).
Discovery / UDP:
- Clients publish their current UDP endpoint to rendezvous and connect peer-to-peer via UDP hole punching.
- Rendezvous stores observed address + UDP mapping only; it does not maintain a TCP reachability flag.