A DNS-tunneled encrypted chat system. Uses the DNS protocol as a covert transport layer for end-to-end encrypted messages.
DNS is the "universal protocol" that is almost never blocked, even on the most restrictive networks. dnschat leverages this to provide connectivity where other tools fail:
- Bypassing Captive Portals: Many public Wi-Fi networks (hotels, airports, coffee shops) block all traffic until you pay or sign in, but allow DNS queries to resolve.
dnschatcan tunnel through these restrictions. - Highly Restrictive Firewalls: In environments where only DNS (port 53) is permitted,
dnschatprovides a functional communication channel without requiring VPNs or complex proxies. - Covert Communication: Because the traffic looks like standard DNS queries for a specific domain, it can blend in with normal network noise more easily than direct TCP/IP connections.
- No Direct IP Connectivity: Clients only need to reach a recursive DNS resolver (like 8.8.8.8 or their local gateway), not the
dnschatserver directly.
- Transport: Messages are Base32-encoded and embedded into DNS
TXTquery labels (e.g.,<chunk>.send.chat.example.com). - Relay: A custom authoritative DNS server receives these queries, reassembles the chunks, and stores them in the recipient's "inbox" in memory.
- Security: Everything is End-to-End Encrypted (E2EE). The server is a blind relay—it only sees opaque encrypted blobs and never has access to your private keys or plaintext.
- Retrieval: The recipient polls their inbox via DNS queries and receives the encrypted blobs back as DNS
TXTresponses.
See SPEC.md for the full protocol specification (v0.3).
The server is a custom authoritative DNS server that acts as a blind message relay. It never sees plaintext — all payloads are encrypted client-side.
| Endpoint | Purpose |
|---|---|
register |
Publish a user's X25519 public key |
bind |
Associate an email with a key ID |
unbind |
Remove an email binding |
lookup |
Find a public key by username |
resolve |
Find a public key by email |
send |
Submit an encrypted message (chunked) |
inbox |
Retrieve pending messages |
ack |
Acknowledge and delete a message |
# Default: listens on 0.0.0.0:5353, domain chat.example.com
python server.py
# Custom settings
python server.py --host 127.0.0.1 --port 5353 --domain chat.mydomain.com --debug
# All options
python server.py --helpNo dependencies beyond Python 3.10+ standard library.
python -m unittest test_server -vTo use this in production, you need to delegate a subdomain to this server:
- Add an NS record:
chat.example.com. NS ns-chat.example.com. - Add a glue A record:
ns-chat.example.com. A <your-server-ip> - Run the server on port 53 (requires root):
sudo python server.py --port 53 --domain chat.example.com
┌─────────┐ DNS TXT query ┌──────────────────┐
│ Client │ ──────────────────▶│ dnschat server │
│ (sender) │ │ │
└─────────┘ TXT "OK:c0fe:0" │ ┌────────────┐ │
◀──────────────── │ │ Store │ │
│ │ ┌─keys────┐ │ │
┌─────────┐ DNS TXT query │ │ ┌─emails──┐ │ │
│ Client │ ──────────────────▶│ │ ┌─inboxes─┐ │ │
│ (recvr) │ │ └────────────┘ │
└─────────┘ TXT "<msg_data>" │ │
◀──────────────── └──────────────────┘
Storage is in-memory. Restarting the server clears all state.