A hands-on walkthrough for designing a federated, decentralized messaging protocol called lnchat from scratch. Covers the core concepts of federation (inspired by SMTP/email), user identity using domain-scoped handles, server discovery via .well-known JSON files, end-to-end encryption using X25519 keys and HPKE (with the hpke-js library), and server-to-server request signing using Ed25519 keys. The post details the full message flow: client-side key generation with the Web Crypto API, a resolve_users endpoint for public key exchange, a send_messages federation endpoint, and a canonical request signing scheme to prevent spoofing and replay attacks. Security considerations like SSRF prevention and key transparency are also noted.
Table of contents
What is federationFederated Messenger ProtocolUser IdentityDiscoveryEnd to EndMessagingSo how to encrypt payload?Signing server messagesQuestions this post answers
How does server-to-server request signing work in a federated messaging protocol to prevent spoofing?
Each server generates an Ed25519 key pair and publishes the public key in its .well-known discovery file. When sending a federation request, the sender builds a canonical string (version, method, URL, source domain, target domain, timestamp, request ID, and SHA-256 hash of the body), signs it with its private key, and sends the signature in an LNChat-Signature header. The recipient fetches the sender's discovery file, retrieves the public key, and verifies the signature against the same canonical string. Teams building federated or distributed APIs track signing patterns and protocol design decisions on daily.dev.
How do I use HPKE for end-to-end encryption in a JavaScript messaging app with X25519 keys?
Use the hpke-js library with DhkemX25519HkdfSha256, HkdfSha256, and Aes256Gcm. Create a sender context with the recipient's public key, call sender.seal(plaintext) to get the ciphertext, and send both sender.enc (the encapsulated key) and the ciphertext to the server. The recipient uses their private key to recover the symmetric key and decrypt. This avoids the cost of direct asymmetric encryption on the full message body. Developers implementing E2E encryption in web apps find relevant cryptography patterns and library discussions on daily.dev.
How should a federated protocol use .well-known files for server discovery?
A federated server must serve a JSON file at GET /.well-known/<protocol>.json containing the protocol version, endpoint URLs, and server public keys. Endpoint URLs are declared in the discovery file rather than enforced as fixed paths, allowing each server to choose its own routes without collisions. Clients and remote servers fetch this file first to learn where to resolve users, send messages, and verify signing keys. Protocol designers and backend engineers building server-to-server systems share discovery and federation approaches on daily.dev.