vike
ReferenceBinaries

vike-desktop

The desktop app — a native egui multi-window shell that is a lossy observer of a trading core it does not run, in one build with no cargo features.

The desktop application: a native egui multi-window shell with live charts. Each window has a rich title toolbar — chart-style menu, symbol dropdown, interval dropdown, LIVE badge, clone, minimize and maximize icons (crates/vike-desktop/src/main.rs).

Market data flows feed threads → core ingest (lossless bar closes, conflated forming bars and ticks) → the core-owned bar cache → an arc-swap CoreSnapshot, which this GUI converts to its render model once per changed snapshot. The GUI is a lossy observer; it never touches the core's state directly. That is the property every panel inherits, and it is why a slow frame can cost a picture and never an order.

One build, no features

crates/vike-desktop/Cargo.toml declares no [features] table, so there is one program here and cargo build -p vike-desktop is the whole of it. What that build does NOT contain is the interesting half, and the manifest records each absence as a tombstone rather than a deletion:

  • no trading core. vike-mount (the venue→ExecutionClient/ReconClient composition root) and vike-run (build_node) stood in the manifest as optional dependencies turned on by a fat feature. Both are gone with it, so no make_engine arm is compiled and this binary mounts no venue, signs no order and runs no reconciliation.
  • no DataFusion. vike-data is taken with DEFAULT features — the HistStore trait and nothing behind it. The manifest's own check is cargo tree -p vike-desktop -i datafusion, which must report the crate unreachable.
  • no collectors. vike-backfill was enabled by the same fat feature; the Data Manager's bulk action now takes backfill_route's WIRE arm against a datahub's store instead.
  • one venue crate, unconditionally. vike-deribit, because the options-chain provider vike-app-core mounts is Deribit's.

It still links the (DataFusion-free) Studio and drives it remote-only.

The ibkr and polymarket features went with fat

They gated live venue arms by flipping the matching vike-mount/vike-run feature. With no vike-mount dependency there is no arm to gate, so a venue named at this binary is not demoted to paper — it is not reachable from here at all. Those gates live in vike-tradehub now, which is the one live root.

--observe

vike-desktop --observe <ADDR> is the read-only client mode: it connects to a headless vike-tradehub daemon's observe server instead of spawning a local trading core — which it could not do in any case. The argv scan lives in the binary — argv is the binary's own fact — and feeds vike_app_core::backend_conn::startup_backend, which is the ladder that decides which node is actually dialled. A bare --observe is not an error: the app can do nothing else, so the word alone means "the configured node" and the ladder falls through to the registry. Observe mode is made unmistakable in the OS window title, which reads OBSERVING <addr> — and since the address always resolves, every run now carries that title rather than the historic — live one.

It no longer refuses to start without an address

There is no local trading core, so App::new's local-core arm is compiled out — but the observe address now always resolves (flag, else the active backend, else the local default), so the arm it could have fallen into is unreachable by construction rather than by an early exit. The window opens and the status bar says whether it connected, which is the thing a person can act on.

There is no local store engine either, so open_studio_store's no-address arm is an error naming both fixes — set config.datahub_addr to a running vike-datahub, or connect a backend whose daemon advertises one — rather than a feature-shaped "rebuild fatter" shrug. See observe mode for the mode itself.

Startup order

resolve_settings runs first — before the log subscriber, the window, any feed and any core. Two reasons, in order of weight: the log directory (config.log_dir) and both log levels are themselves settings, so a subscriber built before the load could only ever honour the environment; and a stale risk-ceiling variable must stop the process before it does anything at all. It logs nothing, which is why it can run that early; everything it would have said is emitted the instant the subscriber exists, including the warnings vike_config deliberately returns as data — a clamp nobody is told about is a limit the operator believes they set and does not have. A failure prints to stderr and exits 2.

The binary performs the process's one std::env::vars() sweep and hands it to vike_boot::boot, which owns the startup sequence: refuse removed environment variables, walk once for the settings directory, load the credentials and refuse an arming credential file, then load the settings and render the disclosure. The credential loader is passed as a function, so vike-boot owns when the store is opened and this binary owns how.

A credential file may not arm real money

secrets.env is plaintext and parsed last-wins, so appending one line to it — with no read access at all — would otherwise be enough to flip this app's venue mount onto a live venue. vike-boot runs that refusal the moment the credentials are loaded, before the settings load and long before the window, the feeds, the core and every mount (vike_config::arming). It does not change which sources arm; it makes an arming credential file stop the process rather than run it.

Where the logic actually lives

vike-desktop is deliberately outside the derived CI roster (xtask/src/ci/tables.rs's EXCLUDE_FROM_CI). CI's app-check job compiles it once — there is one feature resolution to compile — runs cargo clippy -p vike-desktop --all-targets -- -D warnings, and runs cargo nextest run -p vike-desktop — but only two things execute in the crate: the pure-logic byte-layout pins in crates/vike-desktop/src/chart_gpu.rs, and main.rs's font_binding_tests, which installs the fonts and renders one real frame to assert every named family is bound to a usable face — it exists because a Linux build that panicked on frame one shipped without it. Everything else, including the whole of App::update, is type-checked and linted and then never run.

That is why the decisions moved down into vike-app-core, which rides the normal roster: the per-frame CoreSnapshot fold (core_sync), the feed lifecycle, the startup layout, window spawn, order dispatch, venue routing and the --observe reverse bridge (observe_bridge, whose hand-maintained order-status mapping is now pinned by an exhaustive round-trip test — before the move, a newly added variant silently rendered as terminal Rejected in the observer). The size of the remaining unexecuted half is ratcheted by crates/vike-ops/tests/ci_excluded_gui_shell_ratchet.rs, which fails a PR that grows it. Putting a new branch in main.rs instead of in vike-app-core is choosing the unexecuted half.

Headless capture

The app has a QA capture mode driven by environment knobs the binary reads and injects as vike_app_core::startup::StartupEnv. VIKE_SHOT being present marks a headless capture and suppresses the saved-workspace restore, so a capture always renders the deterministic default windows; VIKE_SHOT_WIN selects which single tool window opens instead of the default chart; VIKE_STYLE forces every window to one ChartStyle and VIKE_SCALE forces every window's price-scale mode, each ignoring an unparseable or out-of-range value. The recognised VIKE_SHOT_WIN values are deliberately not written down in prose — the test each_shot_win_value_opens_exactly_its_own_tool_window_and_no_feed iterates them, so they live in something that goes red rather than in something that goes stale.

Related: the desktop app, the chart, connections, workspaces.

On this page