What the user saw
@sasha_kot (MoonTerminal chat, 2026-09-26): "поменял api ключ в боте на HL, теперь в мунтерминале биржа не определяется как HL" — they switched a running MoonBot core to Hyperliquid by entering new API keys, and the terminal still does not show that core as Hyperliquid.
MoonBot allows switching the exchange on a live, logged-in bot without restarting the process, so from the terminal's side the core simply keeps running (or at most restarts) on the same address.
Why
The terminal learns a core's exchange exactly once per MoonProto client:
crates/moon-core/src/feed/live/mod.rs: FeedMsg::Identity is published from client.server_info().exchange_code and then latched by identity_sent = true. Nothing ever resets identity_sent for the life of that client — not Reconnecting, not Connected { fresh: false }, not ServerRestart.
- MoonProto fills
ServerInfo only in the init state machine (BaseCheck). On an internal reconnect or a detected ServerRestart init is not repeated, so the snapshot keeps the old exchange code anyway.
The gap is already known for the build number: crates/moon-core/src/session/core_update.rs (doc of the Verifying phase) says a core that restarts for any reason other than a terminal-commanded update "still shows its pre-restart build, because MoonProto's internal reconnect keeps the snapshot". The exchange identity rides the same snapshot, so it goes stale the same way.
The stale identity is not just a caption: the same FeedMsg::Identity drives core_venue (logo, label, grouping, market-name parsing) and set_orderbook_kind (spot vs futures book), and ExchangeId is the key market-data providers are elected by. What a wrong venue does to provider election for the old exchange has not been checked.
Workaround today
Settings → «Подключения» → the ↻ button («Переподключить») on that core's row, or restart the terminal. Both create a fresh MoonProto client, which runs BaseCheck again and publishes the new exchange.
Possible fix (terminal side)
MoonProto has no public way to re-run BaseCheck, so the only route in the terminal is the existing respawn (the same one ↻ and the post-update Verifying phase use):
- respawn the client on
LifecycleEvent::ServerRestart — covers a restarted core;
- a hot exchange switch with no restart may not produce
ServerRestart at all; it needs its own signal (e.g. the core's market list being replaced wholesale). Which signal a hot switch actually produces on the wire has not been checked.
Until then, a hint near the core row ("exchange changed on the core? press ↻") would at least tell the user what to do.
What the user saw
@sasha_kot (MoonTerminal chat, 2026-09-26): "поменял api ключ в боте на HL, теперь в мунтерминале биржа не определяется как HL" — they switched a running MoonBot core to Hyperliquid by entering new API keys, and the terminal still does not show that core as Hyperliquid.
MoonBot allows switching the exchange on a live, logged-in bot without restarting the process, so from the terminal's side the core simply keeps running (or at most restarts) on the same address.
Why
The terminal learns a core's exchange exactly once per MoonProto client:
crates/moon-core/src/feed/live/mod.rs:FeedMsg::Identityis published fromclient.server_info().exchange_codeand then latched byidentity_sent = true. Nothing ever resetsidentity_sentfor the life of that client — notReconnecting, notConnected { fresh: false }, notServerRestart.ServerInfoonly in the init state machine (BaseCheck). On an internal reconnect or a detectedServerRestartinit is not repeated, so the snapshot keeps the old exchange code anyway.The gap is already known for the build number:
crates/moon-core/src/session/core_update.rs(doc of theVerifyingphase) says a core that restarts for any reason other than a terminal-commanded update "still shows its pre-restart build, because MoonProto's internal reconnect keeps the snapshot". The exchange identity rides the same snapshot, so it goes stale the same way.The stale identity is not just a caption: the same
FeedMsg::Identitydrivescore_venue(logo, label, grouping, market-name parsing) andset_orderbook_kind(spot vs futures book), andExchangeIdis the key market-data providers are elected by. What a wrong venue does to provider election for the old exchange has not been checked.Workaround today
Settings → «Подключения» → the
↻button («Переподключить») on that core's row, or restart the terminal. Both create a fresh MoonProto client, which runs BaseCheck again and publishes the new exchange.Possible fix (terminal side)
MoonProto has no public way to re-run BaseCheck, so the only route in the terminal is the existing respawn (the same one
↻and the post-updateVerifyingphase use):LifecycleEvent::ServerRestart— covers a restarted core;ServerRestartat all; it needs its own signal (e.g. the core's market list being replaced wholesale). Which signal a hot switch actually produces on the wire has not been checked.Until then, a hint near the core row ("exchange changed on the core? press ↻") would at least tell the user what to do.