Skip to content

feat(channels): add WebSocket channel for local web-based chat#641

Closed
likeaturtle wants to merge 10 commits intosipeed:mainfrom
likeaturtle:feat/websocket-chat-channel
Closed

feat(channels): add WebSocket channel for local web-based chat#641
likeaturtle wants to merge 10 commits intosipeed:mainfrom
likeaturtle:feat/websocket-chat-channel

Conversation

@likeaturtle
Copy link
Contributor

Add WebSocket channel implementation to support local LAN web chat interface with mobile-optimized responsive design.

Features:

  • Add WebSocket channel implementation in pkg/channels/websocket/

    • Embedded HTML chat interface with modern UI
    • Support for Markdown rendering and syntax highlighting
    • Dark/Light theme toggle with persistence
    • Multi-language support (Chinese/English)
    • Real-time message delivery with typing indicators
    • Connection health monitoring with auto-reconnect
  • Integration with channel manager

    • Add WebSocketConfig to pkg/config/config.go
    • Add default configuration in pkg/config/defaults.go
    • Register WebSocket channel in pkg/channels/manager.go
    • Create adapter for consistent naming convention
  • Mobile-first responsive design

    • Optimized for tablets (≤768px), phones (≤480px), and landscape mode
    • Touch-optimized controls with 44x44px minimum tap targets
    • Safe area support for notched displays (iPhone X+)
    • Smooth momentum scrolling with overscroll prevention
    • Dynamic viewport height (100dvh) to handle mobile keyboards
    • Horizontal scrolling for code blocks and tables
  • Configuration

    • Host: configurable (default: 0.0.0.0)
    • Port: configurable (default: 8080)
    • AllowFrom: whitelist support
    • Embedded assets (HTML, logo) for zero-dependency deployment

Technical Details:

  • Uses gorilla/websocket for WebSocket protocol
  • Shares unified message dispatcher with other channels
  • Implements standard Channel interface (Start/Stop/Send/IsRunning)
  • Ping/pong heartbeat for connection health
  • Graceful shutdown with connection cleanup
  • Client connection tracking with sync.Map for concurrency safety

📝 Description

🗣️ Type of Change

  • 🐞 Bug fix (non-breaking change which fixes an issue)
  • ✨ New feature (non-breaking change which adds functionality)
  • 📖 Documentation update
  • ⚡ Code refactoring (no functional changes, no api changes)

🤖 AI Code Generation

  • 🤖 Fully AI-generated (100% AI, 0% Human)
  • 🛠️ Mostly AI-generated (AI draft, Human verified/modified)
  • 👨‍💻 Mostly Human-written (Human lead, AI assisted or none)

📚 Technical Context (Skip for Docs)

  • **Reasoning:**For end users, after completing the configuration, it is recommended to first quickly test and validate the core functions and configuration status of PicoClaw, and then proceed to adapt other channels.

🧪 Test Environment

Hardware: [NanoKVM Cube]
OS: [NAME=Buildroot, VERSION=-g3649fe90d, ID=buildroot, VERSION_ID=2023.11.2, PRETTY_NAME="Buildroot 2023.11.2"]
Model/Provider: [Volcengine]
Channels: [Discord, Telegram, Feishu, DingTalk, WebSocket]

☑️ Checklist

  • My code/docs follow the style of this project.
  • I have performed a self-review of my own changes.
  • I have updated the documentation accordingly.

Copy link

@nikolasdehor nikolasdehor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice feature for local LAN testing and embedded devices. The chat UI is polished with responsive design, theme toggle, and markdown rendering. Several issues to address:

Critical

  1. Concurrent WebSocket writes are not safe: Send() calls conn.WriteMessage from the manager dispatch goroutine, while handleClient calls conn.WriteControl(PingMessage) from a separate goroutine. The gorilla/websocket documentation explicitly states: "Connections support one concurrent reader and one concurrent writer." You need a write mutex per connection, or use a per-client write channel. This will cause panics under load.

  2. No authentication/authorization on the HTTP endpoint: The WebSocket server listens on 0.0.0.0:8080 by default with no authentication. Anyone on the LAN can open the chat page and interact with the AI agent. The allow_from list checks r.RemoteAddr which includes the port (e.g. 192.168.1.5:54321), so it will never match a configured IP like 192.168.1.5. The IsAllowed check is effectively broken.

  3. External CDN dependencies in embedded HTML: The chat.html loads Google Fonts and FontAwesome from CDNs. This breaks the "zero-dependency deployment" claim and will not work on air-gapped LANs (which is a common use case for embedded devices like NanoKVM). Either embed these assets or use system fonts.

Security

  1. XSS via markdown rendering: The chat HTML renders markdown with innerHTML. If the LLM response contains HTML (which it can), it will be rendered directly. The markdown rendering function should sanitize HTML or use textContent for non-markdown parts. Looking at the renderMarkdown function in the HTML, it does replace < with &lt; inside code blocks, but not in regular text.

Architecture

  1. Does not extend BaseChannel: Unlike every other channel, this one implements the Channel interface from scratch instead of embedding BaseChannel. This means it misses any future additions to BaseChannel (like the group trigger logic, media store, or sender identity from PR #662). Consider embedding BaseChannel.

  2. Port conflict with health server: The default WebSocket port is 8080, but the health server uses the gateway port (default 18790). If a user sets the gateway port to 8080, these will conflict silently. Add a check or at least document the constraint.

  3. 1268 lines of HTML embedded in Go binary: The chat.html is large. Consider putting it in a separate embedded filesystem with //go:embed on a directory instead of a single file, so it can be maintained more easily.

Minor

  1. The min() helper function is unnecessary in Go 1.21+ which has min as a builtin.

  2. The registry_test.go changes (interface{} to any) are unrelated cleanup and should be in a separate PR.

  3. The cmd_status.go channel listing is another instance of the growing switch/if-chain pattern. Same maintenance concern as noted in PR #646.

Please address items 1 (concurrent writes), 2 (broken allow_from), and 4 (XSS) before merging.

@likeaturtle
Copy link
Contributor Author

Hi @nikolasdehor , thanks for your review!
I've completed the fixes for Critical issues 1, 2, 3 and Security issue 4.
The items under Architecture and Minor will be adjusted in follow-up commits.
Could you please review it again? Any further suggestions are appreciated.

@likeaturtle likeaturtle force-pushed the feat/websocket-chat-channel branch from 1842d11 to a50bb97 Compare February 23, 2026 03:35
@alexhoshina
Copy link
Collaborator

Hello, we are currently working on a channel refactoring plan, which includes a plan for a WebSocket server channel. In PR #662, I have also preliminarily implemented a WebSocket channel.
However, I have noticed that our understanding of the WebSocket channel and its boundaries may differ. Currently, the project maintainers might lean more towards Pico only providing service ports without implementing the frontend. Therefore, my PR only implements a WebSocket server and does not offer any WebUI options. I welcome your discussion, and we can work together to advance the channel refactoring plan.

@likeaturtle
Copy link
Contributor Author

Hi @alexhoshina. PR #662 is an important and massive project. If this work can be split into modular parts, I would be more than happy to contribute.
Overall, my PR is only meant to achieve the most lightweight and fast testing and communication within a local network. It was originally created to meet some needs I encountered while using the project. So although it has both frontend and backend capabilities, they are kept very simple.
I also fully agree that PicoClaw should focus more on providing service ports rather than a complete and complex frontend. After all, this will help other developers, including me, build more and better client applications.

@alexhoshina
Copy link
Collaborator

Hi! @likeaturtle I've opened a new refactor branch. You can check the WebSocket server I implemented in the refactor branch. I'm really looking forward to you improving the WebSocket channel-related content.
However, I discussed some UI-related issues with other maintainers, and we think it's better to keep Pico simple, UI-wise.

@likeaturtle
Copy link
Contributor Author

likeaturtle commented Feb 24, 2026

@alexhoshina Great! I will check your branch later. If your branch can be merged, I'll be able to implement the front-end UI capabilities in other projects using this WebSocket protocol.

By the way, I noticed that it has been merged into the refactor/channel-system branch. Do you have a timeline for when it will be merged into the main branch?

@alexhoshina alexhoshina added this to the Refactor Channel milestone Feb 26, 2026
@alexhoshina
Copy link
Collaborator

Hello! @likeaturtle Sorry for the late reply! The refactoring branch is expected to be merged on February 28th Beijing time, but the specific time is unknown.
I will close this PR first. Contributions related to webui can be submitted to #806.

@likeaturtle
Copy link
Contributor Author

likeaturtle commented Feb 26, 2026 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants