LANMess
Based on Local Network

Whisper
your secrets.

A terminal-based, end-to-end encrypted messenger that operates entirely on your Local Area Network. No internet connection required, no cloud servers, and zero telemetry. Built for security researchers and privacy advocates.

LAN
Required Network
0
Cloud Deps
X25519
Handshake
LANMess
Contacts
(1) alice * ●
bob *
[#1] [10:00] You: hey
[#2] [10:01] alice: safety number matches!
Message or /command (M for menu)...
AES-GCM-256◆ X25519◆ ED25519◆ ARGON2ID◆ HKDF-SHA256◆ HMAC-CHAINS◆ HASH-RATCHET◆ TOTP-8◆ LOCAL-NETWORK◆ AES-GCM-256◆ X25519◆ ED25519◆ ARGON2ID◆ HKDF-SHA256◆ HMAC-CHAINS◆ HASH-RATCHET◆ TOTP-8◆ LOCAL-NETWORK◆

Why choose LANMess?

The modern internet is built on centralization and data harvesting. LANMess is a return to localized, peer-to-peer communication where you control the infrastructure.

Requirement LANMess Cloud Messengers
Internet Connection RequiredNo (if LAN Only)Yes
Data Leaves Your NetworkNeverAlways
Account / Phone Number RequiredNoneUsually
End-to-End EncryptionYes (X25519)Mostly
Terminal User Interface (TUI)NativeNone
Auditable Open Source Code100%Rarely

How everything works.

From installation to encrypted message delivery.

01

Vault Creation

Upon first launch, you set a strong PIN. LANMess generates a random 32-byte master key and encrypts it using Argon2id derived from your PIN.

02

Peer Discovery

The app broadcasts a UDP packet on port 50505. Other instances on the LAN hear this and cache your IP, port, and public keys automatically.

03

Secure Adding

To add a peer, you exchange an 8-digit TOTP code. This proves you share the same local network and are intentionally connecting to each other.

04

Encrypted Chat

A chat request triggers an X25519 ephemeral handshake. Once established, messages are encrypted with AES-GCM and signed with Ed25519.

Cryptographic
Architecture

LANMess does not rely on proprietary security through obscurity. It uses battle-tested, standard cryptographic primitives. Here is how a message is secured:

1

X25519 Handshake

An ephemeral key exchange creates a shared secret that is never sent over the network.

2

HKDF Derivation

The shared secret is expanded into independent send and receive hash chains using HKDF-SHA256.

3

AES-GCM & Hash Ratchet

Each message uses a fresh key from the ratchet, encrypts the padded payload with AES-GCM, and immediately advances the chain.

4

Ed25519 Signatures

Every control frame and message is signed with your long-term identity key to prevent tampering.

tui.py SECURE PAYLOAD
async def send_chat_message(
    self, contact_name, text,
    reply_to=0, react_to=0
):
    async with self._get_contact_lock(contact_name):
        contact = self.db.get_contact(contact_name)

        send_chain_hex = contact.get(
            "send_chain_key_hex", ""
        )
        send_chain = bytes.fromhex(send_chain_hex)
        counter = contact.get(
            "send_counter", 0
        ) + 1

        msg_key = chain_message_key(send_chain)
        padded = pad_message(text.encode("utf-8"))
        nonce = os.urandom(12)
        aad = f"{self.user_name}:{counter}".encode("utf-8")
        ct = aes_gcm_encrypt(
            msg_key, padded, nonce, aad
        )

        signed_data = (
            self.user_name.encode("utf-8")
            + counter.to_bytes(8, "big")
            + nonce + ct
        )
        sig = sign_bytes(self.ed_private, signed_data)
        new_chain = chain_advance(send_chain)

        self.db.set_contact_state(
            contact_name,
            send_chain_key_hex=new_chain.hex(),
            send_counter=counter
        )

Engineered for Privacy.

A complete suite of security features designed for hostile network environments.

Terminal Native

Built with Python and Textual. No web views, no Electron. A highly responsive, keyboard-driven interface.

PIN Lockout

Brute-forcing the vault is impractical. 5 failed attempts trigger a 30-second lockout. 50 attempts trigger a 24-hour lockout.

Audit Log

Security events are chained with HMAC-SHA256. The database cannot be silently modified without breaking the cryptographic chain.

Auto Discovery

UDP broadcast finds peers instantly. No need to manually configure IP addresses for peers on the same subnet.

Safety Numbers

Compare a 60-digit fingerprint out-of-band to verify your contact's identity and prevent man-in-the-middle attacks.

Secure Deletion

SQLite PRAGMA secure_delete is enabled. Symmetric keys are zeroed out in memory upon app lock or termination.

Deep Dive FAQ

Everything you need to know about LANMess. Mined directly from the source code.

1. App Setup & The Vault

What is the "Group Code" used for during setup?

The Group Code derives the initial shared network key using PBKDF2-HMAC-SHA256 with 600,000 iterations. Only users who know the Group Code can generate the same discovery tags and format their packets correctly. It acts as the shared secret for the local network group.

Why must my PIN be at least 12 characters long?

Your PIN unlocks the local vault (which contains your private keys). The config enforces PIN_MIN_LEN = 12 and requires at least 3 of: lowercase, uppercase, digit, symbol. A weak PIN makes the Argon2id encryption vulnerable to offline brute-forcing if your device is stolen.

What encryption is used for my local database?

Your SQLite database (~/.lanmess/lanmess.db) is fully encrypted field-by-field. Every piece of data (identity, contacts, messages) is encrypted using AES-GCM. The master key for this data is wrapped using Argon2id (derived from your PIN) and stored in the vault table.

What happens if I fail my PIN multiple times?

LANMess implements escalating lockouts defined in PIN_LOCKOUT_STEPS: 5 failed attempts → 30-second lockout; 10 → 5-minute lockout; 20 → 1-hour lockout; 50 → 24-hour lockout. These are tracked in the database, so restarting the app does not bypass them.

2. Networking & Discovery

Can I use LANMess over a VPN like Tailscale or ZeroTier?

Yes. LANMess operates on Layer 3 using UDP (port 50505) and TCP (ports 50510-50600). As long as your VPN creates a virtual LAN where broadcast packets or direct IP routing can reach the peers, LANMess will work. You may need to use "Add by IP" if UDP broadcast doesn't traverse the VPN.

What happens if my peer's IP address changes?

The UDP discovery mechanism automatically updates peer IP addresses every 2 seconds (DISCOVERY_INTERVAL). However, if you added the contact using "Add by IP", the contact is marked as pinned. Pinned contacts will not automatically update their IP. If the IP changes, you must re-add them manually.

How does the 8-digit "Add Code" work?

The 8-digit code is a TOTP (Time-Based One-Time Password) generated every 60 seconds (LOGIN_CODE_PERIOD = 60). When peer A wants to add peer B, A enters B's TOTP code. The code is verified using HMAC-SHA256 against the timestamp, proving that A and B share the same local network and that A knows B's current code. It prevents random scanning attacks.

What ports do I need to open in my firewall?

Allow UDP 50505 for discovering peers (DISCOVERY_PORT) and TCP 50510-50600 for sending and receiving actual messages (TCP_PORT_RANGE). If you don't open these, peers will appear offline or messages will fail to deliver.

Does LANMess work across VLANs or subnets?

No, not out of the box. UDP broadcast on port 50505 does not cross VLAN or subnet boundaries. You must either be on the same broadcast domain, use "Add by IP" with a routable address, or tunnel traffic through a VPN that presents a virtual LAN to both peers.

3. Cryptography & Security

What exactly are "Safety Numbers"?

Safety numbers are a 60-digit fingerprint generated by hashing both your Ed25519 public key and your peer's Ed25519 public key. Compare this number with your peer over a secure out-of-band channel (in person, or via a trusted call). If they match, no Man-In-The-Middle can be intercepting your keys.

How does the "Forward Secrecy" hash ratchet work?

For every message sent or received, the symmetric encryption key is destroyed and replaced. The next key is generated by hashing the current key using HMAC-SHA256 (chain_advance). If an attacker steals your current key, they cannot decrypt past messages, because the old keys were permanently deleted from memory.

What does "Verified-only" mode do?

When enabled in the Menu, LANMess actively blocks any chat requests or messages from contacts that have not been explicitly marked as verified (where you've compared safety numbers). It enforces a strict zero-trust policy on your contact list.

What happens if a message signature fails to verify?

If an incoming message fails Ed25519 signature verification, or AES-GCM decryption fails (indicating tampering), LANMess immediately blocks the contact, assumes an active attack, and shuts down communication with that peer. The event is logged in the Audit Log.

How does replay protection work?

Every message includes a sequential counter. If an attacker captures an old message and tries to send it again, LANMess checks the counter and rejects any message whose counter is less than or equal to the last received counter. Control frames also carry a 60-second timestamp window check.

4. TUI Interface & Commands

What is the difference between "/clear" and "/clearall"?

/clear deletes the messages only on your local device. The peer still has their copy. /clearall sends a CLEAR_REQUEST to the peer. If the peer accepts, both sides wipe the conversation from their local databases. It requires mutual agreement to prevent data loss.

How do I completely reset LANMess?

Press Ctrl+R or open the Menu and select "Reset everything". This drops all tables (identity, contacts, messages, vault), performs a database VACUUM, and zeroes out the cryptographic keys in memory. Irreversible.

What do the contact statuses mean?

[active]: the X25519 handshake is complete and the ratchet is set (session_established=1), so you can send messages. [waiting]: pending == 1, you sent an add request and are waiting for the peer. [accept?]: pending == 2, the peer sent you a request and you must press Y. [chat req]: incoming_request == 1, the peer wants to start a chat. [legacy]: chat_started is true but session_established is false (e.g. app restart or network loss); press S to restart the handshake.

What is the maximum message length I can send?

The maximum message length is 8,192 characters (MAX_MESSAGE_LEN = 8192). LANMess will refuse longer messages to keep frame sizes within MAX_FRAME_SIZE (64 KiB).

How do I accept a chat request or clear request?

When you have a pending request (chat or clear), a yellow indicator appears next to the contact. Press Y to accept or X to reject. You can also do this via the Menu.

5. Troubleshooting

Why does it say "Message not delivered: no response"?

This means the peer's TCP server is unreachable or blocked. Likely causes: the peer closed the app, their firewall blocks TCP ports 50510-50600, they are on a different subnet/VLAN, or you are no longer on the same LAN.

Why is my peer showing as offline?

LANMess uses a presence system that pings every 3 seconds (PRESENCE_INTERVAL). If no response is received within 15 seconds (ONLINE_WINDOW), the peer is marked offline. Check whether UDP port 50505 is open, or whether they disabled presence in their menu.

Why did I get blocked by a contact?

If the app detects a signature failure, decryption failure, or severe replay attempt from your IP, it automatically blocks you and prevents further communication. This only happens if the cryptographic integrity of the session is compromised.

What does the "Audit Log" warning mean?

The app maintains an HMAC-chained audit log. If someone deletes or modifies database rows directly, the chain breaks. A warning means your database has been tampered with outside of the app.

6. Platform & Compatibility

Which CPUs are supported by the pre-built installers?

The one-click installers (Windows .exe and the macOS/Linux shell scripts) are built for Intel and AMD x86_64 processors only. On ARM64 / aarch64 systems — including Apple Silicon Macs, Windows on ARM, and ARM Linux (Raspberry Pi, Asahi, etc.) — do everything manually: install Python 3.9+, then use pip install lanmess, or clone the repository from source. There is no pre-built ARM installer at this time.

What Python version do I need for a manual install?

Python 3.9 or newer is recommended. LANMess depends on the cryptography and textual packages, plus optional Argon2id acceleration via the argon2-cffi package. On aarch64, ensure your system packages for libssl and libffi are current before pip install.

Why does my antivirus flag the Windows installer?

The .exe is built with PyInstaller, which is frequently false-positive flagged by antivirus engines because packers are also used by malware. The build is open source and reproducible from the repository. If you prefer, install manually with pip install lanmess to avoid the binary completely.

Is there a mobile app?

No native mobile app exists. LANMess is a terminal UI built with Textual. On Android you can run it inside Termux with pip install lanmess. iOS does not provide a suitable terminal environment, so it is not supported.

7. General

Is LANMess free?

Yes. LANMess is released under the MIT license. There are no accounts, subscriptions, or telemetry. The full source is on GitHub and can be audited or self-built at any time.

What happens if I lose my PIN?

There is no recovery. The PIN wraps a random 32-byte master key with Argon2id, and that master key encrypts every field in your database. Without the PIN, the data is cryptographically inaccessible. Your only options are to restore from a backup of your ~/.lanmess folder, or reset everything and start over.

Can I send files, images, or group chats?

Not yet. LANMess is text-only and one-on-one. There is no file transfer, image embedding, or group chat in the current source. Messages are capped at 8,192 characters. Every message you send is written to the local encrypted database as plain text, so secrets pasted into chat are only protected by the session ratchet and the DB encryption at rest.

How do I update LANMess?

Download the latest release and install over the existing one. Your database at ~/.lanmess/lanmess.db is preserved. Database migrations run automatically on first launch, so your identity, contacts, and messages survive upgrades.

Can I export my chat history?

There is no built-in export feature. Every message is stored field-encrypted in the SQLite database using AES-GCM, so a straight SQL dump yields only ciphertext. You can copy the whole ~/.lanmess folder as a backup, but reading it back requires the original PIN and this app.

What happens to an active chat if the network drops?

If either peer restarts the app or loses the network for long enough that the process ends, the established session is lost and the contact is marked as [legacy]. Press S to start a new X25519 handshake with a fresh ephemeral keypair; the old hash ratchet is never reused.

Does LANMess read my keystrokes or other files?

No. The only files LANMess reads or writes are inside ~/.lanmess: the encrypted SQLite database. It does not install hooks, background services, or telemetry endpoints. Verify in the source: config.py resolves BASE_DIR = ~/.lanmess and nothing else.

Take back control.

No accounts. No subscriptions. No telemetry. Pick your platform below to choose how you'd like to download.

Pre-built installers are for Intel and AMD (x86_64) processors only. On ARM64 / aarch64 systems — Apple Silicon Macs, Windows on ARM, ARM Linux — everything must be done manually: install Python 3.9+ and run pip install lanmess, or build from source.
$curl -sSL https://raw.githubusercontent.com/virensahti-nl/lanmess-install/main/macos.sh | bash
$curl -sSL https://raw.githubusercontent.com/virensahti-nl/lanmess-install/main/linux.sh | bash
$pip install lanmess