Files
launcher/docs/CONTRACT.md
T
Kelorastus b7f3061be3 ODIAN Launcher: hub, installer, local stack and remembered sign-in (M2)
- 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>
2026-09-15 15:23:17 +02:00

5.2 KiB

UI ↔ backend contract

The Svelte UI (src/) talks to the Rust backend (src-tauri/) only through the Tauri commands and events below. src/lib/backend.ts is the single place that calls them. When the page runs outside Tauri (a plain browser, for design work), it falls back to a mock with the same shapes.

All names are snake_case on the wire (serde defaults), and timestamps are Unix seconds.

Types

type Phase =
  | "idle"          // nothing running
  | "checking"      // probing the site
  | "signing_in"    // browser sign-in in progress
  | "installing"    // downloading + verifying a version
  | "restoring"     // loading the database snapshot into local Postgres
  | "launching"     // starting Postgres → API → tile server → Odian.exe
  | "running"       // Odian.exe is open
  | "error";        // the last long operation failed; see `error`

interface Status {
  online: boolean;              // the site's update service answered /v1/health
  site: { url: string; name: string | null };
  operator: string | null;      // last signed-in ODIAN username (cached; valid offline)
  signed_in: boolean;           // this PC remembers the operator's site sign-in until Sign out: the
                                // launcher renews the short Engine token by itself (launcher session)
  installed: string | null;     // installed version, e.g. "0.51.0"
  available: string | null;    // the site's CURRENT version (online only)
  update_required: boolean;     // online && installed !== null && available !== null && installed !== available
                                // (never true when nothing is installed: that is a first install)
  can_launch: boolean;          // installed && !update_required && phase is idle or error
  phase: Phase;
  error: string | null;         // set with phase "error"; cleared by the next command or `refresh`
  install_dir: string;          // e.g. "D:\\ODIAN"
}

interface Settings {
  site_url: string;             // e.g. "http://192.168.1.41:30097"
  mcp_url: string;              // site MCP, for Keycloak sign-in
  install_dir: string;
}

interface InstallProgress {
  stage: "manifest" | "app" | "db" | "src" | "tiles" | "restore" | "finalize";
  stage_index: number;          // 1-based
  stage_count: number;
  done_bytes: number;           // within the current stage
  total_bytes: number;          // of the current stage; 0 when unknown
  done_items: number;           // within the current stage
  total_items: number;
  bytes_per_sec: number;
  message: string | null;       // e.g. "tiles dark/protomaps_20260423: 412/1402 blocks"
}

interface StackEvent {
  component: "postgres" | "api" | "tiles" | "odian";
  state: "starting" | "ready" | "stopped" | "failed";
  message: string | null;
}

interface LogEvent { level: "info" | "warn" | "error"; message: string; at: number }

Commands (invoke)

Command Args Returns Notes
get_status — Status cheap; no network
refresh — Status re-probes the site (2 s timeout); also clears an error phase
sign_in — { username: string } opens the system browser (MCP desktop flow) and waits for the loopback callback. Rejects with "Sign-in was cancelled." after cancel_sign_in (phase returns to idle, not error).
cancel_sign_in — void abandons a sign-in that is waiting for the browser
sign_out — Status forgets the operator on this PC: the site sign-in, its launcher session (ended on the site when reachable) and the cached operator. Offline launch needs a new sign-in afterwards, so the UI confirms first
get_notes { version?: string } string Markdown. From the site when online; the cached copy of the installed version offline. Untrusted: the UI must sanitize.
open_external { url: string } void opens an http/https link in the system browser (links in patch notes); anything else is refused
start_install — void installs or updates to available; needs signed_in (the site checks the token); progress arrives as events
start_install_from_drive { path: string } void same, reading an ODIAN drive (SSD) instead of the site; no sign-in needed
cancel_install — void stops at the next file boundary; everything already verified is kept
launch — void starts the local stack; stack events follow
stop — void stops Odian.exe's stack cleanly
get_stack — StackEvent[] the latest event per component of the current or last launch (for a reloaded UI)
get_settings — Settings
set_settings { settings: Settings } Status
pick_folder — string | null native folder picker

Errors reject the promise with a string message.

Planned, not yet available: the size of available for the Install button, and inspecting a drive (its version and size) before installing from it.

Events (listen)

Event Payload When
status Status whenever any field changes
install-progress InstallProgress at most ~10 per second while installing
stack StackEvent on each component state change
log LogEvent notable backend messages, for the activity panel