openclaw/docs/platforms/linux.md
RoboClaw bb2d479926
feat(browser): unify local Chrome setup across desktop and terminal (#152057)
* feat(browser): unify local Chrome setup across desktop and terminal

Co-authored-by: fuller-stack-dev <263060202+fuller-stack-dev@users.noreply.github.com>

* feat(browser): unify local Chrome setup across desktop and terminal

OpenClaw-Publication: d19e865e-b5a0-4c70-8876-c1662f6e7ef2

Co-authored-by: fuller-stack-dev <263060202+fuller-stack-dev@users.noreply.github.com>

* chore(linux): format Chrome setup fixture

Co-authored-by: fuller-stack-dev <263060202+fuller-stack-dev@users.noreply.github.com>

* feat(browser): keep desktop Chrome setup local and preserve pairing

Delegate Windows registration to the shared native management owner, preserve
released native bridge compatibility and saved launcher profiles, and integrate
serialized desktop setup through isolated local runtimes.

Co-authored-by: fuller-stack-dev <263060202+fuller-stack-dev@users.noreply.github.com>
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>

* fix(browser): repair native setup CI contracts

Keep Windows installer dependencies acyclic, validate Unicode within the
package library target, and remove unused private exports. Require all
eight packaged native-host proof cases and update lazy CLI inventory.
Apply native Swift formatter diagnostics without changing behavior.

Co-authored-by: fuller-stack-dev <263060202+fuller-stack-dev@users.noreply.github.com>
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>

* test(cli): account for plugin-owned browser extension catalog

Keep the core-only registration invariant aligned with the Browser plugin
owner already exercised by its lazy registration tests.

Co-authored-by: fuller-stack-dev <263060202+fuller-stack-dev@users.noreply.github.com>
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>

* test(macos): retain released Chrome bridge request expectation

Align the native bridge test with the shipped contract1 request retained
by the canonical setup owner, and reject extra legacy payload fields.

Co-authored-by: fuller-stack-dev <263060202+fuller-stack-dev@users.noreply.github.com>
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>

* test(tui): give command handler harness a unique export

Rename the shared TUI test helper and both consumers to avoid the
Gateway placement harness export collision. No alias or guard waiver.

Co-authored-by: fuller-stack-dev <263060202+fuller-stack-dev@users.noreply.github.com>
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>

* chore(sdk): allow canonical browser config path resolver

Apply the approved single public-export and callable allowance for
resolveConfigPath. Preserve canonical pre-config path ownership and
all other SDK surface checks.

Co-authored-by: fuller-stack-dev <263060202+fuller-stack-dev@users.noreply.github.com>
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>

* fix(browser): preserve desktop setup selection and supported actions

Keep native automatic setup selector-free and resolve saved local browser
selection through the canonical setup owner before installation. Respect
Mac action advertisements and the released legacy install projection in
the Apps card, and document the public config-path resolver contract.

Co-authored-by: fuller-stack-dev <263060202+fuller-stack-dev@users.noreply.github.com>
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>

* fix(auth): retry model selection after concurrent credential refresh

Adopt upstream PR #152426, commit 32298b10f6, without changing its five source files.

Co-authored-by: fuller-stack-dev <263060202+fuller-stack-dev@users.noreply.github.com>
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>

* test: preserve native setup selection and await dashboard document

Match the selector-free native CLI arguments exactly and preserve a saved work-profile result. Wait through the existing document-readiness owner only at the quota test browser-proof boundary, after auth assertions.

Co-authored-by: fuller-stack-dev <263060202+fuller-stack-dev@users.noreply.github.com>
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>

* fix(ui): avoid preloading already imported modules

Remove exact direct static JavaScript imports from lazy preload tables using the emitted build graph. Preserve HTML, lazy-only JavaScript, CSS, and locale hints. Source-exact CI merge reproduction drops startup gzip from 363283 to 362968 bytes without changing budgets. Add a real emitted-bundle regression.

Apply rustfmt layout to the native Chrome selector-free expected arguments.

Co-authored-by: fuller-stack-dev <263060202+fuller-stack-dev@users.noreply.github.com>
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>

* fix: preserve Chrome profiles and attachment follow-up branch binding

Let the TUI canonical setup controller retain its saved browser profile and project only a bounded returned name. Align both native first-run fixture expectations with selector-free setup.

Join pending chat history before the composer task handoff can expose an admitted attachment to restored-outbox delivery. Preserve idempotency, attachment custody, restored delivery semantics, and all existing assertions and timeouts. Add a deterministic regression reproduced on the exact failed CI merge and its main parent.

Co-authored-by: fuller-stack-dev <263060202+fuller-stack-dev@users.noreply.github.com>
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>

* test(state): adopt canonical worker-custody fixture repair

Adopt src/plugin-state/plugin-state-worker.test.ts byte-for-byte from upstream bfec65a2a0 (#152456).

The former fixture held its late competing owner until after awaiting off-thread acquisition. Preserve that overlap, assert continued host authority checks and noncompletion, release custody, then assert the original result and persisted state. No production locking, guard, deadline or outcome assertion is relaxed.

Both prior failures reproduced on the exact CI main parent with independently installed frozen dependencies and Node 24.19.0. All 12 repaired file tests, selected state-logging types, scoped typed lint and fresh P0-P2 review passed.

Co-authored-by: fuller-stack-dev <263060202+fuller-stack-dev@users.noreply.github.com>
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>

* fix(browser): retain saved Windows setup profiles

Recover configured extension profiles through bounded serial read-only C# inspection. Select only independently validated current matching descriptors, confirm the selected generation before effects, and leave the single mutation under the existing C# owner. Preserve POSIX behavior, existing manual relay verification and explicit same-profile repair.

Missing descriptors, runtime/origin drift and unknown or changing observations fail closed without automatic mutation. Keep raw management facts private and populate the existing browserProfile field only from validated binding metadata. No ABI, schema, SDK, configuration flag or registry/activation owner change.

29 actual CLI/controller/Windows-adapter boundary cases plus sibling coverage: 94 tests pass. Canonical changed checks, full production build and fresh independent P0-P2 review passed. Actual C# native proof remains separately coordinated.

Co-authored-by: fuller-stack-dev <263060202+fuller-stack-dev@users.noreply.github.com>
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>

* fix(browser): preserve saved profiles after POSIX bundle relocation

Separate validated native registration ownership from supported origin-migration readiness. Recover the profile only after full private manifest and exact launcher validation; preserve the existing one-slot migration rule and all unsupported-origin, ACL and foreign-host refusals. Fail closed before selector-free installation when the saved selection cannot be proved.

Extract the unchanged shared origin helpers into a cohesive sibling to satisfy the existing line-cap guard without waivers. Windows admission, ABI and selector behavior remain unchanged.

Actual Linux/Darwin CLI-to-filesystem relocation regressions: 18 failures on original production, all 22 cases repaired. Preserve the private relay key and inode, config, Chrome preferences, work relay19444 and explicit-profile intent. 137 focused tests, eight real POSIX native-host E2E cases, canonical changed checks, full production build and fresh P0-P2 review passed.

Co-authored-by: fuller-stack-dev <263060202+fuller-stack-dev@users.noreply.github.com>
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>

* fix(ui): retain input handoff through the shared outbox owner

Remove the superseded pending-history no-yield workaround after main introduced foreground submission custody in the shared outbox owner. Restore chat-submit-guard.ts exactly to pinned main cc7 rather than retaining competing timing policies. Keep passive drains fenced while the input task yields.

Preserve the retained history regression with explicit MessageChannel admission, no passive send before resume, and the same terminal leaf, idempotency key, attachment bytes and exactly-once assertions after completion. Original composed source fails all five focused cases; the repair passes 67 handoff/attachment cases and 20 real Chromium cases in the canonical secretless network-none runner. Canonical checks, UI build/performance and fresh P0-P2 review pass. No assertion, timeout, origin or proxy-policy weakening.

Browser POSIX/Windows repairs remain byte-identical to accepted255f. The failed e40e CI receipts remain preserved; fresh exact-head CI and parent handoff are still required.

Co-authored-by: fuller-stack-dev <263060202+fuller-stack-dev@users.noreply.github.com>
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>

* fix(tasks): preserve reads across native event finalization

Hand joined event publication to its exact native successor after the native
flow and observer publication frame completes. Keep worker settlement and
cleanup, reversible claim transfer, current-authority and ABA checks, and
post-commit delivery in their existing owners without replaying writes.

Cover pre-result and readback finalization, native and reentrant successor
chains, rollback, failed publication, delivery, and terminal activity cleanup.

Co-authored-by: fuller-stack-dev <263060202+fuller-stack-dev@users.noreply.github.com>
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>

* test(ui): retain rail baseline geometry on readiness failure

Keep the exact existing readiness predicate, fixtures, case inventory, assertion and timeout. When the predicate is false, retain synthetic marker identity and numeric geometry so hosted CI can distinguish scroll, visibility and viewport failures.

This is diagnostic evidence, not a repair or waiver of the unresolved rail failure. Local rootless browser infrastructure is unavailable; the existing hosted CI lane will verify the reviewed task-publication repair and collect meaningful rail evidence. Canonical changed checks and P0-P2 diagnostic review pass.

Co-authored-by: fuller-stack-dev <263060202+fuller-stack-dev@users.noreply.github.com>
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>

* docs(linux): describe saved-profile Chrome setup selection

Match the selector-free adapter argument vector and its regression test. Address the fresh P3 review finding without changing runtime behavior. Markdown syntax and diff checks pass; the generic formatter excludes this subtree.

Co-authored-by: fuller-stack-dev <263060202+fuller-stack-dev@users.noreply.github.com>
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>

* test(transcripts): join configured startup before cleanup faults

Observe and await the real startTranscripts promise through a narrow call-through spy while retaining the configured service entry point and real SQLite/provider work. Bind the await to the existing test lifetime instead of charging startup to the subsequent short active-map poll.

Gate provider return after persisted utterance to prove readiness does not settle early; retain both missing/unreadable row injections and all cleanup, private-source, lifecycle-token and summary assertions. Cover real startup rejection explicitly. No production change, timeout increase, retries or broad module/storage mocks.

The deterministic ordering boundary fails with the old fire-and-forget readiness and passes with the real promise join. Final 39 tests across 3 files, canonical changed checks and full-owner P0-P2 review pass. This does not recover whether the historical CI startup was late or rejected.

Co-authored-by: fuller-stack-dev <263060202+fuller-stack-dev@users.noreply.github.com>
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>

* fix(browser): preserve automatic desktop status inspection

Restore read-only Device-page inspection for current and released Mac bridges while keeping installation and verification explicit. Preserve the native filesystem prerequisite proof, split installer repair tests within the existing line cap, and remove the superseded constant export.

* fix(browser): preserve registered setup configuration

Require canonical setup to match an owned launcher's effective state and
config selection before installation or relay access. Preserve equivalent
implicit/explicit default selections and the saved launch context. Recheck
automatic profile selection before effects and the current manifest before
publication through the existing registration owner. Keep manual install
and relocation repair contracts unchanged.

Cover mismatched configs, legacy selectors, equivalent defaults, selection
drift, and actual bootstrap after refused setup. Restore the missing Command
import in the existing Unix-only companion CLI test.

Focused tests, types, lint, fresh review, clean package build and sealed Mac
ARM64 runtime proof pass. The separate historical clock-jump CI failure has
bounded replay evidence and remains documented without a speculative fix.

Co-authored-by: fuller-stack-dev <263060202+fuller-stack-dev@users.noreply.github.com>

* fix(browser): keep setup registration types acyclic

Move the private registration status contract beside its context policy and
point both consumers at that owner. Remove the publication-module back-edge
without keeping an unused compatibility export.

The full architecture gate, extension production/test types, typed lint and
fresh independent review pass. Node's transformed JavaScript is byte-identical
for all three affected modules, so the existing runtime proof remains valid.

Co-authored-by: fuller-stack-dev <263060202+fuller-stack-dev@users.noreply.github.com>

* test(linux): handle Chrome setup in desktop sharing fixture

Recognize the exact automatic Chrome setup invocation and require its native
no-respawn flag. Keep unknown-command rejection, selected-auth validation,
process-group ownership and joined teardown assertions unchanged.

The original fixture reproduces the CI rejection against the real Linux app.
The repaired fixture passes all nine checks against that same binary, with
five Chrome setup calls and five node starts and joined stops. Fresh review
is clean; production app behavior is unchanged.

Co-authored-by: fuller-stack-dev <263060202+fuller-stack-dev@users.noreply.github.com>

---------

Co-authored-by: fuller-stack-dev <263060202+fuller-stack-dev@users.noreply.github.com>
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-09-22 06:28:29 -07:00

30 KiB
Raw Blame History

summary read_when title
Linux support + companion app status
Looking for Linux companion app status
Enabling camera, location, or notifications on a Linux node host
Planning platform coverage or contributions
Debugging Linux OOM kills or exit 137 on a VPS or container
Linux app

The Gateway is fully supported on Linux. Node is the primary, default, and recommended runtime; Bun 1.4+ builds with WAL-reset-safe node:sqlite can run OpenClaw as an explicit opt-in. Use pnpm rather than Bun for dependency installation.

Desktop companion

The OpenClaw Linux companion is a Tauri desktop app for local and remote Gateways. It:

  • walks new users through choosing a local Gateway, a discovered remote Gateway, a manually entered Gateway URL, or an SSH tunnel
  • installs the OpenClaw CLI and Node in a private managed runtime when local setup needs them, rather than requiring a global CLI install; release builds install the stable channel automatically, while development builds ask for the channel first
  • attaches to a healthy Gateway before attempting service changes
  • delegates install, start, stop, and restart operations to the CLI-managed systemd user service
  • discovers nearby Bonjour Gateways and opens each Control UI in a route-scoped window, so several Gateway dashboards can stay connected and be used simultaneously
  • opens the Gateway-served Control UI with its resolved authentication URL
  • opens Model Setup for an unconfigured local or remote Gateway, discovers available AI access, and waits for your explicit action before selecting, testing, installing, or saving a provider
  • continues into guided onboarding after connecting a new model; onboarding can import detected Claude Code, Codex, or Hermes memories into the agent workspace (the same import stays available later under Settings → Import Memory)
  • remains available from the system tray when its window is closed

The window controls share the dashboard's top row. Drag empty header space or a session title to move the window, and double-click to maximize or restore it. The thin strip below the top resize edge also moves the window. Minimize, maximize/restore, and close sit at the top right; the window edges remain resizable. Closing the main window leaves OpenClaw available in the system tray. When connecting to an older Gateway whose dashboard does not support this layout, the companion keeps the system title bar. Update the Gateway to enable the unified window controls.

The local startup, setup, recovery, Gateway manager, and Quick Chat screens share light and dark styling and follow system appearance changes while open. Connection drafts, credential visibility, and Quick Chat replies stay intact. The connected dashboard retains its own web UI appearance setting.

Remote setup and Connection Settings use one Authentication choice for token or password. Show credential reveals the entered value; switching types clears the draft and masks the new field. Press Enter or Connect to Gateway to connect. In Connection Settings, blank credentials reuse the saved credentials for the same endpoint.

Chrome extension setup

The app prepares the local Chrome native helper at startup and after CLI installation. Release builds reuse a matching CLI or install a version-matched browser runtime under their own app-data directory. This download does not create, probe, refresh, or restart a Gateway service, replace its runtime, or change the selected remote connection. It requires an internet connection.

Choose Set Up Chrome Extension… in the tray to retry setup and open the official Chrome Web Store listing after native registration succeeds. Google Chrome on Linux still requires Add to Chrome in the Store; the app does not use enterprise force-install policies or reopen the Store at every startup. Once enabled, supported host-local setups pair automatically without a copied credential. A remote-only desktop connection still needs a browser node on this computer to expose its tabs to the remote Gateway.

Development builds use an existing local CLI rather than downloading an unrelated stable runtime. The Windows Tauri test build does not provide this runtime installer. See Chrome extension for approval, disconnection, and manual recovery.

Desktop compatibility

Published AMD64 AppImages are built on Ubuntu 22.04 and require glibc 2.35 or newer plus a libstdc++ that provides GLIBCXX_3.4.30. Ubuntu 22.04 and Debian 12 meet that ABI floor. RHEL 9 and Rocky Linux 9 ship glibc 2.34, so they cannot run the published AppImage. Extracting the AppImage does not bypass this requirement.

.deb installs stay owned by the system package manager; installing the download does not add an APT repository. AppImages use the signed in-app updater.

Global shortcuts are available on X11. On Wayland, use the tray's Quick Chat entry when your desktop provides a tray host; global shortcuts are unavailable. Tray access is a shortcut fallback, not a native Wayland compatibility guarantee.

The shell does not grant microphone capture to its embedded WebKitGTK WebView, so getUserMedia is expected to fail there. Open the Gateway's Control UI in a regular browser for Talk mode.

The desktop connects as a Gateway operator and uses the local CLI to share this computer's desktop with its Primary Gateway. Its app-owned node exposes desktop streaming only. Other device commands belong to the CLI node host and its Linux Node plugin.

The native macOS app and Windows Hub are separate applications, not this shell's opt-in macOS and Windows Tauri test bundles. See their platform pages for requirements and capabilities.

Gateway selection

Open Gateways → Manage Gateways… from the native app or tray menu to save a direct URL or SSH connection. Choose Add Gateway or Edit to open the connection form; Back to Gateways returns to the saved list and discards unsaved changes. Under Authentication, choose token or password and enter a credential only if needed. Saved credentials stay hidden; leave the field blank to keep them for the same connection. Switching authentication types clears the credential you have entered. SSH certificate pins are under Advanced connection settings.

The dashboard's profile menu switches only its current window; Control-click opens an additional window. Choosing a Gateway from the native menu focuses its existing window without reloading it, while Open … in New Window creates an independent one.

The Primary Gateway continues to own Quick Chat and the desktop connection. Changing it requires the separate Set as Primary confirmation on a saved token-authenticated connection. Other Gateway windows retain their own targets. The companion remembers successful explicit selections, returns to Primary when that saved connection is removed, and keeps credentials in the operating system's credential store. Linux requires an unlocked Secret Service, such as GNOME Keyring or KWallet's Secret Service support.

An unavailable credential store shows a dismissible notice without blocking the dashboard. Saved connections remain intact; use Manage Gateways… → Try again after resolving the reported credential-store problem.

When a saved Gateway fails to load, the same window returns to its local connection editor. Correcting the endpoint updates the remembered selection only after the new dashboard loads successfully.

The macOS Tauri build is named OpenClaw-Tauri and keeps its saved connections separate from the native OpenClaw app.

Desktop sharing

Open Settings → This computer → Capabilities → Desktop sharing to change the setting. The macOS Tauri build labels this section This Mac. Sharing starts enabled; an existing desktop.host.enabled: false stays off until you explicitly enable it in the app. Your choice persists across app restarts and is independent of Keep computer awake.

Sharing requires a local OpenClaw CLI, including when your Gateway is remote, and an authenticated local VNC server. On macOS, enable Screen Sharing in System Settings. Approve the computer's desktop capability on the Primary Gateway when requested, then open its desktop from Systems. See paired node desktops for authentication, pairing, and upgrade behavior.

The status row shows whether the app's desktop process is running or needs attention. Pairing approval and the local VNC server must also be ready before the desktop can open. Missing CLI or invalid configuration errors appear here. Turning sharing off, changing Primary Gateway, or quitting the app stops the old desktop connection. Closing the window to the tray keeps sharing active.

First-run setup

Choose Get started on the welcome screen, then choose where your assistant should live:

  • On this computer installs any missing local prerequisites and starts the Gateway as a systemd user service.
  • On another computer connects to an existing Gateway. Select a discovered Gateway, enter its address under Gateway URL, or choose SSH tunnel and enter an SSH target such as user@gateway-host. The Gateway port defaults to 18789.

If the remote Gateway requires authentication, expand Gateway authentication and enter its token or password. Use one credential type, matching the remote Gateway's configuration. Remote setup does not install or start a local Gateway service; the remote host owns its model, provider credentials, and agent state.

Use HTTPS or wss:// for public direct connections. Plain HTTP or ws:// should be limited to loopback, trusted private networks, and Tailnet hosts. When the saved configuration includes gateway.remote.tlsFingerprint, select SSH tunnel instead of a direct connection. The embedded browser cannot enforce a certificate pin, so the app rejects direct connections before loading the remote dashboard or exposing its credentials. Saved remote token and password values can use environment- or file-backed SecretRefs; exec and shared-store references must be resolved on their owning Gateway host. SSH uses your existing OpenSSH authentication and host-key verification. See Remote access for secure Gateway configuration.

After the connection succeeds, Model Setup discovers AI access available to the selected Gateway and shows it as a choice. Discovery does not import or copy an account. On a fresh visit, the companion does not select, test, install, or save a provider until you choose its action. Provider sign-in or API-key entry is offered when needed, and a successful model response is required before opening the agent. An already configured Gateway opens its normal dashboard after verification; newly configured access continues into guided onboarding.

If the Gateway confirms that a live model test failed before saving the model and credentials, close the error and retry or choose another connection. An uncertain error keeps replacement setup blocked because settings may already have been saved. Confirmed cancellation and requests rejected before setup started can be retried immediately.

Model Setup can resume an activation across a Gateway restart or app reopen while its temporary recovery record is valid. Recovery stays bound to the same Gateway, agent, and authentication. When the known activation target still matches the selected model, OpenClaw verifies that exact model before continuing guided onboarding rather than activating the provider again. For an unresolved result, use Verify & use selected model to explicitly verify and adopt a displayed model, or wait for the setup attempt's bounded window to end before choosing Check again. Recovery is not guaranteed after that record expires, browser storage becomes unavailable or is cleared, or the Gateway, agent, or authentication changes.

Ollama automatic discovery uses eligible models already loaded in memory, not all models installed on disk. To use an idle installed model, choose Choose connection on its Ollama card, then Local only. See Ollama.

For OpenAI, choose ChatGPT Login to use a ChatGPT or Codex subscription, or OpenAI API Key for API billing. Browser sign-in completes on the Gateway host. If that host is remote or its localhost callback cannot be reached, choose ChatGPT Device Pairing from the additional sign-in options instead; device pairing works without a localhost callback. See OpenAI and OAuth.

When the desktop app starts with a supported provider API key in its environment, the Gateway service keeps that dedicated inference credential in an owner-only environment file. Provider admin keys, GitHub tokens, and unrelated environment variables are not copied into the service.

Host sleep

Choose Keep computer awake in the native tray menu to prevent idle sleep while the desktop companion is running, including when its windows are closed. The setting is off by default and remembers your choice across app restarts. Turning it off or quitting OpenClaw releases the keep-awake request. It does not change your permanent power settings or unlock the computer. If the operating system cannot honor a saved request, the menu marks the checked preference inactive and reports the error. You can still uncheck it to turn the saved preference off.

Linux uses GNOME’s session manager or an xdg-desktop-portal backend that supports idle inhibition. Depending on the desktop, this can also prevent display dimming and automatic locking; manual locking remains available. The optional macOS and Windows Tauri builds prevent system idle sleep without requesting that the display stay on.

On systems with systemd-logind, the companion prepares a suspension lease for its local Gateway before the host sleeps. After wake, it reconnects and resumes the Gateway; remote Gateway routes are left untouched. If logind or the system bus is unavailable, the sleep hook disables itself and the app continues normally.

Stable releases built from main or their matching release/YYYY.M.PATCH branch ship .deb and AppImage bundles as assets on the GitHub release for the tag, named OpenClaw-<version>-amd64.deb and OpenClaw-<version>-amd64.AppImage, with a SHA256SUMS.linux-app.txt checksum file next to them. Download the .deb and install it with sudo apt install ./OpenClaw-<version>-amd64.deb, or mark the AppImage executable and run it directly. The AppImage runtime needs FUSE 2 (sudo apt install libfuse2, or libfuse2t64 on Ubuntu 24.04+); without it, run the AppImage with APPIMAGE_EXTRACT_AND_RUN=1.

Regular stable publication requests Linux bundles automatically after the Gateway release becomes visible. Linux build, signing, and publication finish independently. While those bundles are pending, the app updater continues to offer the previous published Linux version through its original signed download.

Download only a release that contains the named Linux bundles and checksum file; a new Gateway release alone does not prove a new Linux app is available. The shipped updater still uses releases/latest/download/latest.json. Independent linux-stable publication tooling is not a client endpoint or download-link migration. That activation requires separate release approval and signed installed-client proof; see Linux companion publication.

Media codecs

The companion uses GStreamer plugins for audio and video playback. WebM/VP9, Opus, Vorbis, and WAV normally work through plugins-good. H.264/MP4, AAC, and MP3 require the libav and/or plugins-bad packages. The .deb uses the host's plugins and declares all three packages as dependencies. The AppImage bundles the GStreamer media framework and the plugins required for those formats. For a source build or when rebuilding either Linux bundle, install the packages and inspection tool explicitly:

sudo apt update && sudo apt install gstreamer1.0-libav gstreamer1.0-plugins-good \
  gstreamer1.0-plugins-bad gstreamer1.0-tools patchelf xdg-utils

The packaging script stages only that media capability set before Tauri invokes linuxdeploy. This prevents optional host plugins from adding unrelated system libraries to the AppImage dependency closure.

The packaging flow provisions Tauri's five AppImage tools into a clean, digest-pinned cache. After Tauri builds the AppImage, the finalizer re-verifies that cache, removes bundled Wayland client libraries from the retained AppDir, and rebuilds the artifact. WebKitGTK and Mesa then use one compatible host stack.

You can also build the same bundles from a source checkout:

plugins=$(mktemp -d)
cache=$(mktemp -d)
trap 'rm -rf "$plugins" "$cache"' EXIT
export XDG_CACHE_HOME="$cache"
apps/linux/scripts/stage-appimage-gstreamer.sh "$plugins"
apps/linux/scripts/tauri-appimage-tools.sh prepare
apps/linux/scripts/tauri-appimage-tools.sh verify pre-build
export LDAI_RUNTIME_FILE="$(apps/linux/scripts/tauri-appimage-tools.sh runtime-path)"
(
  cd apps/linux/src-tauri
  GSTREAMER_PLUGINS_DIR="$plugins" \
    pnpm dlx @tauri-apps/cli@2.11.4 build --bundles deb,appimage \
      --config '{"bundle":{"createUpdaterArtifacts":false,"useLocalToolsDir":false}}'
)
apps/linux/scripts/finalize-appimage.sh \
  apps/linux/src-tauri/target/release/bundle/appimage

The Linux App workflow checks affected pull requests with Rust tests, native builds, and the native inline-browser smoke; it does not build bundles for pull requests. Manual runs build and upload the .deb and AppImage as the openclaw-linux-companion workflow artifact; they do not publish a release. See apps/linux/README.md in the repository for Linux build dependencies and development commands.

Quick Chat

Ctrl+Shift+O opens a new session only in the focused dashboard. The companion does not reserve this chord globally, so other foreground apps keep their own shortcut behavior.

Open Quick Chat with Ctrl+Shift+Space or the Quick Chat tray item. The agent chip shows the configured avatar, emoji, or monogram; select it to switch agents. Messages use the selected agent's main session and honor global session scope. The native Rust client owns a persistent Ed25519 device identity. It uses the CLI handoff's shared token or password only to bootstrap pairing, then stores and prefers the Gateway-issued device token on later connections. The identity and device token live in the app config directory in a mode 0600 file; Quick Chat's WebView receives neither credentials nor the WebSocket.

When the native connection is unavailable, Quick Chat shows Gateway unreachable — retrying and disables send until reconnection. A remote device that has reached the pairing phase shows Approve this device in the dashboard (Nodes) instead, with a short device ID when the Gateway provides one. A Gateway that requires a missing shared credential shows Gateway requires a credential — open the dashboard on the gateway host; no pairing request is waiting for approval in that state. Server-provided remediation guidance replaces these fallback notices when it is more specific. For TLS Gateways, the CLI hands the app the Gateway certificate's SHA-256 fingerprint; the native client pins that certificate and reports Gateway TLS trust failed — check the certificate fingerprint separately from downtime. Gateways whose shared secret is configured through a SecretRef omit it from the CLI handoff. Existing paired installs keep working through their stored device token, but a fresh install cannot create a pending pairing request under shared-secret authentication without that bootstrap credential. Setup-code and bootstrapToken redemption need dedicated product UI and remain a follow-up; Quick Chat does not attempt either flow.

On X11, use the gear in Quick Chat to record or reset a custom shortcut. The Quick Chat shortcut tray toggle enables or disables it without disabling the plain Quick Chat tray item. Global shortcuts are not available on Wayland, so the shortcut settings are hidden and the tray item remains the entry point. After an accepted send, Quick Chat stays open and streams the selected agent's plain-text reply above one bottom composer, with your submitted message alongside the reply. Collapse the reply to keep a compact composer; expanding it restores the live text and any widget contents. You can prepare the next draft while a reply streams, then send it when the turn finishes. Return sends, Shift-Return adds a newline, and Ctrl+Enter sends and opens the dashboard. Open dashboard is also available beside the composer controls. Press Esc to dismiss the bar and its reply.

CLI and SSH alternative

The CLI remains the simplest option for a headless server or VPS. Use a manual SSH tunnel when connecting without the Linux desktop companion:

  1. Install Node 26 (recommended), or another supported release: Node 24.16+ or Node 26.1+.
  2. On npm 12 or npm 11.16+, run npm i -g openclaw@latest --allow-scripts=openclaw. On npm 11.15 and earlier, omit --allow-scripts=openclaw.
  3. openclaw onboard --install-daemon
  4. From your laptop: ssh -N -L 18789:127.0.0.1:18789 <user>@<host>
  5. Open http://127.0.0.1:18789/ and authenticate with the configured shared secret (token by default; password if gateway.auth.mode is "password").

Full server guide: Linux Server. Step-by-step VPS example: exe.dev.

Node capabilities

The bundled Linux Node plugin gives the CLI openclaw node service device capabilities without requiring the desktop app. Commands are advertised to the Gateway only when their capability is enabled and the required local tool exists.

Capability Default Requirement
Desktop notifications (system.notify) On notify-send from libnotify and a desktop notification session
Camera photos and clips (camera.*) Off FFmpeg, V4L2 camera access, and PulseAudio or PipeWire for clip audio
Location (location.get) Off GeoClue2 and its where-am-i demo

Configure the plugin in openclaw.json:

{
  plugins: {
    entries: {
      "linux-node": {
        config: {
          notify: { enabled: true },
          camera: { enabled: true },
          location: { enabled: true },
        },
      },
    },
  },
}

Restart the node service after changing these settings. Availability is determined once per process and the node advertisement is rebuilt on restart.

The Gateway approves the node's command and capability surface separately from device pairing. On first start, or after enabling more capabilities, approve the pending surface:

openclaw nodes pending
openclaw nodes approve <requestId>

A node can be connected and device-paired while its effective caps and commands remain empty until this approval completes.

Camera devices must be readable by the service user, commonly through the video group. Camera clips use the default PulseAudio or PipeWire source when includeAudio is true; microphone audio exists only as that clip track, not as a standalone command. Location requires the node-service user to be permitted by the host's GeoClue policy.

camera.snap and camera.clip also require explicit Gateway arming through gateway.nodes.commands.allow. See Camera capture and Location command for payloads, limits, and errors.

Retired Linux Canvas

The bundled Linux Canvas bridge and its desktop Canvas window have been removed. For inline widgets in the Control UI, use show_widget. The separate macOS widget panel requires a connected Mac and is render-only. These widget surfaces do not restore the former Linux Canvas bridge or its A2UI push commands.

Install

Gateway service (systemd)

On Linux hosts without a supported service manager, run the Gateway in the foreground or through your own supervisor, such as rc.d. openclaw gateway status --deep reports no supported service manager detected and identifies a remaining service unit as stale. That recorded unit does not select the status probe's configuration or port. Updates continue with a service warning; restart your manually launched Gateway after the update. An unavailable user session bus on a systemd host remains a separate service-access diagnostic.

Install with one of:

openclaw onboard --install-daemon
openclaw gateway install
openclaw configure   # select "Gateway service" when prompted

Repair or migrate an existing install:

openclaw doctor

openclaw gateway install renders a systemd user unit by default. Full service guidance, including the system-level unit variant for shared or always-on hosts, lives in the Gateway runbook.

Managed units escape literal paths automatically. In a custom unit, do not add shell quotes around WorkingDirectory= or EnvironmentFile= paths, even when they contain spaces. Use a separate EnvironmentFile= directive for each absolute path; systemd ignores relative paths. Write %% for a literal percent sign. EnvironmentFile= also accepts glob patterns, so escape literal glob characters with a backslash. Managed working-directory paths must not end in spaces or tabs: systemd 255 loses that trailing whitespace when starting the process. OpenClaw rejects those paths rather than risk using a different directory; choose a path without trailing whitespace.

Write a unit by hand only for a custom setup. Minimal user-unit example (~/.config/systemd/user/openclaw-gateway[-<profile>].service):

[Unit]
Description=OpenClaw Gateway (profile: <profile>)
After=network-online.target
Wants=network-online.target
StartLimitBurst=10
StartLimitIntervalSec=300

[Service]
ExecStart=/usr/local/bin/openclaw gateway --port 18789
Restart=always
RestartSec=5
RestartPreventExitStatus=78
TimeoutStopSec=330
TimeoutStartSec=30
SuccessExitStatus=0 143
OOMPolicy=continue
KillMode=mixed

[Install]
WantedBy=default.target

Hand-written units do not inherit the adaptive heap sizing that openclaw gateway install writes for managed Gateway services. Prefer the managed installer, or set an explicit heap limit in the custom supervisor after accounting for native-memory headroom.

TimeoutStopSec=330 covers the Gateway's five-minute cooperative drain plus teardown reserve. To inspect the current managed unit body, run systemctl --user cat openclaw-gateway.service (or systemctl --user cat openclaw-gateway-<profile>.service for a named profile).

Enable it:

systemctl --user enable --now openclaw-gateway[-<profile>].service

Memory pressure and OOM kills

On Linux, the kernel picks an OOM victim when a host, VM, or container cgroup runs out of memory. The Gateway is a poor victim because it owns long-lived sessions and channel connections, so OpenClaw biases transient child processes to be killed first when possible.

For eligible Linux child spawns, OpenClaw wraps the command in a short /bin/sh shim that attempts to raise the child's own oom_score_adj to 1000, then execs the real command. This is unprivileged: a process may always raise its own OOM score.

Covered child process surfaces:

  • Supervisor-managed command children
  • PTY shell children
  • MCP stdio server children
  • Managed local model and embedding service children
  • OpenClaw-launched browser/Chrome processes (via the plugin SDK process runtime)

Sandbox backend transports keep their prepared environment and inherited OOM score instead of receiving this wrapper. Workload resource policy belongs to the sandbox backend; ordinary host commands and PTYs retain the child-first bias.

The wrapper is Linux-only and skipped when /bin/sh is unavailable, or when the child env sets OPENCLAW_CHILD_OOM_SCORE_ADJ to 0, false, no, or off. Use this opt-out only for controlled diagnosis: it removes child-first OOM protection and makes the Gateway more likely to be selected as the victim under real memory pressure.

Managed local model and embedding services fall back to direct spawn when their effective environment defines SHELLOPTS, BASHOPTS, a BASH_FUNC_* key, or a reserved OC_INTERNAL_OOM_EXEC_{BASH_ENV,ENV,CDPATH,PS4} carrier. Exact environment fidelity and shell startup safety take precedence in these cases, so OpenClaw does not attempt to change oom_score_adj; use the verification below to check the child's effective value.

Verify a child process:

cat /proc/<child-pid>/oom_score_adj

When the write succeeds, the expected value for covered children is 1000. If /proc is unavailable or unwritable, the child still runs without the OOM bias. The Gateway process itself keeps its normal score (usually 0).

The systemd unit's OOMPolicy=continue keeps the Gateway service alive when a transient child is selected by the OOM killer instead of marking the whole unit failed and restarting all channels; the failed child/session reports its own error.

This does not replace normal memory tuning. If a VPS or container repeatedly kills children, raise the memory limit, reduce concurrency, or add stronger resource controls (systemd MemoryMax=, container memory limits).