This repository builds a Windows MSIX package containing:
- one .NET 10 NativeAOT launcher exposed through the
openclawandclawctlapp execution aliases; - a pinned, verified build of
openclaw/openclaw.
Node.js is a device prerequisite and is never downloaded or included in the MSIX.
The package is independent from the
OpenClaw Windows Node and Companion
and uses a separate OpenClaw.Gateway package identity. Both packages use the
OpenClaw Foundation publisher metadata established for OpenClaw's Windows
packages.
Both aliases activate the same packaged openclaw.exe. The launcher recovers
the alias used to start it from the native process command line and selects one
of two deliberately separate surfaces.
openclaw is a transparent launcher for the bundled OpenClaw CLI. It does not
own package-management commands. Every argument, including an empty argument
list, is forwarded unchanged to node openclaw.mjs, and the launcher returns
the exact child exit code.
Before launching, the host discovers node.exe on PATH and verifies its
version and executable architecture. It never downloads, installs, or services
Node.js.
The expanded OpenClaw application is installed read-only inside the MSIX.
After resolving Node.js, the launcher confirms that packaged
app\openclaw.mjs exists and executes it directly. It does not extract, hash,
copy, repair, or otherwise change package files at runtime.
Every OpenClaw child process runs with
OPENCLAW_SUPERVISOR_MODE=external,
OPENCLAW_SERVICE_REPAIR_POLICY=external, and
OPENCLAW_NO_AUTO_UPDATE=1. These declare external lifecycle ownership,
prevent doctor-owned service repair, and disable configured background
auto-updates. The pinned OpenClaw v2026.8.2 release honors external supervisor
mode by refusing native service mutation and OpenClaw self-update with guidance
to use the external supervisor's workflow. This behavior belongs to upstream
OpenClaw; the launcher does not reserve, reject, or rewrite upstream command
arguments.
OpenClaw inherits the terminal's working directory; the launcher does not make
the read-only application directory the workspace.
clawctl exposes package readiness and launcher version information:
| Command | Behavior |
|---|---|
clawctl setup |
Verify compatible Node.js is on PATH and confirm packaged app\openclaw.mjs exists. |
clawctl --version |
Print the packaged launcher version. |
Bare clawctl, clawctl -h, and clawctl --help print help without changing
state. clawctl setup --help prints help for that command alone. Help, usage,
and completion come from
System.CommandLine.
Invalid management input is rejected with exit code 1 and a parse diagnostic
on standard error; no readiness check runs.
Help and version requests take precedence over the rest of the command line.
clawctl --version bogus prints the launcher version and exits 0 rather than
reporting bogus, because the version request is satisfied before the
remaining arguments are validated. The version printed is always the packaged
launcher's assembly version, including when the launcher is hosted by another
process.
Response-file expansion is disabled. A leading @ has no meaning to clawctl
and is reported as an unrecognized argument rather than read from disk.
These parser conveniences belong to clawctl only. openclaw forwards every
argument to the OpenClaw CLI verbatim, so a leading @ or a directive-shaped
token reaches that CLI uninterpreted.
Commands such as doctor, gateway, and uninstall belong to the OpenClaw
CLI and must be invoked through openclaw.
setup requires a compatible device-installed Node.js runtime. Missing,
outdated, malformed, or architecture-incompatible runtimes produce an
actionable error rather than a later process-launch failure.
clawctl setup is read-only. It performs no extraction, hashing, inventory
walk, or state mutation.
The launcher places Node.js in a Windows job configured with
JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE. The launcher remains alive while Node.js
runs; if the launcher exits or is terminated, Windows terminates Node.js and
its child processes when the job handle closes.
Install the current Node.js LTS release, open a new terminal, optionally check
readiness, then use openclaw:
winget install --id OpenJS.NodeJS.LTS --exact --source winget
clawctl setup
openclawThe packaged OpenClaw revision accepts Node.js
>=22.22.3 <23 || >=24.15.0 <25 || >=25.9.0. The launcher keeps this
requirement in one shared validator used by clawctl and openclaw.
.github\workflows\gateway-msix.yml resolves an explicit OpenClaw ref before
building. Pull-request and main push runs use the pinned commit configured in
both:
workflow_dispatch.inputs.openclaw_ref.default;- the non-manual fallback in
env.OPENCLAW_REF.
Changing only the workflow-dispatch default does not change automatic builds.
For a one-time override, run Build OpenClaw Gateway MSIX manually and
provide a tag, branch, or preferably a full 40-character commit SHA in
openclaw_ref.
The payload artifact records the requested ref and resolved upstream commit in
payload-metadata.json. That build-only file is not embedded in the MSIX.
msix-metadata.json records both the packaging repository commit and bundled
OpenClaw commit, while embedded payload-files.json records every packaged
application file's path, length, and SHA-256.
release-policy.json records the immutable OpenClaw commit and payload version
approved for official signing, plus the independent MSIX package version and
release tag. Updating that
policy requires a reviewed repository change. Official signing runs only from
main and verifies the workflow input, policy-approved package version, both
architecture metadata files, both MSIX hashes, the embedded manifests, and
every file against the embedded application inventory. It also byte-compares
the bundle's embedded packages with those authorized standalone packages before
requesting Azure credentials.
dotnet restore .\OpenClaw.Gateway.MSIX.slnx
dotnet test .\OpenClaw.Gateway.MSIX.slnx `
--configuration Release `
--no-restorescripts\Build-Payload.ps1 npm-installs an OpenClaw package into an expanded,
architecture-specific application tree. scripts\Build-MSIX.ps1 copies that
tree into package content, rejects any Node.js executable or runtime archive,
creates a per-file inventory, and then creates an unsigned NativeAOT MSIX.
scripts\Build-LocalMSIX.ps1 can reuse a successful workflow payload or a
local payload directory. The Node.js used by the payload build jobs is build
infrastructure only and is not copied into the MSIX.
Normal pull-request and push workflows publish unsigned packages for validation. Manual runs support three signing modes:
unsignedaccepts any OpenClaw branch, tag, or commit and publishes unsigned MSIX packages;testaccepts any OpenClaw ref and publishes MSIX packages signed with a temporary self-signed certificate plus the public.cerneeded for local installation;officialrequires the approved immutable commit fromrelease-policy.json, may run only frommain, and publishes the signed packages as permanent assets on a GitHub Release named by the policy.
Official signing uses the protected release-signing environment, Azure OIDC,
and the existing OpenClaw Artifact Signing account and certificate profile.
Test-signing private keys are generated only on the temporary GitHub runner
and are deleted before artifacts are uploaded. No signing secret or private
key is stored in the repository.
Official releases use the independent four-part numeric packageVersion and
releaseTag from release-policy.json. The initial signing proof uses package
version 0.0.0.0 and tag v0.0.0.0; a later policy change can establish the
long-term Gateway-to-MSIX version mapping. The workflow creates the tag in this
repository and a GitHub Release with generated release notes. Each release
contains a signed, multi-architecture
OpenClawGateway-<version>.msixbundle as the recommended download, plus signed
OpenClawGateway-<version>-x64.msix and
OpenClawGateway-<version>-arm64.msix packages for architecture-specific
deployment. The duplicate GitHub Actions artifacts remain short-lived transport
and diagnostic copies.
For the all-zero proof only, MakeAppx assigns the outer bundle identity its
date/time-based version because it does not preserve 0.0.0.0 as a bundle
version. The two embedded architecture packages retain identity version
0.0.0.0; signing authorization verifies those versions and byte-compares both
embedded packages with the approved standalone inputs.
An .msixbundle is a single installable container for the x64 and ARM64 MSIX
packages; Windows selects the package appropriate for the device. An
.appinstaller file is separate update-channel metadata rather than an
alternative package format. This repository does not publish one yet, so GitHub
Release installs do not opt devices into automatic update checks.
The release-signing GitHub environment must define these environment
variables (they are identifiers, not credentials):
AZURE_CLIENT_ID: application (client) ID of the dedicatedopenclaw-windows-msix-signingEntra application;AZURE_TENANT_ID: Entra tenant ID;AZURE_SUBSCRIPTION_ID: Azure subscription containing the signing resource.
Do not create an AZURE_CLIENT_SECRET. The sign-msix job requests a
short-lived Azure token with GitHub OIDC. The Entra application must have a
federated identity credential with:
- issuer:
https://token.actions.githubusercontent.com; - subject:
repo:openclaw@252820863/openclaw-windows-packaging@1347889239:environment:release-signing; - audience:
api://AzureADTokenExchange.
This repository was created after GitHub's immutable OIDC subject rollout, so
the subject includes the organization and repository IDs. The older mutable
repo:openclaw/openclaw-windows-packaging:... form will not match its tokens.
The service principal must have Artifact Signing Certificate Profile Signer
on the openclaw certificate profile (or a containing scope). The workflow
uses account openclaw, certificate profile openclaw, and endpoint
https://eus.codesigning.azure.net/. The expected public certificate subject
is recorded in release-policy.json.
| Data | Default path |
|---|---|
| OpenClaw application files | Read-only MSIX package app directory |
| OpenClaw configuration and user state | %USERPROFILE%\.openclaw |
| Launcher diagnostics | %LOCALAPPDATA%\Packages\<package-family>\LocalState\OpenClawGatewayMSIX\Logs\openclaw.log |
OpenClaw application files are owned and serviced by Windows as part of the
immutable MSIX installation. OpenClaw user state remains outside the package.
Updating or removing the MSIX does not automatically delete that state or stop
a running Gateway. Use OpenClaw's documented
openclaw uninstall flow before
removing the MSIX.
The payload build emits an expanded npm-installed application tree.
Build-MSIX.ps1 rejects bundled Node.js, copies the tree into package content,
and records every application file's path, length, and SHA-256 in
payload-files.json. Package construction verifies that exact inventory
against the generated MSIX. Official signing authorization repeats the
inventory validation, including rejecting missing, changed, duplicate, unsafe,
or unlisted application entries, before requesting signing credentials.
At runtime, Windows' MSIX package integrity and read-only enforcement is the
trust boundary. openclaw and clawctl setup only check that
app\openclaw.mjs exists; neither performs file hashing or an inventory walk.
This avoids redundant startup overhead while keeping package mutation under
Windows servicing control.
The longer-term design is to run the Gateway payload in a dedicated isolated agent session rather than the interactive session where the human user is logged in. This will provide a boundary similar in purpose to running the Gateway in WSL, using the forthcoming isolated-session capabilities. That isolation is not provided by the current MSIX implementation.
This is an independent public implementation in the OpenClaw ecosystem, informed by the upstream OpenClaw and Windows Node projects rather than a source fork of either repository. See CONTRIBUTORS.md for acknowledgements and links to the contributor histories.