A lightweight LAN chat app built with FastAPI + WebSockets.
Supports both public chatrooms (everyone on the network) and token-protected private spaces for secure group conversations.
First update
No CSS
Works directly in the browser โ no extra software needed!
Supports desktop & mobile displays.
โญ main project concept
โญ
- ๐ Public Chat โ all devices on the LAN can join easily.
- ๐ Private Chat (Token) โ create a personal space with a unique token.
Only people who enter that token can join. - ๐ Timestamps on every message.
- ๐ฑ Responsive UI โ works on PC & mobile.
- โก Powered by FastAPI + WebSockets.
๐ User Session Features
-
First-Time Login
-
When a new user opens the app, they are prompted to enter:
- A username
- A password (set by the user, minimum 4 characters for easier recall)
-
The username and password are stored locally in the browser (
localStorage
) as part of a session object.
-
-
Password Security (Masked View)
-
Whenever the password is shown in popups, only the 2nd and 3rd characters are hidden.
-
Examples:
1234
โ1**4
12345
โ1**45
abcdef
โa**def
-
-
30-Minute Session Timeout
-
If the page is refreshed or reopened after 30 minutes of inactivity, a popup appears:
- Option 1: Continue with the saved username (requires entering the saved password).
- Option 2: Start a new session with a new username and password (clears old data).
-
-
Username Bar Interaction
-
Clicking the username at the top of the page shows a popup with:
-
The current username
-
The current password (masked)
-
Two buttons:
- โUse another usernameโ โ allows setting a new username & password.
- โCancelโ โ closes the popup without changes.
-
-
-
Reload & Cache Refresh
- Choosing to reset (new session) clears all stored data, including username, password, and messages.
- On reset, the page reloads, ensuring that any new CSS/JS updates are also applied.
โก This makes the app behave more like a real chat system with basic account persistence, session expiry, and manual username management, while still being lightweight and browser-based.
Second Update
LAN-Token-Chat/
โโโ main.py # Backend server (FastAPI + WebSocket)
โโโ index.html # Frontend UI (simple chat interface)
โโโ README.md # Project documentation
git clone https://github.com/akashdip2001/LAN-Token-Chat.git
cd LAN-Token-Chat
pip install fastapi uvicorn
python main.py

On the host computer:
http://127.0.0.1:8000
On another device in the same LAN:
http://<your-local-ip>:8000
- Everyone can join by pressing Join Public Chat.
- One person clicks Create Private Space.
โ The server generates a token (example:
a3f9c1b2
). - Share this token with friends.
- Others click Join Private Space, enter the token, and join the private chatroom.
โ Only users with the correct token can access that private space.
[12:45:01] Alice: Hello everyone!
[12:45:15] Bob: Hi Akashdip ๐
Private Chat (Token: 7fa21bc3)
[13:10:22] Alice: Secret group chat ๐ต๏ธ
[13:10:45] Bob: Only we can see this ๐
- Backend: Python, FastAPI, WebSockets
- Frontend: HTML, JavaScript, CSS
- Environment: Runs on any OS with Python 3
- Encrypt messages for extra privacy.
When testing with multiple devices (e.g., PC username 12345
and mobile username akashdip
), an extra username such as preview-15f1
appears in the online users list.
Example seen in UI:
Request
12345
Request
akashdip
Request
preview-15f1
This extra user is not a real user, but comes from the landing page preview feature.
In script.js
, the landing page (index.html
) opens a dummy WebSocket to fetch live messages for the preview box:
(function previewSocket() {
const anon = 'preview-' + Math.random().toString(16).slice(2, 6);
const socket = new WebSocket(`ws://${location.host}/ws/public/${anon}`);
state.previewSocket = socket;
})();
- This generates a fake user (
preview-xxxx
) each time someone loads the landing page. - The server treats it as a real participant, so it shows up in the online users list.
Edit the renderOnlineUsers(users)
function in script.js
:
function renderOnlineUsers(users) {
const wrap = document.getElementById('onlineUsers');
if (!wrap) return;
wrap.innerHTML = '';
users
.filter(u => !u.startsWith('preview-')) // ๐ฅ filter out dummy preview clients
.forEach(u => {
const row = document.createElement('div'); row.className = 'user-pill';
const name = document.createElement('div'); name.textContent = u;
const btn = document.createElement('button'); btn.className = 'glass-btn small'; btn.textContent = 'Request';
btn.setAttribute('data-user', u);
row.appendChild(name); row.appendChild(btn);
wrap.appendChild(row);
});
}
Now, only real usernames (e.g., 12345
, akashdip
) appear.
If the preview box on index.html
is not needed, simply comment out the preview socket code:
// (function previewSocket() {
// const anon = 'preview-' + Math.random().toString(16).slice(2, 6);
// const socket = new WebSocket(`ws://${location.host}/ws/public/${anon}`);
// state.previewSocket = socket;
// })();
This will stop generating preview-*
users but removes live preview on the landing page.
- This is not a bug with username sessions, but a side-effect of the preview socket.
- Fixing it requires either filtering out fake preview usernames (preferred) or removing the preview feature.