Architecture and implementation

Overview · Build and release · Olympus integration · Service contract

Responsibilities and boundaries

The system separates stable bootstrap programs from replaceable application releases. Olympus.Launcher.exe remains the shortcut/protocol target. Olympus.Agent.exe runs independently in the background. Neither executable loads Olympus assemblies into its own process; the application runs as a separate process from a verified version directory.

graph TD Setup["Olympus.Setup: branded Burn EXE"] --> MSI["Olympus.Installer: per-user MSI"] MSI --> Launcher["Stable launcher and startup UI"] MSI --> Agent["Background deployment agent"] User["Start menu, taskbar pin, or olympus URL"] --> Launcher Logon["Windows user sign-in"] --> Agent Launcher --> Core["Shared deployment core"] Agent --> Core Core --> Feed["Configuration service: release metadata"] Core --> Store["HTTPS package storage"] Core --> Local["State, receipts, and immutable Versions directories"] Launcher --> App["Verified Olympus application process"] Agent -. "updateReady IPC" .-> App Launcher -. "launch arguments IPC" .-> App App --> Data["Persistent Data directory"]

The diagram describes component relationships, not concurrent execution of every operation. The service, package host, and real application are outside this solution. Named-pipe handling and save/restart UI must be implemented by Olympus; the fixture demonstrates the protocol.

Installation layout and ownership

%LocalAppData%/Olympus/
  Launcher/Olympus.Launcher.exe          MSI-owned stable entry point
  Agent/Olympus.Agent.exe                MSI-owned updater
  Versions/<application-version>/        immutable application payload
    Olympus.exe                         or the manifest's relative executable
    .deployment-receipt.json             generated metadata and file hashes
  Staging/                              partial downloads and isolated extraction
  Data/                                 application-owned persistent data
  Logs/deployment-YYYYMMDD.log            deployment diagnostics, UTC date
  deployment.json                       selected versions and accepted policy
  startup-failures.json                  consecutive early failures per release
  installation.lock                     root-wide installation/state lock
  agent.lock                            root-wide agent singleton lock
  launch-<session-id>.lock               per-session launch gate

Paths in Storage.cs resolves the root, creates the runtime directories, and rejects reparse points in directory ancestry. The default root is fixed by the installed contract. --root exists for development and must be supplied consistently to manually started test processes; it is not a supported installer directory picker.

The MSI owns only the two bootstrap EXEs, the shortcut, its registration values, and empty-folder cleanup. It does not harvest Versions, Data, logs, or state. Uninstall therefore preserves them. The deployment runtime does not currently implement automatic version retention or orphan staging cleanup.

What setup does

Olympus.Setup uses the native WiX Standard Bootstrapper Application. Its theme XML, localization, mountain artwork, CODE Group logo, and button images provide the installer UI. The MSI is embedded, so obtaining the bootstrap tools does not require a separate MSI download. Downloading the real Olympus application on first launch still requires the deployment feed/package host.

Package.wxs declares a current-user MSI with CODE Group as publisher. It creates:

Resource Installed contract
Start menu shortcut Targets the stable launcher, uses the Olympus icon, and sets System.AppUserModel.ID to EPS.Olympus.
URL handler HKCU Software\Classes\olympus; open command is the quoted launcher followed by -- "%1".
Agent logon entry HKCU, 64-bit view, Software\Microsoft\Windows\CurrentVersion\Run, value Olympus.Agent, containing the quoted stable agent path.
Component key paths HKCU values under Software\EPS\Olympus\Installer, used for MSI component ownership.

Before replacing/removing its files, the MSI uses WiX CloseApplication to terminate accessible processes named Olympus.Agent.exe. This is specific to the headless updater and may include development agents with that name. It does not target the real Olympus.exe. After committed installation/repair, an asynchronous action starts the installed agent; the success page separately offers to open Olympus.

Restart Manager is configured with DisableShutdown, retaining file-in-use detection without automatic shutdown mitigation. A locked launcher or another unresolved file-in-use condition can still require user intervention or a restart. Installation lifecycle behavior must be tested on real machines.

The permanent MSI UpgradeCode is B39325E4-8D28-49DA-8D1A-F4DB3D57F6C7; the bundle UpgradeCode is 2F73FDE5-20ED-45C6-A776-6B4EFC4444EA. Keep both stable across releases and CPUs. File-component GUIDs differ by CPU. Major upgrade removal is scheduled after InstallInitialize, inside the installation transaction. Downgrades and duplicate same-version MSI products are blocked; an ALLUSERS override is rejected.

Theme implementation details

The setup's CODE Group logo is composed into the upper-right of the mountain hero. Its original transparent PNG remains in Assets/code-group.png. New-InstallerArtwork.ps1 regenerates the hero and rounded button states. The version label sits at the lower-left. The launcher embeds its own artwork and multiresolution icon as assembly resources.

WiX theme font colors are Win32 BGR values, not HTML RGB. Each text control needs an explicit FontId: labels do not inherit the window's font/color settings. Missing it produces the default light background and black text. A layout render alone will not catch native-control styling problems; capture the built setup during UI review.

Fresh application launch

  1. Program.Main sets the launcher's process AppUserModelID, configures WinForms, and creates StartupForm.
  2. The form starts deployment work off the UI thread and consumes DeploymentProgress reports.
  3. LaunchAsync parses arguments, creates the paths, and acquires the session launch gate. A linked 16-minute cancellation budget bounds the launch operation.
  4. It first sends a launch message to the current session's Olympus pipe. An accepted acknowledgement finishes forwarding. If a managed process exists but cannot accept IPC, the launcher avoids a duplicate; arguments that cannot be forwarded produce a visible error.
  5. With no running primary, it reads state under the installation lock, reconciles startup failures, and attempts a verified local retry if a previous early failure cleared active selection.
  6. It checks the deployment feed, with a five-second manifest deadline. This check happens even when an active release already exists. A real package download has the update cycle's separate 15-minute budget.
  7. A transport/I/O failure may fall back to an installed release. Selection still checks cached or newly accepted minimum-version policy, data compatibility, prerequisites, and payload integrity. An invalid manifest or an outdated launcher is not silently treated as ordinary offline operation.
  8. Under the installation lock, it attempts to select a verified pending version. Promotion waits until no managed Olympus process is running in any session. If a pending update cannot be selected while an active version remains, the error is logged and the active candidate is still validated before use.
  9. It verifies the receipt and every payload file, checks bootstrap/application minimums and prerequisites, and creates a separate Olympus process. It sets the integration environment variables and passes each application argument through ArgumentList.
  10. It observes startup, updates last-known-good/failure state, and releases the installation lock. It then starts the stable installed agent as a fallback. The agent's singleton lock prevents duplicate updater instances.

What completes startup

ObserveStartupAsync refreshes Process state and checks for a main window every 100 ms. A nonzero main-window handle completes readiness immediately and hides the splash; later application exits are outside launcher monitoring.

A process that exits normally before window detection closes startup quietly without recording failure or marking that release known-good. A nonzero exit before readiness is a confirmed startup failure. If no main window appears and the process remains alive, the legacy windowless fallback completes after ten seconds. That fallback and window visibility are heuristics, not an application-initialized health handshake.

The launcher does not wait ten more seconds after detecting a window. Closing Olympus immediately after its window opens must not resurrect the splash.

Background update cycle

The agent owns agent.lock for its lifetime across the user's sessions. It is a WinExe, not a console application or Windows service. It checks immediately on startup, then normally every 15 minutes. After failed checks, the backoff sequence is 30, 60, 120, 240, 480, then 900 seconds, capped at 15 minutes. The loop wakes every 30 seconds to evaluate work and notifications.

UpdateClient.CheckAsync is shared with the launcher:

  1. Snapshot state under the installation lock.
  2. Fetch/reuse and validate the service manifest outside the lock. Enforce the caller's actual launcher or agent minimum version.
  3. Under the lock, reject version/minimum-floor downgrades or a changed hash for the same published version, record accepted policy and its deadline, cache the response source/ETag, and assess whether the release is blocked or already present.
  4. Detect prerequisites, download the ZIP outside the lock, enforce size limits, and verify SHA-256.
  5. Reacquire the lock and re-read state. If the manifest is still current and allowed, install/verify the package and set Pending.
  6. Delete the completed temporary download after the installation attempt.

This cycle stages an update; it never activates it, restarts Olympus, or replaces a running application's files. A fresh launcher selects the pending version once all managed sessions have stopped. ETag/304 reuse still retries unfinished package work and bootstrap compatibility checks.

Persisted state and policy

State field Meaning
active Release selected for new application starts.
pending Verified installed release waiting for activation.
lastKnownGood Last release that met the window/legacy readiness check.
failedVersions Releases blocked after three consecutive confirmed early process failures.
manifest Last accepted internal deployment metadata, including minimum floor and resolved deadline.
manifestSource, eTag Scope and validator for conditional HTTP requests.
manifestFirstAcceptedUtc First local acceptance of the current release, used for its durable countdown.

State, receipts, and failure history use source-generated System.Text.Json metadata. Writes use a unique sibling temporary file, flush it, and atomically replace/move it on the same volume. Callers still need the installation lock; atomic writes do not serialize competing updates.

The service sends restartDeadlineMinutes. State resolves it from the release's first acceptance time and persists the resulting UTC deadline before downloading. Polling, HTTP 304, and process restarts cannot extend it. A same-release policy change can shorten the deadline, but cannot remove/extend an already established one. The agent sends the resolved deadlineUtc to Olympus. It is advisory and does not authorize forced termination. Local clock changes are not defended as a trusted-time protocol.

Locking

Lock Scope Protects
launch-<session>.lock One root, one Windows session Launch/forward/startup ordering between launchers.
installation.lock Whole installation root, all sessions State changes, extraction, activation, and startup readiness state.
agent.lock Whole root, all sessions One background agent owner.

Ownership is an exclusive open file handle, not the existence of the lock file. Process termination releases the handle. Installation/launch contention retries Windows sharing/lock violations at 200 ms; unrelated I/O errors propagate. The splash reports waiting once rather than treating every retry as a new error. Network package downloads do not hold the installation lock.

IPC names deliberately depend on user/session, not root. Separate development roots in the same session can still address the same primary pipe. Use an isolated test account/session for real-process tests, rather than assuming --root provides IPC isolation.

Startup failure and rollback rules

startup-failures.json stores consecutive confirmed early nonzero process exits per application release. The automatic block threshold is three. A first or second failure can trigger a verified last-known-good fallback while leaving the failed release eligible for a later attempt. Successful readiness clears the streak. Legacy one-strike blocked entries migrate to one recorded failure, not permanent quarantine.

One launcher invocation attempts at most the initial selection plus one fallback. A fallback must be intact, meet the cached minimum floor and prerequisites, and share the data compatibility ID. A minimum or data-format constraint can legitimately prevent rollback. An allowed local retry uses recorded candidates and receipts, not an arbitrary scan of unknown version directories.

Network errors, cancellation, missing prerequisites, file/process-creation errors, normal exits, and application exits after readiness do not count as those startup failures. Corrupt state fails with diagnostics; automatic reconstruction or deletion is not implemented.

Package integrity and trust

The downloader accepts normal TLS-validated HTTPS, disables redirects, and limits packages to 2 GiB. Extraction limits expanded content to 4 GiB and 20,000 archive entries. It normalizes ZIP separators before checking traversal/absolute/ambiguous paths, reserved device names, alternate streams, case collisions, and links. Packages cannot supply the deployment receipt.

The entry point needs an MZ/PE header and a compatible machine type; x86 PE is allowed for legacy Framework/AnyCPU payloads on either supported OS architecture. These checks do not prove application dependencies are complete or the executable is signed.

Extraction occurs in a unique staging directory. The runtime hashes the extracted files into a receipt before a same-volume rename to Versions/<version>. Installed directories are immutable. Launch verification rejects missing, modified, extra, or linked files. Hashing every file can affect startup time with large real-world payloads.

SHA-256 establishes integrity relative to the accepted manifest. There is no signed-manifest envelope, endpoint user authentication, or Authenticode enforcement in this client. A malicious same-user process is also outside the local receipt/state trust guarantee. These are production hardening/integration decisions, not protections implied by the hash.

Source-code map

File or area, relative to repository root Implementation
Olympus.Launcher/Program.cs Launch/forward flow, environment, process creation, readiness observation, rollback attempt, fallback agent start.
Olympus.Launcher/StartupForm.cs Borderless form, embedded artwork, progress, cancellation, retry, diagnostics, rounded corners/DPI handling.
Olympus.Launcher/TaskbarIdentity.cs Permanent process AppUserModelID.
Olympus.Agent/Program.cs Root singleton, scheduling/backoff, session notifications, manual startup registration commands.
Olympus.Deployment.Core/Storage.cs Paths, JSON persistence, installation lock, diagnostics, command-line options, endpoint.
Olympus.Deployment.Core/UpdateClient.cs HTTP request, ETag, bootstrap checks, accepted policy, download/install orchestration.
Olympus.Deployment.Core/ServiceManifest.cs Nested service transport model and mapping to internal metadata.
Olympus.Deployment.Core/Models.cs Internal state/receipt/IPC models, JSON contexts, release/dependency version parsing.
Olympus.Deployment.Core/Packages.cs Download limits, SHA-256, extraction, receipts, PE and installed-file validation.
Olympus.Deployment.Core/Lifecycle.cs Process discovery, pending activation, prerequisite checks, fallback and local retry.
Olympus.Deployment.Core/StartupFailures.cs Three-failure policy and legacy reconciliation.
Olympus.Deployment.Core/Ipc.cs Bounded framed named-pipe client and serialization helpers.
Olympus.Deployment.Core/AgentStartup.cs Explicit manual HKCU Run registration support.
Olympus.Deployment.Core/Prerequisites.cs Allowlisted prerequisite probes; the WebView2 special case and its limitations are documented in the service contract.
installer/Olympus.Installer/Package.wxs MSI ownership, shortcut identity, protocol/logon registry, lifecycle actions and upgrades.
installer/Olympus.Setup/Bundle.wxs, Theme.xml, Theme.wxl Embedded MSI and native branded setup experience.
scripts/Build-Installer.ps1, Test-Installer.ps1 Publishing/packaging pipeline and compiled MSI contract checks.

The generator-required JSON context declarations are partial for System.Text.Json source generation. Other handwritten C# classes remain in their primary files; the application integration examples follow the same convention.