Hallway is a real-time multi-user chat server built with Go. It utilises a centralised Hub architecture to manage state across thousands of concurrent connections without the need for complex mutex locking.
| Property | Value |
|---|---|
| Language | Go 1.24+ |
| Web Framework | Gin (HTTP Routing) |
| WebSocket | Gorilla WebSocket v1.5.1 |
| Architecture | Hub / Client goroutine model (CSP) |
| History | Rolling in-memory buffer (Last 100 messages) |
| Frontend | Single Page Application (LINK-WILL-BE-ATTACHED) |
Hallway enables real-time JSON message exchange. A single Hub goroutine acts as the "Single Source of Truth," owning the client list and message history.
- Websocket Architecture Design: Real time communication!
- Immediate Context: New clients receive a replay of the last 100 messages upon joining.
- Authoritative Usernames: Usernames are resolved server-side via
?username=query params. If blank, the server generates anAnon_HHmmsstimestamp-based name. The server "stamps" this name on every message to prevent spoofing.
All communication uses a universal JSON envelope:
type Message struct {
Type string `json:"type"` // chatMessage | system | userCount
Payload any `json:"payload"` // Shape varies by Type
}- ChatMessage:
{ "type": "chatMessage", "payload": { "username": "alice", "message": "Hello!" } } - System:
{ "type": "system", "payload": { "text": "alice joined" } } - UserCount:
{ "type": "userCount", "payload": { "count": 3 } }
The central switchboard. history uses lazy allocation (it stays nil until the first message arrives to save memory).
| Field | Description |
|---|---|
clients |
Map of active connections. |
broadcast |
Inbound channel for fan-out messages. |
register |
Channel for new connection arrivals. |
history |
Slice of up to 100 rolling messages. |
Represents a live session. Every client spawns two dedicated goroutines:
- readPump: Reads from WebSocket, enforces authoritative usernames, and pushes to Hub.
- writePump: Drains the buffered
sendchannel (cap 256) and writes to the browser.
Hallway follows the mantra: "Do not communicate by sharing memory; instead, share memory by communicating."
main()→ Startsgo hub.run().serveChat()→ Upgrades HTTP to WS, creates Client, and starts:
go client.writePump()go client.readPump()
readPump ➔ hub.broadcast ➔ hub.run ➔ client.send ➔ writePump
To keep memory usage constant, the history buffer is trimmed once it exceeds MaxHistory:
h.history = append(h.history, message)
if len(h.history) > MaxHistory {
h.history = h.history[len(h.history)-MaxHistory:]
}Inside hub.run(), system broadcasts and user count updates are launched via go statements. This ensures the Hub doesn't block itself trying to send a message to its own broadcast channel while the loop is still busy.
- Persistence: History lives in RAM; data is lost on restart.
- Authentication: No password protection; usernames are assigned via URL parameters.
- Scaling: Single-room only. Multi-room support would require a map of Hubs.
- Security:
CheckOriginis set totruefor development; it should be restricted for production.
- Clone the repository.
- Run the server:
go run main.go
- Attach the server to your frontend or test in Postman