Same WiFi. No server. No account. Just type.
whisperlan turns every laptop on your local network into a chatroom — plus encrypted 1-on-1s. One Python file, zero dependencies, macOS + Linux. The command stays short: type whisper (or whisperlan, same thing).
🎥 That's a static mock. To record a real demo:
brew install vhs(or asciinema), run twowhisper --name …sessions side by side, export todocs/demo.gif, and swap the image link above. Real typing demos convert 10x better than mockups.
┌─────────────────────────────────────────────────┐
│ 🤫 whisper │ room: lan │ 3 online │
│ type + enter to send │ /p @name private │ /help │
│ 12:01 ali yo who is here?? │
│ 12:01 bob present 🫡 │
│ ── cara joined from 192.168.1.42 🔒 ── │
│ 🔒→bob secret plan at midnight_ │
│──────────────────────┼─ ● ONLINE ───────────────│
│ 🔒→bob secret plan… │ ➤ #lan (room) │
└──────────────────────┴──────────────────────────┘
Option A — Homebrew 🍺 (easiest on Mac):
brew tap Tarraf2020/whisperlan https://github.com/Tarraf2020/whisperlan && brew install Tarraf2020/whisperlan/whisperlanUpgrades later with brew upgrade whisperlan.
Option B — one-liner:
curl -fsSL https://raw.githubusercontent.com/Tarraf2020/whisperlan/main/install.sh | bashOption C — clone:
git clone https://github.com/Tarraf2020/whisperlan.git
cd whisper
./install.shOption D — pip:
pip install git+https://github.com/Tarraf2020/whisperlan.gitForked it or renamed your repo? Set
REPO=you/name:curl -fsSL ... | REPO=you/name bashDon't forget to replaceTarraf2020/whisperlanin this README +pyproject.tomlwith your repo path.
Requires: Python 3.8+ (stdlib only — curses, socket, ssl). No packages to install.
Just re-run the installer (it overwrites + keeps your name/history):
curl -fsSL https://raw.githubusercontent.com/Tarraf2020/whisperlan/main/install.sh | bashNo need to check manually — whisperlan tells you inside the app: a 🔔 line appears when a peer runs a newer version, or when GitHub has one (checked max once a day, silent when offline).
whisperlan --name ali
# short alias works too:
whisper --name aliFriend on the same WiFi, same port:
whisper --name bobYou auto-discover each other in ~5 seconds. Flags: -n = name, -p = room port, --pport = private port, -v = version.
| What | How |
|---|---|
| Talk to everyone | just type + Enter |
| Private 1-on-1 🔒 | /p @bob, then type (prompt turns 🔒→bob) |
| One private, no switch | /dm @bob yo or @bob yo |
| Back to group | /room |
| Rename | /name newname |
| Fun | /me dances, /shrug, /flip, /unflip, /party — work in private chats too |
| Misc | /online, /clear, `/notify on |
| 💥 Panic | /panic → /panic yes: nukes YOUR session history (room + privates, memory only — nothing is ever written to disk). Others keep theirs. |
Mention with @name in the room → they get a notification. Sidebar shows (2) unread badges for privates.
Privates, DMs, and @you mentions pop a real desktop banner — not just a terminal beep:
- Warp: uses Warp's native notification hook (OSC 777), works out of the box. If you also want a sound, enable it in Warp:
Settings > Features > Terminal > Audible terminal bell(+ macOSSystem Settings > Notifications > Warp). - Other terminals: Ghostty/iTerm2/Kitty/WezTerm banner via OSC 9 + audible
BEL(OSC-only by default, click focuses the terminal). Fallbackosascript(macOS) /notify-send(Linux) only when the terminal likely lacks OSC 9 (e.g. Apple Terminal). Force withWHISPER_NATIVE=on(dual) orWHISPER_NATIVE=off(OSC-only). - Toggle anytime inside the app:
/notify off(mute),/notify on. Or permanently:WHISPER_NOTIFY=off whisper --name ali.
- Room = encrypted mesh 🔒: one TLS-encrypted TCP unicast per peer, never broadcast. Passive sniffers see connection metadata at most, never content. Needs v1.5.0+ on both ends — old peers are skipped with a warning.
- Private = direct TLS-encrypted TCP to one peer (
port+1by default, auto-bumped if busy). Cert self-generates once via systemopenssl→~/.whisper-cert.pem. Passive sniffers see gibberish. - Presence stays public:
hello / heartbeat / bye / typingare still plaintext broadcast (nick + IP + ports, no chat content). A sniffer sees who's online, not what they say. - Honest limit: self-signed = TOFU, no CA, so a skilled active LAN attacker could MITM. No
opensslon the machine = plain-TCP fallback (still unicast, still sniffable). Don't send bank passwords. 😉
Packet shape: JSON {v, id, type, from, uid, to?, text, ts, ip, pport, mesh?}, types hello / heartbeat / bye / msg / dm / typing / rename / me / priv / priv-typing / room / room-me (msg/me/dm = legacy ≤1.4.x receive path only). Heartbeat 5s, timeout 15s. No chat history is written to disk — quit and the session is gone.
whisper: command not found→ open a NEW terminal (picks up~/.local/binon PATH), orexport PATH="$HOME/.local/bin:$PATH".- Nobody sees me → same WiFi? (guest networks isolate clients), same
--port? firewall allowed the UDP popup? - Solo test → two terminals:
whisper --name ali+whisper --name bob. - Weird characters → use iTerm2 / Ghostty / Kitty / Terminal.app.
- Uninstall →
./uninstall.sh.
If whisper made your LAN party better, fuel the next feature:
- Hit ⭐ Star + GitHub Sponsor (button at the top, via
.github/FUNDING.yml) - Or Ko-fi:
whisperlan(placeholder — swap in your real link)
Maintainer: after
git push, go to github.com → your repo → Settings → Sponsors (or ko-fi.com) and replaceTarraf2020/whisperlanin.github/FUNDING.ymlwith your real handles, or the button goes nowhere.
PRs welcome! Keep it: one file, zero deps, works on fresh macOS Python. Run a syntax check before pushing:
python3 -m py_compile whisper.pyIdeas wanted: file drop (/send), emoji reactions, internet relay mode, Windows support.
MIT — see LICENSE. Do whatever you want, just keep the notice.
Made for LAN parties, classrooms, hackathons & dorm rooms. If it made your table laugh, leave a ⭐.