Skip to content

elite-dangerous-local-journal-reader

Audience: Agent

Purpose

Desktop (Tauri 2) application that watches Elite Dangerous journal *.log lines and sidecar JSON on disk, wraps each update in a BridgeEnvelope, and streams it to an embedded web UI over Tauri IPC edos:file:event. An injected bridge script re-emits the same payload as browser postMessage / CustomEvent using IFM outer name edos-file-event (plus legacy elite-event / elite-status shorthands). Complements the hosted elite-dangerous-remote-journal-reader popup: same shared types and message strings, different host (native shell vs browser + File System Access API).

Tech stack

  • Shell: Rust, Tauri 2 (src-tauri/)
  • Bridge helpers (published patterns): TypeScript src/edosBridge.ts — depends on @howfe/elite-dangerous-event-types and @tauri-apps/api only inside this repo; copied or imported bridge listeners in other web apps must not bundle Tauri (see contract doc).
  • Optional tooling: Node build-service/ — see elite-dangerous-local-journal-reader/docs/build-service.md.

Layout

Path Role
src-tauri/src/bridge.rs EVENT_NAME = edos:file:event; string constants aligned with MessageTypeEnum (elite-event, elite-status)
src/edosBridge.ts listenEliteEventFromBridge, listenEdosFileEventFromBridge, debug helpers; re-exports envelope types/constants
elite-dangerous-local-journal-reader/docs/bridge-contract.md Contract for embedded apps (event names, payloads, security)

Integration points

  • @howfe/elite-dangerous-event-typesEdosFileEventEnvelope, EDOS_FILE_EVENT_MESSAGE (edos-file-event), TAURI_EDOS_FILE_EVENT_CHANNEL (edos:file:event).
  • @howfe/inter-frame-messenger — same outer event strings for parity with remote reader / surface map IPC (IPC.md).
  • Embedded URL — which web app loads inside the webview is a product/host choice; TODO: Verify src-tauri/tauri.conf.json / capabilities for allowed remote.urls when documenting a specific pairing.

URL-wired “install on desktop” (build service)

The Node build-service/ is a small HTTP API that produces a single Windows .exe with your hosted web app URL embedded in a PE trailer (after marker <<<EDOS_BRIDGE_TAIL_V1>>>), so the Tauri shell opens that URL in the webview without extra config files. Full contract, cache keys, env vars, and Docker build context are in elite-dangerous-local-journal-reader/docs/build-service.md; machine-readable API in elite-dangerous-local-journal-reader/build-service/api/openapi.yaml; URL resolution order (CLI, env, tail, compile-time) in elite-dangerous-local-journal-reader/README.md.

Typical “Get desktop bridge” flow for a hosted web UI

  1. Choose appUrl = the canonical HTTPS URL of the web app you want inside the bridge (the same deployment users already open in the browser).
  2. Call POST /build with JSON { "appUrl": "<that url>", "targetPlatform": "windows-x64" } on the build-service host, or GET /build?appUrl=… (query form is bookmark-friendly; GET still performs build or cache lookup — do not treat it as a safe prefetch).
  3. Read JSON: use artifactDownloadPath (path only) on the same host as the API — production deployment for this repo’s build-service: https://app.edos.howfe.org${artifactDownloadPath} — to download the wired executable (Content-Disposition: attachment on GET /artifacts/<file>).
  4. In the UI, start the download from the hosted app (see Download filename below). Avoid sending users straight to the artifact URL with location.href or a plain <a href> if you want a short, product-specific save name.

Download filename (hosted “Get desktop bridge” buttons)

GET /artifacts/<file> always uses a deterministic server file name (for example edos-local-journal-bridge-0.1.0-012d749e40c3.exe). That string is the on-disk cache key, not a product marketing name. Each hosted app chooses the name shown in the user’s Downloads folder when implementing its download button.

Recommended (works cross-origin): after step 2–3, fetch the artifact bytes from the build-service origin, then trigger save with a programmatic link and download:

const BUILD_SERVICE_ORIGIN = 'https://app.edos.howfe.org';

/** Per-app: e.g. `edam.exe` (surface map), `edjev.exe` (EDJEV). */
const DESKTOP_BRIDGE_FILENAME = 'edjev.exe';

async function downloadDesktopBridge(appUrl: string): Promise<void> {
  const buildRes = await fetch(
    `${BUILD_SERVICE_ORIGIN}/build?${new URLSearchParams({
      appUrl,
      targetPlatform: 'windows-x64',
    })}`,
    { headers: { Accept: 'application/json' }, cache: 'no-store' },
  );
  const build = (await buildRes.json()) as { artifactDownloadPath?: string };
  if (!buildRes.ok || !build.artifactDownloadPath?.startsWith('/')) {
    throw new Error('Build request failed or missing artifactDownloadPath');
  }

  const artifactUrl = `${BUILD_SERVICE_ORIGIN}${build.artifactDownloadPath}`;
  const fileRes = await fetch(artifactUrl);
  if (!fileRes.ok) {
    throw new Error(`Artifact download failed (${fileRes.status})`);
  }

  const blob = await fileRes.blob();
  const objectUrl = URL.createObjectURL(blob);
  const anchor = document.createElement('a');
  anchor.href = objectUrl;
  anchor.download = DESKTOP_BRIDGE_FILENAME;
  anchor.click();
  URL.revokeObjectURL(objectUrl);
}

Why not only <a download="edjev.exe" href="…/artifacts/…">? The HTML download attribute is often ignored for cross-origin URLs (hosted app on edjev.howfe.org, artifacts on app.edos.howfe.org). Navigating to the artifact URL directly uses the server’s Content-Disposition file name (the long cache key). The blob + download pattern avoids both issues.

Not required in build-service: the API does not need a per-app file name parameter; cache entries stay keyed by appUrl + version metadata. Optional future enhancement: GET /artifacts/...?filename=edjev.exe to override Content-Disposition for bookmark-only flows without JavaScript — TODO: Verify if implemented before relying on it.

Browser / CORS: the Node app (build-service/src/server.ts) does not set CORS. For the public https://app.edos.howfe.org deployment, Traefik (reverse proxy in front of the build-service container) adds CORS headers for browser fetch to /build and /artifacts; change policy there if a hosted SPA’s preflight still fails.

Extension points

  • Add or adjust watched sources in Rust (source_registry, watchers) and keep SourceType / EdosBridgeSourceKind aligned (elite-dangerous-event-types/edosEnvelope.ts).
  • build-service/ — OpenAPI-driven packaging flow per project-local docs.

Pitfalls / checks

  • Do not require @tauri-apps/api in generic web apps; only the shell uses invoke/listen — see bridge-contract.md.
  • Origin filtering — embedded listeners should pass expectedOrigin: window.location.origin where applicable (edosBridge.ts).
  • Rust ↔ TypeScriptBridgeEnvelope uses camelCase JSON (serde(rename_all = "camelCase")); match EdosFileEventEnvelope fields.