Skip to content

Platform Support

This page answers one question: will this work on my operating system? Which runtime to build for is a separate question, and Runtimes has it. The two do not line up: a package can be fully cross-runtime and still be Linux-only, because the native library underneath it is.

LinuxmacOSWindows
Apps on GJS (--app gjs)supportedpartial, see belownot available (no GJS host)
Apps on Node/Bun/Deno (--app node, via @gjsify/node-gi)supportedsupportedsupported
Browser builds (--app browser)supportedsupportedsupported
Web views (@gjsify/iframe)supportedsupportedsupported, with limits
The CLI on a gjs host (no Node, Bun or Deno on the machine)supportedengines ship, not exercised in CInot available (no GJS host)
The CLI on Node, Bun or Denosupported, all three exercised in CIsupported, Node exercised in CIsupported, Node exercised in CI

The last two rows are the same gjsify binary, asked which runtime is executing it. You pick by what the machine already has: the gjs route installs with a curl line and no package manager, the other three install from npm, Bun or Deno. Off Linux, the CI leg that drives the toolchain end to end is the Node one (cli-cross-platform.yml).

Browser builds carry no native code at all, so they are portable by construction. The rows that name a gjs process are where the operating system matters.

The web-view row is a capability rather than a build target, and it reaches all three because each operating system supplies a different engine under one namespace. What “with limits” means on Windows is spelled out below and in Web Views.

There is no GJS on Windows, so anything ending in a gjs process is out: --app gjs bundles, test:gjs, and the examples built around them. That is an upstream gap, not a missing task here: nobody ships a prebuilt libgjs for Windows, and gjs has to be built against a SpiderMonkey that Windows package managers do not provide in a form its build system takes.

Everything that does not end in a gjs process works. On Node, Bun or Deno a Windows machine runs the toolchain (gjsify install, gjsify build for --app node, --app browser and --library, gjsify check, gjsify clear, gjsify copy), and showcases run on those three:

Code
gjsify showcase # list them
gjsify showcase express-webserver # runs on node here
gjsify showcase express-webserver --runtime bun # or deno

gjsify showcase picks the runtime itself, so that middle line runs the showcase’s --app node bundle without being told to. A showcase that declares no support for the chosen runtime says so instead of crashing. Install the CLI with npm, Bun or Deno on Windows: the curl bootstrap needs a gjs binary, and there is none here.

The Windows leg CI walks is the Node one: cli-cross-platform.yml installs the published CLI, builds --app node, runs the output and drives a showcase on windows-latest. Bun and Deno install the same package and are not covered by that leg.

You do not have to supply GTK yourself. @gjsify/gtk-runtime-win32-x64 ships the GTK 4 / Adwaita closure that @gjsify/node-gi loads gi:// namespaces from, so there is no gvsbuild or system GTK to install. CI opens a real GTK window and runs the full Adwaita storybook against that bundle on windows-latest, under Node, on a runner with no gvsbuild on it — against the bundle built in that same run, which is what proves the bundle this commit would publish. A separate workflow (gtk-os-suites.yml) runs the GTK-bearing suites against the PUBLISHED tarball instead, which is the one a stranger downloads; the two have been different bytes before.

Web views reach Windows too. @gjsify/webview2-native supplies a WebKit-6.0 namespace backed by Microsoft’s WebView2, so the code that embeds a page on Linux embeds one here unchanged. Three things decide whether you can build on it. The view is an overlay the OS composites on top of your window, so no ancestor can clip or scroll it. The WebView2 Evergreen runtime has to be present on the end user’s machine, which your installer has to declare. And the view has been proven headlessly, not re-parented under a real application window. Web Views has each of those in full.

Two host settings are worth getting right before you start, because both fail in ways that do not name themselves:

  • Long paths. Set HKLM\SYSTEM\CurrentControlSet\Control\FileSystem\LongPathsEnabled to 1 (needs elevation) and keep your checkout short, C:\src\… rather than a deep home directory. Nested node_modules runs past MAX_PATH quickly.
  • Line endings. Prefer core.autocrlf=false. A default Git for Windows clone is not dirty, and .gitattributes already pins the byte-verified artifacts to LF whatever you configure.

Real file symlinks are optional: gjsify install links workspace packages with NTFS junctions, which need no privilege. Turning on Developer Mode enables the handful of tests that do want symlinks; without it they report themselves as skipped.

Every native piece gjsify ships for macOS — the prebuilt bridges, the GTK runtime bundles — needs macOS 15 (Sequoia) or newer, on Apple silicon and Intel alike. That is the oldest release Apple still ships security fixes for, and the oldest the Homebrew-built GTK runtime can reach.

--app node is fully supported: builds, conformance and a real GTK/Adwaita window are all exercised on Apple silicon, and @gjsify/gtk-runtime-darwin-arm64 ships the GTK 4 / Adwaita closure so you do not need a Homebrew GTK. Intel Macs get the same against @gjsify/gtk-runtime-darwin-x64: the same builds, the same display-free conformance, the same windowed GUI proof, each one a matrix leg beside its Apple-silicon twin rather than a reduced substitute.

Web views come with the package here. @gjsify/webkit-native puts Apple’s WebKit behind the same WebKit-6.0 namespace @gjsify/iframe uses on Linux, so nothing in your code changes and there is no WebKitGTK to hunt for. Upstream’s GTK port is Linux-only by decision. The web view itself needs nothing newer than macOS 11, so it adds no requirement beyond the macOS 15 above, and a few page-level behaviours differ (document focus, the pointer cursor); Web Views names them.

What is not proven is the CLI on a gjs host. gjsify build under GJS needs a bundler, @gjsify/rolldown-native is the one that runs there, and it does publish darwin-arm64 and darwin-x64 prebuilds, along with @gjsify/lightningcss-native and @gjsify/oxfmt-native. What is missing is a CI job that drives gjs -m install.mjs all the way to gjsify build with no Node on the machine. It may well work for you; nothing here promises it does.

The Node route is the one a macOS CI leg does walk end to end: cli-cross-platform.yml installs the published CLI, builds --app node and runs the output on macos-latest. Bun and Deno install the same package and are not covered by that leg.

A native bridge is a gjsify package that ships a compiled artifact. Each one declares the <os>-<arch> targets it promises, and CI is held to that declaration in both directions.

Package Tier darwin linux win32
arm64 x64 arm64 arm64-musl ppc64 riscv64 s390x x64 x64-musl arm64 x64
@gjsify/gamepad-native 1 darwin-arm64: declared, a CI job targets it, artifact committed darwin-x64: declared, a CI job targets it, artifact committed linux-arm64: declared, a CI job targets it, artifact committed linux-arm64-musl: unsupported linux-ppc64: unsupported linux-riscv64: unsupported linux-s390x: unsupported linux-x64: declared, a CI job targets it, artifact committed linux-x64-musl: unsupported win32-arm64: unsupported win32-x64: declared, a CI job targets it, artifact committed
@gjsify/http-soup-bridge 1 darwin-arm64: declared, a CI job targets it, artifact committed darwin-x64: declared, a CI job targets it, artifact committed linux-arm64: declared, a CI job targets it, artifact committed linux-arm64-musl: unsupported linux-ppc64: declared, a CI job targets it, artifact committed linux-riscv64: declared, a CI job targets it, artifact committed linux-s390x: declared, a CI job targets it, artifact committed linux-x64: declared, a CI job targets it, artifact committed linux-x64-musl: unsupported win32-arm64: unsupported win32-x64: unsupported
@gjsify/http2-native 1 darwin-arm64: declared, a CI job targets it, artifact committed darwin-x64: declared, a CI job targets it, artifact committed linux-arm64: declared, a CI job targets it, artifact committed linux-arm64-musl: unsupported linux-ppc64: declared, a CI job targets it, artifact committed linux-riscv64: declared, a CI job targets it, artifact committed linux-s390x: declared, a CI job targets it, artifact committed linux-x64: declared, a CI job targets it, artifact committed linux-x64-musl: unsupported win32-arm64: unsupported win32-x64: unsupported
@gjsify/lightningcss-native 1 darwin-arm64: declared, a CI job targets it, artifact committed darwin-x64: declared, a CI job targets it, artifact committed linux-arm64: declared, a CI job targets it, artifact committed linux-arm64-musl: declared, a CI job targets it, artifact committed linux-ppc64: declared, a CI job targets it, artifact committed linux-riscv64: declared, a CI job targets it, artifact committed linux-s390x: declared, a CI job targets it, artifact committed linux-x64: declared, a CI job targets it, artifact committed linux-x64-musl: declared, a CI job targets it, artifact committed win32-arm64: unsupported win32-x64: unsupported
@gjsify/napi 3 darwin-arm64: declared, a CI job targets it, artifact NOT committed here darwin-x64: unsupported linux-arm64: unsupported linux-arm64-musl: unsupported linux-ppc64: unsupported linux-riscv64: unsupported linux-s390x: unsupported linux-x64: declared, a CI job targets it, artifact NOT committed here linux-x64-musl: unsupported win32-arm64: unsupported win32-x64: unsupported
@gjsify/node-gi 2 darwin-arm64: declared, a CI job targets it, artifact NOT committed here darwin-x64: declared, a CI job targets it, artifact NOT committed here linux-arm64: declared, a CI job targets it, artifact NOT committed here linux-arm64-musl: unsupported linux-ppc64: unsupported linux-riscv64: unsupported linux-s390x: unsupported linux-x64: declared, a CI job targets it, artifact NOT committed here linux-x64-musl: unsupported win32-arm64: declared, a CI job targets it, artifact NOT committed here win32-x64: declared, a CI job targets it, artifact NOT committed here
@gjsify/oxfmt-native 1 darwin-arm64: declared, a CI job targets it, artifact committed darwin-x64: declared, a CI job targets it, artifact committed linux-arm64: declared, a CI job targets it, artifact committed linux-arm64-musl: unsupported linux-ppc64: unsupported linux-riscv64: unsupported linux-s390x: unsupported linux-x64: declared, a CI job targets it, artifact committed linux-x64-musl: unsupported win32-arm64: unsupported win32-x64: unsupported
@gjsify/rolldown-native 1 darwin-arm64: declared, a CI job targets it, artifact committed darwin-x64: declared, a CI job targets it, artifact committed linux-arm64: declared, a CI job targets it, artifact committed linux-arm64-musl: unsupported linux-ppc64: unsupported linux-riscv64: unsupported linux-s390x: unsupported linux-x64: declared, a CI job targets it, artifact committed linux-x64-musl: unsupported win32-arm64: unsupported win32-x64: unsupported
@gjsify/sab-native 1 darwin-arm64: declared, a CI job targets it, artifact committed darwin-x64: declared, a CI job targets it, artifact committed linux-arm64: declared, a CI job targets it, artifact committed linux-arm64-musl: declared, a CI job targets it, artifact committed linux-ppc64: declared, a CI job targets it, artifact committed linux-riscv64: declared, a CI job targets it, artifact committed linux-s390x: declared, a CI job targets it, artifact committed linux-x64: declared, a CI job targets it, artifact committed linux-x64-musl: declared, a CI job targets it, artifact committed win32-arm64: unsupported win32-x64: unsupported
@gjsify/terminal-native 1 darwin-arm64: declared, a CI job targets it, artifact committed darwin-x64: declared, a CI job targets it, artifact committed linux-arm64: declared, a CI job targets it, artifact committed linux-arm64-musl: unsupported linux-ppc64: declared, a CI job targets it, artifact committed linux-riscv64: declared, a CI job targets it, artifact committed linux-s390x: declared, a CI job targets it, artifact committed linux-x64: declared, a CI job targets it, artifact committed linux-x64-musl: unsupported win32-arm64: unsupported win32-x64: unsupported
@gjsify/tls-native 1 darwin-arm64: declared, a CI job targets it, artifact committed darwin-x64: declared, a CI job targets it, artifact committed linux-arm64: declared, a CI job targets it, artifact committed linux-arm64-musl: unsupported linux-ppc64: declared, a CI job targets it, artifact committed linux-riscv64: declared, a CI job targets it, artifact committed linux-s390x: declared, a CI job targets it, artifact committed linux-x64: declared, a CI job targets it, artifact committed linux-x64-musl: unsupported win32-arm64: unsupported win32-x64: unsupported
@gjsify/webgl 1 darwin-arm64: declared, a CI job targets it, artifact committed darwin-x64: declared, a CI job targets it, artifact committed linux-arm64: declared, a CI job targets it, artifact committed linux-arm64-musl: unsupported linux-ppc64: declared, a CI job targets it, artifact committed linux-riscv64: declared, a CI job targets it, artifact committed linux-s390x: declared, a CI job targets it, artifact committed linux-x64: declared, a CI job targets it, artifact committed linux-x64-musl: unsupported win32-arm64: unsupported win32-x64: declared, a CI job targets it, artifact committed
@gjsify/webkit-native 1 darwin-arm64: declared, a CI job targets it, artifact committed darwin-x64: declared, a CI job targets it, artifact committed linux-arm64: unsupported linux-arm64-musl: unsupported linux-ppc64: unsupported linux-riscv64: unsupported linux-s390x: unsupported linux-x64: unsupported linux-x64-musl: unsupported win32-arm64: unsupported win32-x64: unsupported
@gjsify/webrtc-native 1 darwin-arm64: declared, a CI job targets it, artifact committed darwin-x64: declared, a CI job targets it, artifact committed linux-arm64: declared, a CI job targets it, artifact committed linux-arm64-musl: unsupported linux-ppc64: declared, a CI job targets it, artifact committed linux-riscv64: declared, a CI job targets it, artifact committed linux-s390x: declared, a CI job targets it, artifact committed linux-x64: declared, a CI job targets it, artifact committed linux-x64-musl: unsupported win32-arm64: unsupported win32-x64: unsupported
@gjsify/webview2-native 1 darwin-arm64: unsupported darwin-x64: unsupported linux-arm64: unsupported linux-arm64-musl: unsupported linux-ppc64: unsupported linux-riscv64: unsupported linux-s390x: unsupported linux-x64: unsupported linux-x64-musl: unsupported win32-arm64: unsupported win32-x64: declared, a CI job targets it, artifact committed
  • declared, a CI job targets it, artifact committed
  • declared, a CI job targets it, artifact NOT committed here
  • committed artifact, no CI job targets it
  • declared, no CI job targets it
  • produced, undeclared
  • unsupported

Read ✓ as “you can install this and it will have a binary for your platform”. ○ means the binary is built and published to npm but not committed to the gjsify repository, which matters only if you are building gjsify from a clone. · means that platform is not supported at all. The marks come from the same rule the CI gate runs, so this table and the audit cannot disagree; the Internals page explains what each mark does and does not assert.

Column names are real directory names, spelled the way a running process computes them about itself (${process.platform}-${process.arch}), so the declaration, the committed prebuild, the CI job and the loader all use the same string.

Audit the matrix from a gjsify checkout
Terminal
gjsify run --node-script scripts/audit-runtimes.mjs --platforms # human-readable
gjsify run --node-script scripts/audit-runtimes.mjs --platforms --markdown # the same marks, as markdown
gjsify run --node-script scripts/audit-runtimes.mjs --check # the CI gate
Terminal
node scripts/audit-runtimes.mjs --platforms # human-readable
node scripts/audit-runtimes.mjs --platforms --markdown # the same marks, as markdown
node scripts/audit-runtimes.mjs --check # the CI gate
Terminal
bun scripts/audit-runtimes.mjs --platforms # human-readable
bun scripts/audit-runtimes.mjs --platforms --markdown # the same marks, as markdown
bun scripts/audit-runtimes.mjs --check # the CI gate
Terminal
deno run -A scripts/audit-runtimes.mjs --platforms # human-readable
deno run -A scripts/audit-runtimes.mjs --platforms --markdown # the same marks, as markdown
deno run -A scripts/audit-runtimes.mjs --check # the CI gate

Every native bridge is optional at runtime. Each one is loaded through a guarded probe, so a missing prebuild degrades the feature rather than crashing your app. What you lose:

bridgewithout it
@gjsify/http-soup-bridge@gjsify/http’s server loses its GC-safe Soup message wrapper
@gjsify/http2-nativeno raw h2c; createServer() stays HTTP/1.1
@gjsify/tls-nativeno OCSP parsing, no TLS session resumption or channel binding
@gjsify/terminal-nativeisatty, window size and raw mode fall back to env heuristics
@gjsify/sab-nativeno cross-process SharedBuffer; the rest of worker_threads is unaffected
@gjsify/webgl, @gjsify/webrtc-nativeWebGL and WebRTC unavailable

@gjsify/sab-native runs on Linux and on macOS 14.4 or newer, with a separate backend per OS behind one contract: memfd_create and futex on Linux, shm_open and os_sync_wait_on_address on macOS. Windows is out of scope, because Windows has no cross-process address-keyed wait. The reasoning is in ADR 0013.

  • Runtimes: GJS, Node.js, Bun, Deno and the browser
  • Web Views: one web-view API, three engines, and the Windows caveats
  • Install & Update: getting the CLI onto each platform
  • How It Works: what the platform checks actually prove