Skip to main content

LanTalk

Terminal-based LAN chat — pure asyncio, auto-discoverable, zero runtime dependencies.

  _                 _____     _ _
 | |               |_   _|   | | |
 | |     __ _ _ __  | | __ _| | | __
 | |    / _` | '_ \ | |/ _` | | |/ /
 | |___| (_| | | | || | (_| | |   <
 |______\__,_|_| |_\_/ \__,_|_|_|\_\

PyPI version Python License: MIT


What is LanTalk?

LanTalk lets people on the same Wi-Fi or LAN chat instantly — no accounts, no internet, no servers in the cloud. One person runs lantalk and starts a server; everyone else runs lantalk and joins. That's it.

  • Works on school networks, home networks, offices, hackathons
  • Pure Python stdlib — nothing to pip install beyond the package itself
  • Clients auto-discover the server via UDP broadcast (no IP typing required)

Features

🔍 Auto-discovery UDP beacon finds servers on your LAN automatically
🔐 Password auth Optional server password with SHA-256 hashing
👤 Username dedup Two people can't use the same name simultaneously
💬 Private messages /pm <user> <message> — end-to-end on the wire
🛡 Rate limiting Spammers get warned then kicked (10 msg / 5 s)
Idle timeouts Dead sockets cleaned up after 5 minutes of silence
🚫 Persistent bans Banned IPs survive server restarts via bans.json
🔇 Full validation Every message checked for type and field constraints
📝 Structured logging Console + lantalk.log file, levels DEBUG/INFO/WARNING
Pure asyncio Single event loop — no threads, no race conditions

Install

pip install lantalk

Python 3.10+ required. No third-party dependencies.

From source

git clone https://github.com/yourusername/lantalk
cd lantalk
pip install -e ".[dev]"

Quick start

1 — Start a server (one person does this)

$ lantalk

  [1]  Start a Server
  [2]  Join as Client

Choose (1 or 2): 1
Port (default 5050):
Server display name: Alice
Set password (leave blank for none): secret

══════════════════════════════════════════════════════
  LanTalk Server v2.0.0  —  Alice
  Listening on  192.168.1.5:5050
  Discovery beacon active (UDP 5051)
  Password protection: ON
══════════════════════════════════════════════════════

2 — Join as a client (everyone else)

$ lantalk

  [1]  Start a Server
  [2]  Join as Client

Choose (1 or 2): 2
Scanning for LanTalk servers…
Found 1 server(s):

  [1] 192.168.1.5:5050
  [m] Enter manually

Choose server: 1
Display name: Bob
Password: secret

══════════════════════════════════════════════════════
  LanTalk Client v2.0.0  —  Bob
  Connected to 192.168.1.5:5050
  Commands: /users  /pm <user> <msg>  /quit
══════════════════════════════════════════════════════
[Bob | lantalk] > Hey everyone!
[14:22] Alice: Welcome Bob!
[Bob | lantalk] > /pm Alice just between us 👋
[Bob | lantalk] > /users
[14:23] *** Online: Alice, Bob, Carol ***
[Bob | lantalk] >

Commands

Client commands

Command Description
/users List all connected users
/pm <user> <message> Send a private message
/quit Disconnect and exit

Server console commands

Command Description
/users List connected users
/stats Show connection count and ban list size
/kick <user> Disconnect a user
/ban <user> Ban a user's IP (persisted to bans.json)
/bans Show all banned IPs
/unban <ip> Remove an IP from the ban list
/help Show this list
/quit Shut down the server
(anything else) Broadcast as a server message to all clients

Architecture

┌──────────────────────────────────────────────────────────────┐
│  LAN  (e.g. 192.168.1.0/24)                                  │
│                                                              │
│  ┌────────────────────────────────────┐                      │
│  │  LanTalkServer  (pure asyncio)     │                      │
│  │                                    │◄─── TCP :5050 ───┐   │
│  │  _discovery_beacon()  ─────────────┼──── UDP bcast    │   │
│  │  asyncio coroutine, no threads     │     every 2 s    │   │
│  │                                    │                  │   │
│  │  per-client _RateLimiter           │                  │   │
│  │  _validate() on every message      │                  │   │
│  │  asyncio.wait_for() timeouts       │                  │   │
│  │  bans.json  (persistent)           │                  │   │
│  │  lantalk.log  (structured)         │                  │   │
│  └────────────────────────────────────┘                  │   │
│                                                          │   │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────────┴─┐ │
│  │ Client A     │  │ Client B     │  │ Client C           │ │
│  │ UDP discover │  │ UDP discover │  │ manual IP entry    │ │
│  │ asyncio      │  │ asyncio      │  │ asyncio            │ │
│  └──────────────┘  └──────────────┘  └────────────────────┘ │
└──────────────────────────────────────────────────────────────┘

Message flow (post-auth):
  Client ──JSON──► Server ──broadcast JSON──► all other Clients
                          └──(PM)──────────► one Client

Concurrency model

Everything runs in one asyncio event loop — no threading.Thread, no locks between threads, no shared mutable state across OS threads. The only asyncio.Lock is used to guard the _clients dict between concurrent coroutines.

JSON wire protocol

Every message is a single UTF-8 JSON line terminated with \n.

// Server → Client: auth challenge
{"type": "auth_request", "ts": "14:22", "needs_password": true}

// Client → Server: credentials
{"username": "Bob", "password": "secret"}

// Server → Client: success / failure
{"type": "auth_ok",   "ts": "…", "username": "Bob", "server_name": "Alice"}
{"type": "auth_fail", "ts": "…", "reason": "Username already taken."}

// Client → Server: chat message
{"type": "message", "text": "Hello!"}

// Server → all clients: broadcast
{"type": "message", "ts": "14:22", "user": "Bob", "text": "Hello!"}

// Server → one client: private message
{"type": "pm", "ts": "14:22", "from_user": "Alice", "text": "hey"}

// Server → clients: system event
{"type": "system", "ts": "14:22", "text": "Bob joined from 192.168.1.10."}

Security notes

  • Passwords are never stored or sent in plaintext. The server stores a SHA-256 hash; clients send the raw password only during the auth handshake (upgrade to TLS in v3 is planned).
  • Banned IPs are written to bans.json in the server's working directory and reloaded on every start.
  • Rate limiting: each client is capped at 10 messages per 5 seconds. Exceeding this sends a warning; repeat offences kick the user.
  • Usernames are limited to 24 alphanumeric/underscore/hyphen characters.
  • All incoming messages are schema-validated before processing; malformed JSON is silently dropped.

Logging

LanTalk writes structured logs to both the terminal and lantalk.log:

[14:22:01] INFO   Server started on 192.168.1.5:5050
[14:22:15] INFO   Bob joined from 192.168.1.10
[14:23:44] WARNING  Carol hit rate limit
[14:25:01] INFO   Kicked Dave
[14:25:03] INFO   Banned Dave (192.168.1.22)

Set LANTALK_LOG_LEVEL=DEBUG to see per-message traces.


Running tests

pip install -e ".[dev]"
pytest -v

Changelog

2.0.0  — pure asyncio (no threads), validation, rate limiting,
          timeouts, persistent bans, structured logging, v2 prompt
1.1.0  — asyncio rewrite, UDP discovery, JSON protocol, auth, commands, pytest
1.0.0  — initial: threading, basic TCP, plain-text protocol

Roadmap

  • TLS encryption (v3)
  • File transfer (/send file.pdf)
  • Web UI mode (http://localhost:5050)
  • Plugin system (@lantalk.plugin)

License

MIT © Ezra

Release files for lantalk 2.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for lantalk 2.0.0
File Size Uploaded
lantalk-2.0.0.tar.gz 19.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for lantalk 2.0.0
File Interpreter ABI Platform
lantalk-2.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 35.5 kB

Release files / lantalk-2.0.0.tar.gz

Download URL lantalk-2.0.0.tar.gz
Size 19.8 kB
Tags Source
SHA-256 checksum
How to use checksums
fbc42b40bdac125ab34b980abf46a2d8a76a271a808551cd42581271803f5cf8
BLAKE2b-256 checksum
How to use checksums
662b7ed391676afa9bd3b685acde51eea218bb655cb53d0128381fce4d6b4a23
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.14

Release files / lantalk-2.0.0-py3-none-any.whl

Download URL lantalk-2.0.0-py3-none-any.whl
Size 15.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
cb40eabc6e0f16d35338dc49bce3d4c5fa683854184b9bd961af7cc91df17cbf
BLAKE2b-256 checksum
How to use checksums
fc8b91ae3fb37a1c1ba523ef09ac980be42a3dc49bc4a1663bcf119f160ceebf
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.14

Release history Release notifications | RSS feed

This release

2.0.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page