Peer-to-peer encrypted chat. Messages are end-to-end encrypted using the Signal Double Ratchet protocol with X25519 key exchange. No servers, no accounts — just keys.
nix run .#gratcli -- tuiThis builds and launches the interactive TUI. If no identity exists yet, one is generated automatically on first launch.
The project is a Nix flake. You can build or run everything directly without installing Go:
nix build # Build both binaries to ./result/bin/
nix run .#gratserver # Run the server daemon
nix run .#gratcli # Run the CLI
nix develop # Dev shell with Go, sqlc, golangci-lintIf you don't use Nix, make works too (requires Go 1.25+):
make # Binaries go to bin/
make test # Run all testsDocker Compose gives you a two-node chat environment — no conflicts with local sockets or databases:
docker compose up --build -dThis creates two isolated nodes (node1, node2) on a shared network, each with its own identity and database.
Open the TUI on a node:
docker compose exec node1 gratcli tuiPress a in the contacts pane and type the other node's hostname (e.g. node2) to discover it. The discovery protocol connects to the peer, exchanges identities, and stores them automatically. Do the same from the other node and you're ready to chat.
To tear down: docker compose down -v
Two binaries, a handful of packages.
Binaries (cmd/):
gratserver— long-running daemon. Listens on TCP (:1337) for peer connections and on a Unix socket for local IPC fromgratcli.gratcli— CLI tool that talks togratserverover the Unix socket. Sends messages, fetches messages, manages identity, launches the TUI.
Public packages (pkg/):
identity— cryptographic identity: Ed25519 signing key + X25519 DH key, 20-byte base32 address, key bundles, signed network updates.doubleratchet— Signal Double Ratchet (X25519 + AES-GCM).kdfchain/has the root and message KDF chain primitives.netprotocol— binary wire protocol for peer TCP connections. Packet types: Heartbeat, Message, KeyExchange, Discovery. All fixed-size encoding.ipcprotocol— binary wire protocol for local Unix socket IPC between CLI and server.session— ties aDoubleRatchetto aKeyBundleand ephemeral exchange key.SessionManagermaps peer addresses to sessions.
Internal packages (internal/):
nethandle— TCP connection loop, dispatches packets to typed handlers.ipchandle— Unix socket server for local CLI commands.database— SQLite viamodernc.org/sqlite, migrations viagolang-migrate, queries generated bysqlc.
SQL queries live in internal/database/queries/. After editing them, regenerate with sqlc generate (available in the Nix dev shell).