The dev drive, plugged into another PC that was offline and then connected,
ran two sessions with no live aircraft. Its API log, on the drive, says why:
"upstream: live mode off (ODIAN_UPSTREAM_URL is unset)" - both times.
Root cause: live mode is decided when the stack starts (StackConfig::
upstream_url = Inner::engine_url), and engine_url was only ever learned from a
site probe and held in MEMORY. A launcher opened offline has none, and it probes
again only on window focus (30 s debounce) or the refresh button - so a Launch
pressed after connecting still used the offline answer, the API got no
ODIAN_UPSTREAM_URL, and live_loop (which would have synced the offline edits and
switched the API to the DHP when the network came) never started.
- launch() re-probes the site first when the launcher thinks it is offline (one
probe, 2 s at most on a PC that really is). It is also the update check a
launch from a stale "offline" skipped.
- The Engine API address is kept in launcher.json (LauncherState::engine, keyed
by site like tile_sync) and loaded at start, so a session started offline
still arms live mode and goes live by itself once the network is back - the
path live_loop already handles. Changing the DHP address in Settings drops it.
Spot-check: state::tests - per-site, unchanged-is-no-write, survives save/load,
and a launcher.json from before the field still loads.
A dev web launch on a PC without Node.js - a fresh one, or another PC the dev
drive was plugged into - failed with nothing but "stopped while starting (exit
code: 1)": `cmd /c npm run dev` had no npm to run, so nothing said why.
New nodejs.rs finds the folder holding node.exe AND npm.cmd - this process's
PATH first, then %ProgramFiles%\nodejs, where the installer puts it (an
install adds itself to the SYSTEM PATH, which the running launcher never sees).
Launcher::ensure_node runs before a web run's build/start: with no Node.js it
logs why and runs `winget install --id OpenJS.NodeJS.LTS --exact --source
winget --accept-*-agreements` in its own console (progress, Windows' permission
prompt and any failure stay on screen), then judges by what is on disk - not
by winget's exit code, which is non-zero for "already installed" too. No Node
afterwards stops the launch with the manual step spelled out.
`npm run dev` (web_command) and `npm install` (install_web) now get that folder
first on their PATH, so the launch goes on straight after the install without
restarting the launcher. start_web keeps a guard with a real message.
Spot-check: `winget show --id OpenJS.NodeJS.LTS --exact --source winget` names
the nodejs.org x64 MSI; nodejs::tests cover PATH-first, the fresh-install
folder, npm-without-node, and the argument list.
Two reasons an offline dev run's web client had no map:
1. Vite proxied every tile to the public tile host. web_command now sets
ODIAN_TILE_SERVER_URL=http://127.0.0.1:18081 for the Vite it starts, and
ODIAN_Engine's _WEB/vite.config.ts sends /basemap, /mvt and /netlayer there:
the site while it answers, this PC's installed tiles when it does not - the
same path the .exe already draws from.
2. The power-grid netlayer had no source offline. datasets.json named every
PostGIS overlay but `powerv2_postgis`, so the tile server answered
/netlayer/<ds>/powerv2/* with 404 "powerv2_render_sqlite not found" while
postgis_power_entities / _links / _topology sat in the local snapshot. Added
to POSTGIS_SOURCES; the tile server resolves it through the same
ODIAN_TILE_POSTGIS_DSN as the others.
Spot-check: with the site unreachable, /netlayer/<ds>/powerv2/bbox/8/128/86/131/89.json
answers 200 (721064 B, the same body the site serves) and topology.bin 200.
`npm run tauri dev` died before the window opened: Vite's watcher crashed
with EBUSY on target\debug\deps\odian_launcher_lib.dll. Root cause: the Cargo
workspace builds into target/ at the repo root, not under src-tauri/, and
vite.config.ts only excluded src-tauri - so Vite watched the DLLs the Rust
build holds locked.
Spot-check: a fresh clone, `npm install`, `npm run tauri dev` opens the
launcher window instead of "beforeDevCommand terminated".
A dev run's web client showed no live tracks while the .exe beside it did.
Root cause: the local API relays a request to the DHP only when it carries a
token that API minted itself (main.cpp's forwarder, the guard that keeps it
from being an open proxy). The .exe mints one with ODIAN_MINT_SECRET; the web
client was opened with ?user= only, so every /session/* call it made was
served from this PC's database.
The launcher now mints a token from this stack's API for the run's operator
(stack::mint_web_token) and opens the browser with it in the URL FRAGMENT
(StackConfig::web_url_signed). A fragment never reaches a request line or a
server log, and the launcher's own log still prints the tokenless address.
If the mint fails the web opens as before and the log says it shows this PC's
data only. Needs ODIAN_Engine's _WEB/src/auth.ts to read #token=.
Spot-check: launch dev mode with the web target while online; the API log
reads "now LIVE on the DHP" and the browser shows live tracks.
- Tauri 2 + Svelte 5 hub in ODIAN dark: patch notes and roadmap, install and
update progress, local stack status, settings with DHP and MCP addresses.
- crates/odian-packs: Rust installer for pack format v1 (signed manifests,
resumable verified downloads from a DHP or an ODIAN drive), byte-compatible
with ODIAN_MCP_Server's Python odian_packs.
- Local stack on 127.0.0.1: Postgres -> Engine API -> tile server -> Odian.exe,
one Job Object, clean shutdown; database snapshot restore on a fresh PC only.
- Keycloak sign-in through the DHP's MCP, remembered until Sign out with a
launcher session that renews the Engine token silently.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>