Skip to main content

Terminal-based LAN chat — async, discoverable, secure

Project description

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

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

lantalk-2.0.0.tar.gz (19.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

lantalk-2.0.0-py3-none-any.whl (15.7 kB view details)

Uploaded Python 3

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

Hashes for lantalk-2.0.0.tar.gz
Algorithm Hash digest
SHA256 fbc42b40bdac125ab34b980abf46a2d8a76a271a808551cd42581271803f5cf8
MD5 d172891aaf8f8dddf6ea7e9c56a89c02
BLAKE2b-256 662b7ed391676afa9bd3b685acde51eea218bb655cb53d0128381fce4d6b4a23

See more details on using hashes here.

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

Hashes for lantalk-2.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 cb40eabc6e0f16d35338dc49bce3d4c5fa683854184b9bd961af7cc91df17cbf
MD5 1663b6a81ae82048fc23c67d52ac808e
BLAKE2b-256 fc8b91ae3fb37a1c1ba523ef09ac980be42a3dc49bc4a1663bcf119f160ceebf

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page