Files
OpenResearch/docs/linux.md
T
Myles AndersonandClaude Opus 5.5 c95be22a35 OR-326 Add the Linux desktop app as a self-updating AppImage (#445)
* Open the macOS app's dashboard in its own window

The OpenResearch.app now hosts the dashboard in a native window (tao + wry
WKWebView) instead of handing it to the user's browser. It adds a standard
menu bar, save panels for downloads, a native window.confirm panel, and a
Cmd+Q that flushes workspace state and shuts the server down cleanly.
Pop-ups open in the system browser (http/https/mailto only).

The app now prefers port 4792 so the window's localStorage survives
relaunches. The Dock-click tab-focus script, its Apple-events entitlement,
and the SSE client counter it relied on are removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Open the dashboard in its own window on Windows

Adds the Windows desktop app. A small GUI-subsystem OpenResearch.exe
(windows/launcher) starts the orx.exe beside it as `orx app` in a hidden
console, so orx and the git, shell, and agent processes it runs share one
invisible console instead of each flashing a window.

`orx app` shares the macOS window code in src/commands/app.rs: WebView2
window, pop-ups to the system browser, save panel, page-load reveal, and
port 4792. On Windows, closing the window quits, a second launch focuses
the running window (named mutex + event), and the taskbar groups the
window with the Start menu shortcut. An update restart relaunches as the
app on the same port. Quit on both platforms now goes through
up::request_shutdown instead of a self-sent SIGTERM.

An Inno Setup script builds a per-user OpenResearch-Setup.exe with a Start
menu entry and a WebView2 bootstrap. CI builds it as an artifact, and
release-windows-app.yml attaches it to releases once WINDOWS_APP_ENABLED
is set. The icon is embedded via embed-resource.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Fix the Windows build and address review of the Windows app

- The focus listener captured its bare HANDLE (edition 2021 disjoint
  capture) instead of the Send wrapper, which failed to compile on Windows.
- An orx.exe away from the CLI installer's prefix, like the app's, is now
  Portable even when the installer's receipt exists, so it can update itself.
- Single instance creates its event before the mutex; the launcher hands its
  foreground rights to orx.exe.
- Focus requests and server readiness are ignored while quitting, and focus
  waits for the first load. The save dialog is owned by the window.
- Telemetry counts an app start only after the instance claim.
- The installer reports a failed WebView2 bootstrap and shows progress.
- Icon embedding now fails the build if no resource compiler is found.
- CI format-checks the launcher; the release job drops an unpinned action.
- Docs: maintainer notes for the release gate, two known gaps, and wording.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Tighten the Windows app after a second review

- Keep the macOS save panel free-floating: only Windows gets the window as
  its owner, since a parent makes rfd show a sheet on macOS.
- Treat the WebView2 bootstrap as failed unless the runtime is then present.
- Move the UTF-16 helper out of the single-instance module, and bind the
  kernel object names before the mutex call that GetLastError follows.
- Docs and a dead_code reason.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Add the Linux desktop app as a self-updating AppImage

A `desktop` cargo feature builds the windowed app on Linux (glibc, WebKitGTK),
leaving the static musl CLI builds untouched. build.rs sets cfg(desktop_app)
for macOS, Windows, and Linux with the feature, replacing the macOS-or-Windows
gates.

The AppImage's AppRun starts `orx app`, sharing the Windows entry point:
the webview is built into the GTK window's box (X11 and Wayland), closing
quits, a Unix socket in XDG_RUNTIME_DIR keeps one instance and focuses it,
and the login shell's PATH is adopted as on macOS.

scripts/build-linux-appimage.sh bundles WebKitGTK with pinned linuxdeploy,
its GTK plugin, and appimagetool, copying WebKit's helper processes and
rewriting libwebkit2gtk's /usr paths to ././ so they resolve inside the
image. CI builds x86_64 and aarch64 AppImages on Ubuntu 22.04 and
smoke-tests each under Xvfb; release-linux-app.yml attaches them with
linux-app.json once LINUX_APP_ENABLED is set.

The AppImage updates itself (InstallChannel::AppImage, updates/linux_app.rs):
a sha256-checked download renamed over the file, production builds only,
and Restart execs the new AppImage on the same port.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Fix the Linux app's downloads, bundled WebKit, and host environment

From review of the Linux desktop app:

- Downloads froze the app: rfd's GTK backend runs its dialog on a thread
  of its own, which waits forever on the main context tao holds. The save
  dialog is now a gtk::FileChooserDialog run on the main thread.
- The bundled WebKit helpers found their libraries only beside themselves,
  so they loaded the host's WebKit or none. They now get an RPATH to the
  image's usr/lib, and linuxdeploy no longer makes stray copies of them.
- The GTK hook's variables reached every host program orx opens. AppRun
  now saves the session's values and orx hands them back to xdg-open, the
  folder picker, error dialogs, and agents.
- The smoke test now runs after WebKitGTK is removed from the runner and
  checks that each WebKit helper runs, and loads libwebkit2gtk, from the
  image; cleanup kills the app's whole process group.
- Startup failures now reach stderr and a zenity or kdialog dialog.
- The tools and the AppImage runtime are pinned by sha256, and only the
  release job that publishes can write to the release.
- Smaller fixes: the shell probe runs after the single-instance claim and
  falls back to /bin/sh; APPDIR is canonicalized and must not be empty or
  root; relative project paths resolve against home inside the image.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Keep the session's GTK settings across Linux app restarts and terminals

Restore the host GTK variables before an update relaunches the AppImage, so
the new AppRun doesn't save the old image's values as the session's, and in
the dashboard's terminals. Share one APPDIR containment check between the
update channel, relative project paths, and the folder picker, which now keeps
a terminal `orx up`'s working directory.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Relaunch the AppImage only from inside it, and tidy after review

Pick the relaunch target with the same APPDIR check as the update channel, so
an `orx app` started from the app's terminal restarts itself. Hand the shell
probe the session's GTK settings, harden the smoke test's helper checks and
cleanup, and bring the docs up to date.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Run the AppImage detection test only on Unix

Its mount paths aren't absolute on Windows, which has no AppImage anyway.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Import the AppImage test's helper inside the Unix-only test

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Show the app window even when the dashboard never finishes loading

On a UTM VM the Linux app ran with no window: it stays hidden until the page
finishes loading, and that never happened. Show it 15 seconds after the
server is up regardless, say so on stderr, and have the smoke test fail if
that fallback fires or no window appears.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Give the Linux app its name and icon in the dock, and keep GIO off host modules

GNOME shows an unmatched window as a gear named after its class, so name the
program OpenResearch and have each launch install a desktop entry and icon
pointing at the AppImage (TryExec hides it once the file is gone).

Bundle GLib's TLS module and set GIO_MODULE_DIR to the image's, so the bundled
GLib stops loading the host's modules, built against a newer one. A second
launch now shows the window even before the page loads, so a stuck instance
can't swallow it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Tidy the Linux app after the fourth review

Write the desktop entry only from the AppImage's own orx (shared with the
update relaunch), keep a session's GIO_EXTRA_MODULES off the bundled GLib,
mark a Dock-reopened macOS window as shown so the load fallback can't reopen
it, close a race in the smoke test, and note the Fedora and openSUSE TLS gap.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Show a stalled app window, and close the app before the installer runs

If the dashboard never finishes loading, show the window 15 seconds after the
server is up and say so on stderr; a relaunch (or a Dock click on macOS) also
shows it before the page loads. The installer and uninstaller now check the
app's single-instance mutex and ask the user to close it, rather than
replacing files under a running app and skipping its shutdown path.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Keep a replaced download's original until it lands, and don't offer a launch without WebView2

Move the file a download replaces aside and restore it if the download fails
or is cancelled (WebView2's flyout can cancel), rather than deleting it up
front. The installer no longer offers to start OpenResearch when the WebView2
Runtime is still missing, since the window could not open.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 13:19:33 -07:00

3.3 KiB

Linux

The desktop app

From Releases, download OpenResearch-x86_64.AppImage (or OpenResearch-aarch64.AppImage on ARM), make it executable, and run it:

chmod +x OpenResearch-x86_64.AppImage
./OpenResearch-x86_64.AppImage

The dashboard opens in its own window. Closing the window quits OpenResearch, and starting it again while it runs brings the window back. Each start adds OpenResearch to your applications, so the dock and app grid show its name and icon: ~/.local/share/applications/openresearch.desktop, pointing at wherever the AppImage now is, and its icon under ~/.local/share/icons. The entry hides itself once the AppImage is deleted; remove those two files to drop it entirely. Keep the AppImage somewhere you can write to, such as ~/Applications: it updates itself by replacing that file, and Restart in the dashboard relaunches the new version.

The AppImage bundles WebKitGTK, so it needs nothing installed beyond glibc 2.35 or newer (Ubuntu 22.04, Debian 12, Fedora 36, and later) and fusermount to mount itself. Where FUSE isn't available, run it with --appimage-extract-and-run, or set APPIMAGE_EXTRACT_AND_RUN=1.

The app starts orx app from inside the image. Agents find orx because its folder leads their PATH, and because a desktop launcher doesn't read your shell's startup files, the app asks your login shell for PATH and the other variables orx cares about when it starts. The browser, file manager, editors, terminals, and agents it opens get your session's GTK settings back, not the AppImage's. It uses port 4792, or a free port if something else holds it.

The CLI

The release's openresearch-cli-<arch>-unknown-linux-musl archives and the shell installer are static binaries with no GUI; orx up opens the dashboard in your browser.

Known gaps

Audio and video previews WebKitGTK plays media through GStreamer, which the AppImage doesn't bundle.
Error dialogs If the app can't start, it says why through zenity or kdialog, and on stderr; without either, run the AppImage from a terminal to see it.
Wayland The bundled GTK runs under XWayland, as linuxdeploy's GTK plugin sets it up to.
HTTPS in the window The bundled TLS reads certificates from /etc/ssl/certs/ca-certificates.crt, as on Debian, Ubuntu, and Arch; on Fedora and openSUSE, HTTPS pages inside the window fail. The dashboard itself is local HTTP.

Releasing the app

release-linux-app.yml builds both AppImages from the release's commit, attaches them, and writes linux-app.json, once the repository variable LINUX_APP_ENABLED is true. Like the macOS app it follows a Release run dispatched by a token (see macos/DISTRIBUTION.md); to attach the AppImages to an existing release, dispatch the workflow with its tag. CI on every pull request also builds, smoke-tests, and uploads both AppImages (openresearch-linux-<arch>-appimage). The smoke test runs after WebKitGTK is removed from the runner, so it fails if the image falls back to a host copy.

To build one locally on Ubuntu 22.04 or newer:

sudo apt-get install libwebkit2gtk-4.1-dev libgtk-3-dev librsvg2-dev file patchelf
cargo build --release --features desktop
scripts/build-linux-appimage.sh target/release/orx dist