Terminal-based LAN chat — async, discoverable, secure
Project description
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
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file lantalk-2.0.0.tar.gz.
File metadata
- Download URL: lantalk-2.0.0.tar.gz
- Upload date:
- Size: 19.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fbc42b40bdac125ab34b980abf46a2d8a76a271a808551cd42581271803f5cf8
|
|
| MD5 |
d172891aaf8f8dddf6ea7e9c56a89c02
|
|
| BLAKE2b-256 |
662b7ed391676afa9bd3b685acde51eea218bb655cb53d0128381fce4d6b4a23
|
File details
Details for the file lantalk-2.0.0-py3-none-any.whl.
File metadata
- Download URL: lantalk-2.0.0-py3-none-any.whl
- Upload date:
- Size: 15.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cb40eabc6e0f16d35338dc49bce3d4c5fa683854184b9bd961af7cc91df17cbf
|
|
| MD5 |
1663b6a81ae82048fc23c67d52ac808e
|
|
| BLAKE2b-256 |
fc8b91ae3fb37a1c1ba523ef09ac980be42a3dc49bc4a1663bcf119f160ceebf
|