Architecture (EDOS workspace)¶
Audience: Agent
This document describes how the standalone projects in this workspace fit together at runtime and which contracts matter when changing behavior. For a folder-by-folder inventory, see REPOS.md. For message-level journal/file IPC, see IPC.md. For URL bootstrap and embed manifests on hosted UIs, see HOSTED_APP_CONTRACT.md. For optional federated app discovery (composition hosts, link-only registry indexes), see APP_DISCOVERY_REGISTRY.md.
Design principles¶
- Not a monorepo — each top-level folder is an independent unit (own dependencies, build, deploy). Integration is via published npm packages (
@howfe/...) and HTTP / browser APIs (postMessage,fetch). - Journal data stays local — the hosted “remote journal reader” does not upload journal files to a server; it uses the File System Access API in the browser and forwards parsed events to an opener window via
postMessage. - Shared typing —
@howfe/elite-dangerous-event-typesis the single source of truth for journal JSON shapes,Status.json, the desktopBridgeEnvelope(EdosFileEventEnvelope,isEdosFileEventEnvelope), and loose guards for sidecar snapshot blobs (Cargo.json,Market.json, …). - Shared IPC helper —
@howfe/inter-frame-messengerwrapswindow.postMessagewith allowlists andMessageTypeEnumvalues, includingedos-file-event(normative envelope for snapshots + journal metadata) alongside legacyelite-event/elite-statusbare payloads used by the hosted remote journal reader. - Composition sibling IPC — When multiple apps run as sibling iframes under EDAC, cross-app queries (for example Stellar Scan reading Universe Places bookmarks) go through the composition host proxy (
edac.places.query→edos.places.query), not peerpostMessageand not hidden provider iframes. See HOSTED_APP_CONTRACT.md. - Community APIs via cache — Calls to third-party Elite Dangerous community APIs (SPANSH, EDSM, and similar) must go through the API cache server, not direct
fetchto those endpoints from apps. Those services are run by volunteers in their spare time and typically have no DDoS or abuse protection. The cache is the fair-use layer: deduplicate requests, spread load, and avoid hammering upstream when many users or tools run at once. - Optional visual identity — First-party web UIs may share a light corporate design (orange-on-black design tokens and layout habits) in DESIGN_GUIDANCE.md. Stack-agnostic; legacy Quasar apps are not the template for new work.
- Curated data from other Elite Dangerous tools — EDOS does not import curated game data from other Elite Dangerous applications; use citable public references or verify and maintain the data in-repo. See DOC_CONVENTIONS.md.
Major subsystems¶
| Subsystem | Responsibility |
|---|---|
| Event types | TypeScript types + guards for journal lines, Status.json, sidecar snapshot shapes, and EdosFileEventEnvelope. |
| Inter-frame messenger | Typed postMessage envelope, origin checks; elite-event / elite-status (remote reader); edos-file-event (full bridge envelope). |
| Local journal bridge (desktop) | Tauri app tails Journal*.log + JSON sidecars; Tauri IPC edos:file:event; injected script reposts edos-file-event (+ legacy shorthands) to the embedded web UI. See elite-dangerous-local-journal-reader/. |
| Remote journal reader (web UI) | User picks Status.json and journal .log locally; polls files; emits elite-event / elite-status bare payloads to window.opener. |
| Journal event viewer (web UI) | Hosted EDJEV app (edjev in current deployments); same bridge/postMessage contracts when embedded in the local journal reader webview — not a separate native host. |
| Surface map (web UI) | Map + external API loaders; opens journal reader popup; subscribes to journal/status events. |
| Stellar Scan (web UI) | Per-system body table; journal handlers + SPANSH via api-cache; IFM listener on window (elite-dangerous-stellar-scan/src/composables/useJournalConnection.ts). |
| Credit Account (web UI) | Commander credit ledger from journal events; IFM listener on window (elite-dangerous-credit-account/src/composables/useJournalConnection.ts); no community HTTP APIs in the initial app. |
| Planet analyser (HTTP API) | Scan-credit / lifeform analysis service (elite-dangerous-planet-analyser/api/); uses stellar-forge-helper + event-types. |
| API cache server | Required front for community third-party HTTP APIs: proxy + cache + queue so apps do not stress volunteer-run endpoints (see principle 5 above). |
| App composition (EDAC) | Hosted shell: journal hub + fan-out to slots, declarative rules, edac.places.query proxy for sibling iframe access to provider apps (Universe Places first). |
Runtime topology¶
Typical production setup (URLs are hardcoded in app code today; see REPOS.md):
- Surface map is loaded in the user’s browser (origin varies by deployment).
- Remote journal reader is loaded in a popup at
https://edjr.howfe.org(opened from surface map and other hosted apps). - Journal event viewer (EDJEV) is commonly loaded at
https://edjev.howfe.org— in the browser or as the defaultappUrlinside the local journal reader webview (see REPOS.md data-flow diagram). - Surface map at
https://edam.howfe.org(Quasar UI; opens EDJR popup). - Stellar Scan at
https://edss.howfe.org(table UI; same journal/cache patterns as other hosted apps). - Credit Account at
https://edca.howfe.org(credit ledger; journal via EDJR popup or local bridge when embedded). - Expedition planner at
https://edep.howfe.org(route progress UI). - Interstellar Scan at
https://edis.howfe.org. - Systems uninhabitet at
https://edsu.howfe.org(colonisation candidate search). - Planet analyser at
https://edpa.howfe.org(headless HTTP API for scan credits / lifeforms; no browser UI). - API cache at
https://api-cache.howfe.orgfronts calls to SPANSH, EDSM, etc.
Full slug registry and naming rules for new apps: HOSTNAMES.md.
Hosted web apps in a Tauri shell (local journal reader)¶
EDJR (remote journal reader) is a hosted HTTPS Quasar (Vue 3) app in production. EDJEV (journal event viewer) is Vite + Solid (elite-dangerous-journal-event-viewer/). They are not Tauri projects. The only EDOS desktop shell that watches disk and republishes edos-file-event is elite-dangerous-local-journal-reader: it loads one appUrl in a WebView and injects the bridge script so the page receives the same postMessage / CustomEvent contract as documented in IPC.md. URL resolution, PE-tail–wired .exe downloads, and the /build API live in that repo’s README.md and elite-dangerous-local-journal-reader/docs/build-service.md.
Treat “install on desktop” for any hosted EDOS journal UI as: call elite-dangerous-local-journal-reader’s build-service (or link to an artifact it produced) at its public deployment URL https://app.edos.howfe.org (/build, /artifacts/... — see REPOS.md) — not “add Tauri to this web repo.” The webview appUrl embedded in the .exe (e.g. default edjev) is separate: that is which web app loads inside the shell, not where the build API runs.
Hosted app bootstrap (query parameters, integration manifests, optional embed handshakes when loading in a frame or webview) is documented in HOSTED_APP_CONTRACT.md. That contract is separate from IPC.md, which documents InterFrameMessenger journal and file events.
Journal and status pipeline¶
- Elite Dangerous writes
*.log(one JSON object per line) andStatus.jsonunder the player’s Saved Games path (OS-specific). - In the journal reader UI, the user grants file access via the File System Access API (
@vueuse/coreuseFileSystemAccess). JournalWatcher(elite-dangerous-remote-journal-reader/ui/src/JournalWatcher.ts) reads text, splits lines,JSON.parses each line, filters withisEliteEvent, deduplicates with an in-memoryeventHistoryof raw lines, then sends new events via the messenger.StatusWatcher(StatusWatcher.ts) parses the whole JSON file, comparestimestampto avoid duplicate sends, and sendsStatuswhen it changes.- Both watchers use
InterFrameMessengerwithtarget = window.openerso events flow from popup → parent (sendusestarget.postMessage).
The surface map does not read journal files directly; it receives EliteEvent and Status only through useJournalReader (elite-dangerous-surface-map/ui/src/composables/useJournalReader.ts), which listens on the parent InterFrameMessenger instance bound to the popup window.
Sidecar JSON files (desktop bridge)¶
Elite also writes snapshot files next to the journal (Cargo.json, Backpack.json, FCMaterials.json, NavRoute.json, ShipLocker.json, Market.json, Outfitting.json, Shipyard.json, Status.json). The local bridge watches these and emits BridgeEnvelope rows on Tauri edos:file:event, republished to the webview as IFM-shaped edos-file-event. Types and guards: @howfe/elite-dangerous-event-types (edosEnvelope.ts). ModulesInfo.json appears in some sample folders but is not part of the bridge default_sources list until verified against current game output — see elite-dangerous-local-journal-reader/docs/file-source-verification.md.
Surface map consumption of events¶
useJournalReaderis a module-level singleton pattern: one sharedifmand callback lists.readGalaxyDataFromEvents()registersonEliteEventhandlers for scans, location, codex, organics, etc.useCommanderStateregistersonStatusfor commander/vehicle state.startJournalReader()(e.g. fromInteractionButtons.vue) opens the popup and constructs the parent-sideInterFrameMessengerwith listen typesEliteEventandStatus,allowedSenderOrigins: ['*'],allowedListenerOrigins: ['https://edjr.howfe.org'].
Third-party HTTP data (non-journal)¶
- Rule: Any new or existing app that calls community-maintained HTTP APIs must route those calls through the cache server (same pattern as
fetchWithCache). Direct client→upstream calls for those APIs are not acceptable: they increase load on services that lack capacity planning and DDoS defenses. - Current pattern: Loaders under
elite-dangerous-surface-map/ui/src/logic/externalDataLoaders/usefetchWithCache(fetchWithCache.ts), which requestshttps://api-cache.howfe.org?url=...so responses are deduplicated and cached server-side (api-cache-server/api/src/app.ts). Self-hosted deployments should point at their cache instance with the same contract, not at upstream APIs from every browser tab.
Relationship to original-concept.md¶
original-concept.md lists general WebView / postMessage risks (origin validation, performance, legacy WebViews). This codebase implements origin filtering in InterFrameMessenger (allowedSenderOrigins / allowedListenerOrigins). For exact rules per side, see IPC.md.
Sequence: open popup and receive events¶
See IPC.md for sequence diagrams (parent/child roles and message envelope).
Gaps / TODO¶
| Topic | Follow-up |
|---|---|
| Remote reader + sidecars | Hosted journal reader does not yet read/poll sidecar JSON or emit edos-file-event; desktop bridge is the source of full-envelope streaming today. |
| Host-driven journal commands | Child listens for OpenJournal / PollJournal / etc.; surface map parent currently uses empty sendMessageTypes — remote open is via UI inside the popup, not from the map. TODO: Verify if you add parent→popup automation. |
| Configurable service URLs | edjr / api-cache / default edjev bridge targets are fixed strings in TS or Tauri config — consider env-based configuration for self-hosting. |