LanTalk
Terminal-based LAN chat — pure asyncio, auto-discoverable, zero runtime dependencies.
_ _____ _ _
| | |_ _| | | |
| | __ _ _ __ | | __ _| | | __
| | / _` | '_ \ | |/ _` | | |/ /
| |___| (_| | | | || | (_| | | <
|______\__,_|_| |_\_/ \__,_|_|_|\_\
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 installbeyond 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.jsonin 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)
| File | Size | Uploaded | |
|---|---|---|---|
| lantalk-2.0.0.tar.gz | 19.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| 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
|