package main import ( "crypto/rand" "encoding/hex" "encoding/json" "log" "net/http" "sync" "time" "github.com/gorilla/websocket" ) // Message types const ( MsgTypeHandshake = "handshake" MsgTypePublic = "public" MsgTypePrivate = "private" MsgTypeSystem = "system" MsgTypeUserJoin = "user_join" MsgTypeUserLeave = "user_leave" MsgTypeUsersList = "users_list" ) // User represents a connected user type User struct { ID string Name string Avatar string Fingerprint string Conn *websocket.Conn LastSeen time.Time mu sync.Mutex } // Message represents a chat message type Message struct { Type string `json:"type"` From string `json:"from,omitempty"` FromName string `json:"from_name,omitempty"` FromAvatar string `json:"from_avatar,omitempty"` To string `json:"to,omitempty"` Text string `json:"text,omitempty"` Timestamp time.Time `json:"timestamp,omitempty"` } // UserInfo for user list type UserInfo struct { ID string `json:"id"` Name string `json:"name"` Avatar string `json:"avatar"` Status string `json:"status"` } // Server represents the chat server type Server struct { users map[string]*User upgrader websocket.Upgrader mu sync.RWMutex } var server *Server func init() { server = &Server{ users: make(map[string]*User), upgrader: websocket.Upgrader{ CheckOrigin: func(r *http.Request) bool { return true }, ReadBufferSize: 1024, WriteBufferSize: 1024, }, } } func NewUser(id, name, fingerprint string, conn *websocket.Conn) *User { avatar := "" if len(name) > 0 { avatar = string(name[0]) } return &User{ ID: id, Name: name, Avatar: avatar, Fingerprint: fingerprint, Conn: conn, LastSeen: time.Now(), } } func (u *User) SendMessage(msg Message) error { u.mu.Lock() defer u.mu.Unlock() return u.Conn.WriteJSON(msg) } func (s *Server) BroadcastPublic(msg Message) { s.mu.RLock() defer s.mu.RUnlock() for fingerprint, user := range s.users { if fingerprint != msg.From { go user.SendMessage(msg) } } } func (s *Server) SendPrivate(msg Message) error { s.mu.RLock() defer s.mu.RUnlock() recipient, exists := s.users[msg.To] if !exists { return nil } return recipient.SendMessage(msg) } func (s *Server) AddUser(user *User) { s.mu.Lock() s.users[user.Fingerprint] = user s.mu.Unlock() log.Printf("User added: %s (fingerprint: %s, total: %d)", user.Name, user.Fingerprint, len(s.users)) joinMsg := Message{ Type: MsgTypeUserJoin, From: user.Fingerprint, FromName: user.Name, FromAvatar: user.Avatar, Text: user.Name + " joined the chat", Timestamp: time.Now(), } s.BroadcastPublic(joinMsg) s.SendUsersList(user) s.BroadcastUsersList() } func (s *Server) RemoveUser(fingerprint string) { s.mu.Lock() user, exists := s.users[fingerprint] if exists { delete(s.users, fingerprint) } s.mu.Unlock() if exists { log.Printf("User removed: %s (fingerprint: %s, remaining: %d)", user.Name, fingerprint, len(s.users)) leaveMsg := Message{ Type: MsgTypeUserLeave, From: fingerprint, FromName: user.Name, Text: user.Name + " left the chat", Timestamp: time.Now(), } s.BroadcastPublic(leaveMsg) s.BroadcastUsersList() } } func (s *Server) SendUsersList(user *User) { s.mu.RLock() defer s.mu.RUnlock() users := make([]UserInfo, 0, len(s.users)) for _, u := range s.users { if u.Fingerprint != user.Fingerprint { users = append(users, UserInfo{ ID: u.Fingerprint, Name: u.Name, Avatar: u.Avatar, Status: "šŸ”’ Encrypted", }) } } usersData, _ := json.Marshal(map[string]interface{}{ "type": MsgTypeUsersList, "users": users, }) user.mu.Lock() user.Conn.WriteMessage(websocket.TextMessage, usersData) user.mu.Unlock() } func (s *Server) BroadcastUsersList() { s.mu.RLock() defer s.mu.RUnlock() for _, user := range s.users { go s.SendUsersList(user) } } func HandleWebSocket(w http.ResponseWriter, r *http.Request) { conn, err := server.upgrader.Upgrade(w, r, nil) if err != nil { log.Printf("WebSocket upgrade error: %v", err) return } var handshake struct { Type string `json:"type"` Name string `json:"name"` ID string `json:"id"` Fingerprint string `json:"fingerprint"` } err = conn.ReadJSON(&handshake) if err != nil { log.Printf("Handshake error: %v", err) conn.Close() return } if handshake.Type != MsgTypeHandshake { log.Printf("Invalid handshake type: %s", handshake.Type) conn.Close() return } if handshake.Fingerprint == "" || len(handshake.Fingerprint) < 8 { log.Printf("Invalid fingerprint") conn.Close() return } server.mu.RLock() _, exists := server.users[handshake.Fingerprint] server.mu.RUnlock() if exists { errorMsg := Message{ Type: MsgTypeSystem, Text: "Identity already connected. Only one session per identity allowed.", } conn.WriteJSON(errorMsg) conn.Close() return } user := NewUser(handshake.ID, handshake.Name, handshake.Fingerprint, conn) server.AddUser(user) defer server.RemoveUser(user.Fingerprint) log.Printf("User connected: %s (%s)", user.Name, user.Fingerprint) welcomeMsg := Message{ Type: MsgTypeSystem, Text: "Welcome to NoshiTalk! Your identity is cryptographically protected.", Timestamp: time.Now(), } user.SendMessage(welcomeMsg) for { var msg Message err := conn.ReadJSON(&msg) if err != nil { if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) { log.Printf("WebSocket error: %v", err) } break } msg.From = user.Fingerprint msg.FromName = user.Name msg.FromAvatar = user.Avatar msg.Timestamp = time.Now() switch msg.Type { case MsgTypePublic: server.BroadcastPublic(msg) log.Printf("Public message from %s: %s", user.Name, msg.Text) case MsgTypePrivate: err := server.SendPrivate(msg) if err != nil { log.Printf("Private message error: %v", err) } else { log.Printf("Private message from %s to %s", user.Name, msg.To) } } user.LastSeen = time.Now() } } func HandleIndex(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html; charset=utf-8") w.Header().Set("X-Frame-Options", "DENY") w.Header().Set("X-Content-Type-Options", "nosniff") w.Header().Set("Referrer-Policy", "no-referrer") w.Write([]byte(htmlTemplate)) } func generateChallenge() (string, error) { b := make([]byte, 32) _, err := rand.Read(b) if err != nil { return "", err } return hex.EncodeToString(b), nil } func (s *Server) CleanupInactiveUsers() { ticker := time.NewTicker(1 * time.Minute) defer ticker.Stop() for range ticker.C { s.mu.Lock() now := time.Now() for fingerprint, user := range s.users { if now.Sub(user.LastSeen) > 5*time.Minute { log.Printf("Removing inactive user: %s", user.Name) delete(s.users, fingerprint) user.Conn.Close() } } s.mu.Unlock() } } const htmlTemplate = ` NoshiTalk - Anonymous Encrypted Chat

šŸ” NoshiTalk Secure Entry

šŸ”‘ Key generation protects:
  • Your IDENTITY (cryptographic fingerprint)
  • Your LOGIN (challenge-response auth)
  • Anti-spam protection (CPU cost)
āš ļø EPHEMERAL: All messages exist only in RAM. Close tab = everything deleted.
šŸ’¾ Save keys to reuse this identity later.
šŸ’» Desktop client uses memguard (protects keys even from root).
` func main() { go server.CleanupInactiveUsers() http.HandleFunc("/", HandleIndex) http.HandleFunc("/ws", HandleWebSocket) port := ":8080" log.Printf("╔════════════════════════════════════════════╗") log.Printf("ā•‘ NoshiTalk Server - Zero Knowledge Chat ā•‘") log.Printf("╠════════════════════════════════════════════╣") log.Printf("ā•‘ Port: %s ā•‘", port) log.Printf("ā•‘ WebSocket: ws://localhost%s/ws ā•‘", port) log.Printf("ā•‘ Security: E2E Encrypted + Ephemeral ā•‘") log.Printf("ā•‘ Storage: Zero (RAM only) ā•‘") log.Printf("ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•") err := http.ListenAndServe(port, nil) if err != nil { log.Fatal("Server error: ", err) } }