MoonshotAI / MoonshotAI/kimi-code
Server 0.37.2: WS heartbeat pings are never sent on non-loopback connections
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 7.5k
- Forks
- 1.2k
- Avg merge
- 11h 53m
- Merged PRs (30d)
- 350
Description
Summary
When connecting to the headless server's WebSocket endpoint (/api/v1/ws) via a non-loopback address (e.g. the machine's LAN or Tailscale IP), the server never sends the heartbeat {"type":"ping"} frames — even though server_hello advertises heartbeat_ms: 10000. The same client connecting to 127.0.0.1 on the same server process receives a ping every 10 seconds.
Environment
- Server: kimi-code server 0.37.2 (
GET /api/v1/meta→server_version: "0.37.2", backend v2), running on macOS - Clients tested: python
websockets17.0.1 probe, and a custom Kotlin NIO WebSocket client — identical behavior
Reproduction
Connect to ws://<lan-or-tailscale-ip>:58627/api/v1/ws with a valid Bearer token, send client_hello, then subscribe_v2 with transcript: {"*": "delta"}, and just listen:
Over ws://127.0.0.1:58627 (same server, same session):
[ 0.1s] server_hello
[ 0.1s] ack
[ 10.1s] ping ← every 10s, reliably
[ 20.1s] ping
... (still going at 100s+)
Over ws://100.73.220.23:58627 (this machine's Tailscale IP):
[ 0.0s] server_hello
[ 0.0s] hello ack
[ 0.0s] subscribe ack
[100.0s] TIMEOUT — zero ping frames in 100 seconds
Everything else works over the non-loopback connection: handshake, acks, transcript.reset, transcript.ops, event.session.work_changed, prompt.queued — only the periodic heartbeat pings never arrive. The server also answers protocol-level WS ping (opcode 0x9) with pong (rtt 0ms) on that same connection, so the TCP path is healthy.
Impact
Any client that does passive liveness detection ("no frame for N seconds → reconnect") — which is the natural thing to build given heartbeat_ms: 10000 is advertised — will kill and re-establish a perfectly healthy connection every ~35-40s. Our desktop client reconnected 10,000+ times before we traced it, each reconnect triggering a full re-subscribe and history refetch.
Expected behavior
Heartbeat pings are sent on all connections regardless of the peer address (or heartbeat_ms is not advertised when heartbeats are disabled).
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by tracing the server's /api/v1/ws WebSocket handling and heartbeat scheduling, comparing the loopback and non-loopback connection paths. Reproduce with a valid Bearer token, client_hello, and subscribe_v2, then verify that heartbeat pings arrive every 10 seconds on both addresses or that heartbeat_ms is not advertised when disabled.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- api, backend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 52/100