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.
The short answer
Section titled “The short answer”| Linux | macOS | Windows | |
|---|---|---|---|
Apps on GJS (--app gjs) | supported | partial, see below | not available (no GJS host) |
Apps on Node/Bun/Deno (--app node, via @gjsify/node-gi) | supported | supported | supported |
Browser builds (--app browser) | supported | supported | supported |
Web views (@gjsify/iframe) | supported | supported | supported, with limits |
The CLI on a gjs host (no Node, Bun or Deno on the machine) | supported | engines ship, not exercised in CI | not available (no GJS host) |
| The CLI on Node, Bun or Deno | supported, all three exercised in CI | supported, Node exercised in CI | supported, 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.
Windows
Section titled “Windows”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:
gjsify showcase # list themgjsify showcase express-webserver # runs on node heregjsify showcase express-webserver --runtime bun # or denogjsify 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\LongPathsEnabledto1(needs elevation) and keep your checkout short,C:\src\…rather than a deep home directory. Nestednode_modulesruns pastMAX_PATHquickly. - Line endings. Prefer
core.autocrlf=false. A default Git for Windows clone is not dirty, and.gitattributesalready 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.
Which native bridges reach your platform
Section titled “Which native bridges reach your platform”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.
gjsify run --node-script scripts/audit-runtimes.mjs --platforms # human-readablegjsify run --node-script scripts/audit-runtimes.mjs --platforms --markdown # the same marks, as markdowngjsify run --node-script scripts/audit-runtimes.mjs --check # the CI gatenode scripts/audit-runtimes.mjs --platforms # human-readablenode scripts/audit-runtimes.mjs --platforms --markdown # the same marks, as markdownnode scripts/audit-runtimes.mjs --check # the CI gatebun scripts/audit-runtimes.mjs --platforms # human-readablebun scripts/audit-runtimes.mjs --platforms --markdown # the same marks, as markdownbun scripts/audit-runtimes.mjs --check # the CI gatedeno run -A scripts/audit-runtimes.mjs --platforms # human-readabledeno run -A scripts/audit-runtimes.mjs --platforms --markdown # the same marks, as markdowndeno run -A scripts/audit-runtimes.mjs --check # the CI gateWhat a missing bridge costs you
Section titled “What a missing bridge costs you”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:
| bridge | without it |
|---|---|
@gjsify/http-soup-bridge | @gjsify/http’s server loses its GC-safe Soup message wrapper |
@gjsify/http2-native | no raw h2c; createServer() stays HTTP/1.1 |
@gjsify/tls-native | no OCSP parsing, no TLS session resumption or channel binding |
@gjsify/terminal-native | isatty, window size and raw mode fall back to env heuristics |
@gjsify/sab-native | no cross-process SharedBuffer; the rest of worker_threads is unaffected |
@gjsify/webgl, @gjsify/webrtc-native | WebGL 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.
Related
Section titled “Related”- 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