Build analysis Python stdlib · 0 dependencies 2026-10-22

Offline Chat: a multi-user chat that needs zero internet

One Python file is the server, the client, and the protocol. It serves a chat room over whatever local network already exists — school WiFi, a phone hotspot, or a pair of Macs sharing a cable — and it never opens an outbound connection. Everything below is measured from the shipped offline_chat_v2.py, not from a plan.

Server
1,627 lines
Source size
70,888 bytes
Dependencies
0 pip installs
HTTP routes
7 + /files/
Client payload
27,971 bytes
CLI flags
5
The constraint, in the user's words

"can you make me a wireless no need internet multiuser chat that has all the functions before but no need internet make sure its seperate file dont delete the old ne"

Later refinements, each of which changed the design: "i said internet less so people dont need to conenct any wifi or doesnt have to be on same wifi" · "my mac doesnt have hotspot" · "i dotn see create network, trust me, i actually cannot open any hotspot, find another way"

What it actually does

1

No tunnel, no relay, no third party

The original launcher.py in this folder pipes traffic through an SSH tunnel so the room is reachable across the internet. That is exactly what was removed. v2 binds 0.0.0.0 and answers on the LAN interface only — nothing in the request path leaves the local network.

2

SSE instead of WebSockets

Server-to-client push is text/event-stream; client-to-server is a plain POST to /send. No websockets package, no uvloop, no FastAPI — just http.server.ThreadingHTTPServer. Any browser that can hold an EventSource open is a supported client.

3

The client is the page

The entire UI — 27,971 bytes of HTML, CSS and JS — is a Python string constant. There is no client/ folder to serve and no build step. Visiting / on a phone gets the same bytes a Mac gets.

4

Encryption without a CA

--tls generates a self-signed certificate at first run and caches it in the temp directory. If openssl exists it shells out; if it does not (stock Windows), a pure-Python RSA-2048 keygen plus hand-built X.509 DER takes over. Browsers show the familiar self-signed warning once.

5

Panic close

beforeunload and pagehide wipe document.body and clear stored state, so closing the tab leaves a blank white page rather than a readable transcript.

Request path

phone / laptop / iPad
      │  GET /                 → 27,971 B HTML shell (inline CSS + JS)
      │  POST /auth             → room password → token
      │  GET  /events?session_id=…  (SSE, held open)
      │        ← welcome {user_count, is_host, history[]}
      │        ← message / file_share / typing / reaction / user_list
      │  POST /send             → {action: message | set_name | react | delete | claim_host | ping}
      │  POST /upload           → multipart → /files/<name>
      └  GET  /link             → the shareable LAN URL + host token
                    │
              [ same LAN only ]   no outbound socket is ever opened

Contract

RouteMethodReturnsWhy it matters
/GETHTML shellClient and server ship as one file
/healthGET{status, users, tls, stealth, password_protected}Proves the mode actually started
/linkGET{link, host}The only thing a guest needs to type
/authPOST{ok, token}Password gate; wrong password is rejected server-side
/eventsGETSSE streamPush without WebSockets
/sendPOST{ok}Every user action, one endpoint
/uploadPOST{ok, url, name, size, mime}Files land on the host disk
/files/<name>GETbytesShared files stay inside the room

Anatomy: the certificate generator

Why it exists

Encryption is the difference between "a chat" and "a chat the network admin can read". But Windows ships without openssl, and a dependency on cryptography would break the zero-install promise. So the fallback builds the certificate by hand.

What it actually does

  • Miller–Rabin primes → RSA-2048 keypair in pure Python
  • DER writers for INTEGER, OID, SEQUENCE, BIT/OCTET STRING, UTCTime
  • sha256WithRSAEncryption (1.2.840.113549.1.1.11) over the TBSCertificate
  • PKCS#1 v1.5 padding, then PEM at 64 columns
Verified on both paths. macOS/OpenSSL 3.6.3 issues the cert instantly; forcing the fallback (empty PATH, system Python 3.9.6) still produced a 1,062-byte certificate that ssl.SSLContext.load_cert_chain() accepts, and the HTTPS server answered /health with tls:true.

What broke, and what it cost

SymptomCauseFix
Page rendered as unstyled text, no JS ran The template began life as an f-string, so every CSS/JS brace was written {{. Converting it to a plain string kept 204 doubled braces in the shipped HTML. Collapse {{/}} once at render time, before marker substitution.
SyntaxError: Unexpected string in the upload renderer \' inside a Python triple-quoted string is consumed by Python, so a nested window.open('…') reached the browser with no escapes. Dropped the nested quotes entirely: window.open(this.src).
HTTPS probe returned exit 7Cert generation failed before bind() Generate into the temp dir, retry, and fall back to pure Python.
/ served 0 bytesf-string parsed ${…} JS as Python Markers (__TLS_BADGE__, __PW_BADGE__) + .replace().
App died on import under Python 3.9str | None annotation is 3.10+ Optional[str] — the fallback path targets older Windows/Linux installs.
Honest limitations. This is a LAN chat, not a radio. It cannot invent a network: every device must be able to reach the host's IP. On a school network that isolates clients (common on enterprise WiFi), two laptops on the same SSID still cannot see each other — the fix there is a phone hotspot or a direct cable, not code. TLS is a self-signed certificate, so the browser warning is expected and there is no protection against an active MITM who can already intercept your LAN. The password is checked server-side but the room state lives in process memory: kill the server and the transcript is gone. There are no OS firewall rules — earlier notes calling this "firewall-proof" were wrong; it is encrypted, not firewalled.

Modes

FlagEffectWhen
--port NListen port, default 9001Two rooms on one host
--tlsHTTPS with generated certAnything you would not shout
--password PRoom password, token required for /eventsOpen hotspots
--stealthPort 443 + TLS + passwordTraffic that should look like ordinary HTTPS
--bind-ip IPOverride LAN IP detectionMulti-homed host, VPN, odd adapters

Evidence

CheckResult
Server sourceoffline_chat_v2.py · 1,627 lines · 70,888 B · sha256 3d265bb3…fdcf83
Launcherstart_chat.py · 159 lines · 5,665 B · sha256 55a0cf98…1f9934
Original appUntouched: offline_chat.py 888 lines, launcher.py, server/, client/
Real-time pushSecond session joined → system + user_list + message frames received on a held-open SSE stream
MultiuserMessage from sess_bob stored and replayed in history to a later session
Password gateWrong password rejected; correct password returns a token that /events accepts
Client JSnode --check parse of the 15 KB inline script → OK; CSS brace balance 0
Client renderHeadless Chrome on the demo build: 5 message bubbles, system line, sidebar data, share link filled
TLS/health over HTTPS → {"tls": true, "password_protected": true}
Python 3.9Compiles and serves with the pure-Python cert path

What is still not built

Open the UI demo Source mirror Portfolio