Observe mode
vike-desktop --observe against a headless daemon — the one mode the shipped binary can compose, the two the library still declares and nothing reaches, the self-healing bridge, and the control channel that can place real orders.
vike-desktop --observe <ADDR> names the node to watch: the app connects to a headless
vike-tradehub daemon's observe server instead of spawning a local trading core. The scan is in
crates/vike-desktop/src/main.rs — the argument after --observe is the address — and everything it
feeds is in crates/vike-app-core, where it is tested. This page is written from
crates/vike-app-core/src/split_plane.rs, backend_conn.rs, observe_bridge.rs,
tradehub_control.rs, and crates/vike-desktop/Cargo.toml.
One mode, and two the library still declares
split_plane.rs's app_mode maps the pair (fat build?, --observe given?) to what App::new
composes, and it still declares three arms:
| build | --observe | mode |
|---|---|---|
| fat | no | LocalCore — local trading core plus local venue feeds |
| fat | yes | ObserveWithFeeds — a remote account plane beside local feeds |
| thin | yes | ObserveOnly — the feed-less observer |
| thin | no | refused before App::new |
⚠ The shipped binary reaches exactly one of them. crates/vike-desktop/src/main.rs calls
app_mode(false, …) — the first argument is a literal false, because the crate declares no
features and the fat resolution that set it was deleted with the local core. So every launch
composes ObserveOnly, and the two upper rows are library surface nothing in the release can
select. They are listed because the enum is still there and a reader of the source will meet them.
The module doc divides the client's data into two planes with different owners. The account
plane — orders, positions, equity, kline history — belongs to the backend and arrives as wire
snapshots over the observe bridge. The market-data plane — DOM books, the trade tape, tick,
volume and order-flow series — belongs to the venue and is subscribed directly and keylessly. The
ObserveWithFeeds would run both in one process — a remote daemon's account plane beside local
venue feeds, with no local core — and LOCAL_FEED_VENUES is the one written copy of that feed set
(binance, bybit, okx, aster, hyperliquid, polymarket). The arm the release composes, ObserveOnly,
mounts no feeds at all: the venue bridges are not in this binary's dependency graph.
What the one build links
crates/vike-desktop/Cargo.toml declares no [features] table. vike-mount and vike-run
stand in it as a tombstone comment rather than as dependencies, so there is no venue→engine
composition root to reach; vike-data is taken with default features, which is the HistStore
trait and nothing behind it, so no DataFusion enters the tree. The manifest's own verification line
is cargo tree -p vike-desktop -i datafusion, which must report the crate unreachable. The one
venue crate that does link is vike-deribit, an unconditional edge through the options-chain
provider.
It links the Studio and drives it remote-only: with a datahub address resolved —
config.datahub_addr, else the connected backend's own advertisement — it opens a
RemoteHistStore; with neither it shows a "Studio: no data store" label naming both fixes.
CI's app-check job compiles the crate once, because there is one configuration to compile.
Which daemon a launch dials
backend_conn.rs's startup_backend is the ladder, and it has no "not connected" rung. A
non-blank --observe flag is an override and becomes the synthetic cli_observe_record, whose
observe key is the credential-store name VIKE_TRADEHUB_OBSERVE_KEY, whose control key is
VIKE_TRADEHUB_CONTROL_KEY, and which is armed — so on the CLI path the process-level master gate is
the only gate, exactly as before the registry existed. Otherwise the registry's active record is
used as itself, because that record is the only place its key names live. Otherwise the local
default DEFAULT_OBSERVE_ADDR. The doc records why the app no longer exits without a flag: a
viewer that opens and says "not connected" in its status bar is strictly better than one that closed
on a typo, since nothing in it can place an order. config.tradehub_addr is deliberately not a
rung — that is the daemon's bind address, which a client cannot dial. Backends are added and picked
in Connections.
The bridge
observe_bridge.rs is the reverse bridge: each pushed WireSnapshot is republished into the
GUI's arc-swap cell as a CoreSnapshot, so every DOM, Trade and portfolio panel renders the remote
daemon's live state unchanged. Charts render too — the daemon ships a bounded bar tail, about 300
closed bars plus the forming candle per mounted series, which wire_to_core rebuilds into the bar
cache. spawn_bridge connects inside its own thread so window startup never blocks, and re-dials on
a dropped link with a two-second backoff sliced into 50 ms checks so a stop lands promptly. The
status line tracks each phase and ends OBSERVING <addr> (connected), led by the daemon's identity
once a frame carries one — name [LIVE] in uppercase for a live daemon, name [paper] otherwise —
and the OS window title reads Vike Trader — OBSERVING <addr>.
An unknown order status renders as Rejected
map_order_status decodes the wire's rendered status string and falls back, with a warning, to the
terminal Rejected for anything it does not recognise — so an unknown status can never render as
live or actionable. crates/vike-app-core/src/observe_bridge.rs's module doc records that before
the exhaustive round-trip test existed, a newly added status variant did exactly that, silently.
build_portfolio maps the wire's cross-venue totals but sets capital_base to 0.0, meaning
unknown, because the wire carries no seed and inventing one would let the drawdown curve read as a
real capital base on the observe path.
The control channel
Observing is read-only by default. observe_bridge.rs's connect_control builds a second,
dedicated connection under Scope::Control only when two things hold: VIKE_TRADEHUB_CONTROL=1 —
the exact string "1", read off the real process environment by
tradehub_control::control_enabled — and a non-blank control key. When it connects, the GUI's
order buttons drive the remote core, and the function's doc says it outright: this can place and
cancel real orders on the daemon. A missing key logs a warning and the observer stays read-only. For
a registry record the per-backend control gate is an additional AND, so a record file flipping
control = true is never sufficient on its own.
The status strip paints the armed segment amber
main.rs's status bar renders the control summary in a loud amber to flag that this observer can
drive real orders, with a red dot when the control link is down, and a latched server error is
click-to-dismiss. The trade panel routes to the snapshot's own market — under observe, the remote
daemon's venue and symbol — and refuses with NoRoutableMarket before the first frame arrives
rather than misrouting.
tradehub_control::wire_from_command lowers a live-core command into the wire vocabulary and is
deliberately partial: submit, cancel, modify, mass-cancel, flatten, market-exit, trading-state and
the strategy live-params re-tune have a wire form; brackets, batches, conditionals, combos, margin
changes, reconcile plumbing and shutdown yield None and are logged and dropped. The handshake
underneath — an HMAC-SHA256 nonce challenge bound to the scope, so an observe key can never forge a
control handshake — is on The node protocol; the daemon side is
vike-tradehub.
Switching at runtime
backend_conn.rs's switch_backend replaces the whole connection and performs the backend-session
clear its doc calls safety-critical: the old bridge is stopped and joined, session-owned render state
is reset and an empty snapshot published, so backend A's data never paints under backend B's
connection, while the local feed plane survives the switch. switching_available answers false
while a local core runs, so that path is byte-identical to before switching existed.
Connections
The venues × Sim/Demo/Live credential grid, the live Status column, the masked editor behind the one sanctioned credential write, per-venue accounts, and the remote-backend registry.
Workspaces
The multi-window desktop — arrangement, the minimized-window rail, the menu bar, and how a chart layout is persisted to workspace.json and named layouts without ever bricking startup.