This repository builds a Windows MSIX package containing:
- one .NET 10 NativeAOT launcher exposed through separate packaged
openclawandclawctlapplication identities and app execution aliases; - a pinned, verified build of
openclaw/openclaw; - the official Node.js archive matching the upstream build's runtime version and the package architecture.
Builds use .NET SDK 10.0.400 or a later .NET 10 feature band.
The package is independent from the
OpenClaw Windows Node and Companion
and uses the Partner Center-reserved
OpenClawFoundation.OpenClawGateway package identity. Its package family is
OpenClawFoundation.OpenClawGateway_rfcbke2p71se2 and its publisher is
CN=4BA40A7A-B719-4C40-BF91-84AF4F1136FC.
- Agent instructions define repository-wide working, ownership, validation, and safety rules and route scoped source, script, and test work.
- Architecture explains the host, isolated session, MXC, agent runtime, and gateway boundaries.
- Troubleshooting maps observable failures to checks and recovery steps.
- Local development covers loose registration, local MSIX composition, test signing, and the NativeAOT validation lanes.
- Official release process covers the reviewed policy change, signing workflow, upgrade evidence, and publication checks.
- Windows 11 on a build that supports isolated agent sessions, on x64 or ARM64.
OpenClaw always runs inside an isolated session, so a machine that cannot host
one is not supported: both
clawctl setupandopenclawfail with a message naming this requirement and the diagnostic log path.openclawfirst attempts automatic setup. Install the latest Windows updates (Settings > Windows Update), or install a newer Windows version, and runclawctl setupagain. - Developer Mode is required only for the local loose-layout development loop, not for the signed MSIX.
Both application identities activate the same packaged openclaw.exe. The
launcher recovers either the package-qualified application identity or 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.
openclaw runs only inside an isolated agent session. When the setup marker is
absent, it provisions the installation before forwarding its arguments, so a
clean machine can start with openclaw, including openclaw --help and
openclaw --version. It does not run OpenClaw on the host. Unreadable,
incomplete, foreign, newer-schema, preparing, or tearing-down setup markers,
session mismatches, and stale agent Node.js runtimes remain explicit recovery
states with their clawctl setup or clawctl teardown guidance; automatic
setup never repairs them.
Before launching, the host resolves the bundled Node.js executable previously
prepared during setup and checks its PE product version and executable
architecture against the packaged archive without a separate Node.js process.
The runtime directory is prepended
to the child's PATH so Node.js, npm, and npx subprocesses use the bundled
tools without changing the user's environment.
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. It also reports the Windows Gateway session mode
through the process-stable CLAWCTL_GATEWAY_ISOLATION=enabled environment
variable. OpenClaw always runs inside the isolated session, so this value is
always enabled.
These values declare external lifecycle ownership, prevent doctor-owned service
repair, disable configured background auto-updates, and expose diagnostic
isolation status without claiming independent attestation. The selected OpenClaw runtime 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.
After each successful interactive openclaw invocation, the launcher performs
a file-only readiness check inside the isolated session. If the default
.openclaw\openclaw.json has gateway.mode set to local and the managed
gateway is NotStarted or Stopped, it starts the gateway when readiness is
startup eligible, narrating progress on standard error. It does nothing for
a non-zero OpenClaw exit code, redirected output, non-eligible readiness, or a
Starting, Unhealthy, or Unknown gateway. An observed Running gateway is
acknowledged. A failed or unverified start writes a standard-error warning and
a retry command, clawctl gateway-service start, without changing the
OpenClaw exit code; the next eligible run retries. A successful start is
acknowledged for the current Windows logon. See environment
variables to restore the prior start hint.
These host-side variables are not passed to the OpenClaw child. Automatic
behavior is enabled by default. Set either variable to 0, false, no, or
off (case-insensitive and with surrounding whitespace ignored) to suppress
its automatic behavior; any other value, including an unset variable, leaves
it enabled.
| Variable | Suppressed behavior |
|---|---|
CLAWCTL_AUTO_SETUP |
Restores the previous clean-machine failure: openclaw reports that it has not been set up and directs you to clawctl setup; it provisions, records, and registers nothing. |
CLAWCTL_AUTO_GATEWAY_START |
Restores the previous clawctl gateway-service start hint instead of automatically starting an eligible gateway. |
clawctl owns explicit setup, recovery, and the isolated-session operations:
| Command | Behavior |
|---|---|
clawctl setup |
Confirm packaged app\openclaw.mjs exists, provision or reuse the owned isolated session, and install the bundled Node.js runtime in the agent profile. It also configures gateway sign-in recovery without starting a gateway. On a machine that cannot host a session it fails with the Windows requirement described under Requirements. |
clawctl completion |
Write PowerShell completion for both clawctl and openclaw to standard output. Source it for the current shell, or use --install to add a marked block to the current-user PowerShell profile. |
clawctl completion --install [--profile <path>] |
Atomically update the selected profile (or the current-user PowerShell profile) with a marked loader that sources completion from the currently installed clawctl package whenever a new shell starts. Package updates therefore take effect without rewriting the profile. The trusted upstream script is also cached in host LocalState, and clawctl pwsh safely projects it into the current isolated workspace. --uninstall removes the marked profile block and invalidates the host cache so later agent shells do not load a stale projection. |
clawctl setup --fresh [--force] |
Remove this installation's owned session and package-local state, then run setup again. Without --force, incomplete external cleanup stops before local state is erased. --force is valid only with --fresh; it preserves an explicit warning when cleanup of owned external resources cannot be confirmed, but still stops if bounded local deletion fails. |
clawctl status |
Report the recorded isolated session, agent account and shared folder, installed Node.js runtime, gateway, sign-in recovery, and file-only config readiness when the gateway is not running. It asks the backend to start the recorded provision as its status probe, so it is not a passive diagnostic, but it does not provision a replacement or start the gateway. Use clawctl gateway-service status to inspect the gateway alone. |
clawctl open |
Open the running managed gateway's Control UI in the default browser. Requires completed clawctl setup and an already-running gateway; it probes those prerequisites and fails rather than starting the gateway. Packaged OpenClaw resolves the endpoint, TLS, Control UI base path, and authenticated one-time browser handoff. Authenticated URLs and tokens are not printed. |
clawctl teardown --force |
Confirm deletion, then stop and deprovision the owned session and remove its data and setup state. The MSIX remains installed. |
clawctl pwsh |
Open an interactive PowerShell session inside the agent session. |
clawctl pwsh --command <text> |
Run one quoted PowerShell command string inside the agent session and return its exit code. |
clawctl pwsh --file <path> [-- <arguments>] |
Run an agent-visible PowerShell script and return its exit code. Relative paths start in the shared folder reported by clawctl status; use -- before script arguments that begin with -. |
clawctl collect-logs [--output <path>] |
Create a redacted host-and-agent diagnostics ZIP. |
clawctl gateway-service start |
Start the OpenClaw gateway in the isolated session and wait for it to listen. Requires setup. |
clawctl gateway-service status |
Inspect the gateway without starting it. When the gateway is not running, it may start/probe only the already-recorded isolated session to report file-only config readiness; it never provisions a replacement or starts the gateway. |
clawctl gateway-service stop |
Stop the gateway while retaining the session and its data. |
clawctl gateway-service restart |
Stop the gateway and start it again as one lifecycle operation. If the stop cannot be verified, it retains the gateway record and does not start a replacement. If no gateway is running, it starts one. |
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. Parsing,
usage errors, and completion come from
System.CommandLine,
while help is rendered by clawctl itself from the live command tree, so a
command added to the parser is documented without a separate help edit.
Invalid management input is rejected with exit code 1 and a parse diagnostic
on standard error; no readiness check runs.
All commands except pwsh accept --json and emit a versioned JSON document
on standard output. Human diagnostics remain on standard error, and command
exit codes do not change. Every clawctl pwsh mode preserves PowerShell's
standard streams, so --json is rejected rather than capturing or wrapping
script output.
Quote inline PowerShell as one --command value:
clawctl pwsh --command 'Get-Content ~/foo.txt'
clawctl pwsh --file .\diagnose.ps1 -- -Detailed -Name 'test value'PowerShell evaluates the inline command inside the isolated agent, so ~
within --command names the agent profile rather than the invoking user's
profile. A relative --file path starts in the recorded shared folder;
clawctl does not copy a host-local script into that folder.
When the gateway is not confirmed running, status JSON includes an optional
gateway.readiness object with state, stable reason, and failure detail
where applicable. The readiness states are absent, not-ready,
startup-eligible, unavailable, and unknown. A running gateway omits this
object and incurs no config-readiness probe.
Interactive terminals use color for headings and status marks. --no-color,
the NO_COLOR environment variable, redirected output, and CI disable color;
FORCE_COLOR enables it for redirected output or CI unless color was
explicitly disabled. JSON output never contains terminal escape sequences.
clawctl gateway-service start waits for the gateway to bind rather than
returning as soon as the process exists, because a process without a listener
is not a usable gateway. It reports each stage on standard error as it
happens — a spinner when standard error is an interactive terminal, one line
per stage anywhere else — so standard output carries only the result. Under
--json it narrates nothing at all, and standard output carries exactly one
document.
The wait has a fixed budget. A gateway still coming up when the budget is spent
is reported as starting rather than failed, and clawctl gateway-service status
will show it once it binds.
clawctl reports the gateway port only when it can identify that listener
unambiguously:
clawctl gateway-service start
Gateway: ✓ listening
Port: 18789
OpenClaw owns the endpoint configuration, including TLS and a custom Control UI
base path. clawctl status and clawctl gateway-service start therefore do
not construct an HTTP URL that might contradict that configuration. They report
no port when multiple unclassified listeners remain; JSON follows the same
rule, with gateway.port present only when identified and no URL promised.
clawctl open uses packaged OpenClaw's verified, authenticated browser handoff
instead of constructing a URL from that port. It does not start or recover the
gateway: start it first with clawctl gateway-service start if the probe says
it is not running. The command does not print authenticated URLs or tokens.
Help and version requests take precedence over the rest of the command line.
clawctl --version bogus reports the build identity and exits 0 rather than
reporting bogus, because the version request is satisfied before the
remaining arguments are validated.
clawctl --version reports the package version and packaging-repository
commit alongside the bundled OpenClaw payload version and its commit:
clawctl 0.0.0.1
Package: 0.0.0.1 (bfcb5ba73e7ea3e88ceed8c326e58e5baadc3191)
Payload: 2026.8.2 (0965053fe6b9341776df147a6934b7485c60b5ca)
Each commit is the one that produced the version it follows, and is muted so the version stays the value a reader compares.
clawctl --version --json reports the same identity as a versioned document,
so a support or deployment script can collect it without parsing prose:
{
"ok": true,
"schemaVersion": 1,
"command": "version",
"package": { "version": "0.0.0.1", "commit": "bfcb5ba…" },
"payload": { "version": "2026.8.2", "commit": "0965053…" }
}Those four values are compiled into the binary as constants by the build that
produces the package, so the report cannot drift from the payload it shipped
with and costs no file or process access at startup. Build-MSIX.ps1 supplies
the versions and commits it also records in msix-metadata.json; an ordinary
build falls back to the pin recorded in release-policy.json, and reports
unknown for a value no build supplied. The report never comes from the entry
assembly, so it stays correct 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.
clawctl setup provisions an explicitly owned agent session and extracts the
architecture-specific runtime archive from the immutable MSIX into that agent's
writable LocalState:
%LOCALAPPDATA%\Packages\<package-family>\LocalState\OpenClaw\NodeJS\node-v<version>-win-<architecture>.
Extraction is idempotent, versioned, and serialized across concurrent setup
processes, including different Windows sessions. Setup validates existing
runtimes before reuse, replaces invalid runtimes, and validates extraction
before publishing it. It does not prepare the invoking user's host runtime,
because nothing runs on the host.
Setup also mirrors the application's native dependency packages into the
agent's own LocalState, under
%LOCALAPPDATA%\OpenClawGatewayMSIX\agent-native\<content-id>. The
isolated-session identity may read packaged files but may not map them as
executable images, so loading a .node addon directly from the package fails
with ERR_DLOPEN_FAILED even though the same bytes load from a writable
location. Only the packages that carry a .node, .dll, or .exe artifact
are mirrored; the rest of the application, which is nearly all of it, keeps
executing from the immutable package.
That set is discovered by scanning app\node_modules, never from a hard-coded
list, so an upstream revision that introduces a new native dependency is staged
automatically. Whole owning package directories are copied rather than
individual binaries, because a package locates its sibling libraries and helper
executables relative to its own directory. A packaged preload then redirects
both CommonJS and ESM resolution to the staged copies. The launcher places that
preload on the agent Node.js argument vector so OpenClaw retains it when an
agent invokes openclaw again. Once loaded, the preload appends itself to the
agent account's own NODE_OPTIONS for ordinary Node.js workers, so an option
the agent set survives and the invoking host's value never reaches it. Staging
is idempotent, keyed by package content, and reclaims the superseded copy after
an upgrade once nothing is still running from it; a launch holds its root for
its whole lifetime, and a root that is still held is left whole for a later
setup.
Run clawctl setup before using clawctl pwsh or gateway-service start.
openclaw provisions a clean installation automatically, but there is no
session-free mode: it runs only inside the recorded session and leaves degraded
setup state to explicit recovery. Both setup paths fail with the same message
on a machine that cannot host a session. See
MXC compatibility evidence for the
session model, gateway health criteria, and diagnostics limits.
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.
On a clean installation, start directly with openclaw:
openclawRun clawctl setup first when you want the explicit setup, recovery, or
--fresh path.
When an MSIX update changes the bundled Node.js version, run clawctl setup
before launching OpenClaw. Automatic setup does not repair a stale agent Node.js
runtime after an update. Previously extracted versions are left in place so an
update does not remove a running process's runtime.
.github\workflows\gateway-msix.yml selects stable through public npm
openclaw@latest whenever a new packaging run starts. The resolver checks the
exact published version, its signed upstream tag and commit, and the source
package version before building. There is no automatic fallback to another
version or channel; extended-stable and named prereleases are rejected.
Source selection also checks the MSIX release-version rules before building:
numeric correction suffixes must be -2 through -9. Unsupported corrections
are rejected for channel selection, explicit refs, policy pins, and retries.
The openclaw-source-resolution artifact records this choice once per run.
Retries reuse it without querying the moving channel again. If the snapshot
is missing or expired (90-day retention), start a new run instead of retrying.
Package and payload metadata record the resolved source commit and version.
For a one-time unsigned/test override, provide a stable-source tag, branch, or
full commit SHA in the manual openclaw_ref input. Empty means follow stable.
If compatibility requires an older known-good stable release, a reviewed
stableVersion field in release-policy.json can pin its exact version, for
example "stableVersion": "2026.9.4". A pin is not automatic fallback and does
not grant official-signing approval.
For official signing, the selected source must match approvedCommit,
gatewayTag, and payloadPackageVersion in release-policy.json. An empty
input selects stable and checks that approval; an explicit input must be the
full approved commit SHA.
Payload composition validates that the selected OpenClaw runtime discovers and activates the packaging-owned Windows Launcher plugin by default, both without configuration and with an existing profile that has no plugin decision. Runtime inspection verifies its read-only route shape without writing enablement or allowlist overrides, then verifies that an explicit disable prevents registration. The isolated temporary profile is removed after inspection and does not modify user configuration. Incompatible older refs fail instead of producing a package with an unvalidated plugin.
The source build uses that revision's .github/actions/setup-node-env action
to select Node.js and pnpm. Its resolved Node.js version is recorded in
source.json, reused for both Windows payload builds, and carried in
payload-metadata.json. Package composition downloads that exact version;
the launcher derives its runtime version and LocalState path from the bundled
archive name. There is no separate packaging-side Node.js version pin or
runtime-support policy.
Non-official workflows cache the packed OpenClaw tarball by its resolved upstream commit. They also cache each architecture's Windows dependency tree by the resolved commit, tarball SHA-256, Node.js version, and payload-build script. A tarball cache hit still verifies the recorded version, commit and SHA-256; a dependency-tree hit still runs every payload validation and smoke test. Official-signing workflows bypass both caches and always rebuild upstream source and Windows dependencies.
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 Gateway tag
approved for official signing, plus an independent MSIX packaging revision.
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. Run it on Windows with Node.js matching both
the selected upstream version and target architecture: native install scripts
can use process.arch instead of npm's target-CPU flag. CI builds x64 on
windows-latest and ARM64 on windows-11-arm, using matching Node.js binaries.
Both payloads run their CLI smoke test. Cross-architecture Node.js execution
is rejected before staging or npm installation, including when reusing a tree.
These jobs validate payload loading and packaging, not installed agent-session
E2E on a supported host. Build-MSIX.ps1 and
Build-LocalMSIX.ps1 -PayloadDirectory can still cross-compose an already-qualified
payload.
It validates the Gateway and Control UI
build identities on the installed tree, including reused staged installs, then
provisions the packaging-owned Windows Launcher plugin into the payload copy's
bundled plugin directory. Its internal package, path, and plugin ID remain
gateway-isolation. The bundled plugin is enabled by default and adds the
read-only Windows Launcher tab to the Control group, serving it
through an authenticated, sandboxed plugin route. It reads only the launch-time
CLAWCTL_GATEWAY_ISOLATION value and registers no mutation RPC or process
control. The informational page shows one Gateway Isolation row with an
Active badge only for the exact enabled report (HTTP 200). Missing, malformed,
or unsupported reports, including disabled, show Invalid
and neutral status-unavailable text (HTTP 503), never a supported off state.
The live route establishes Gateway availability; disconnected-Gateway messaging
belongs to the Control UI. There are no isolation controls.
The active page's Command reference section provides a brief, copy-only cheat sheet grouped under ClawCtl and OpenClaw. Enter these commands in your normal Windows terminal (user session):
- ClawCtl:
clawctl gateway-service status,clawctl gateway-service restart,clawctl open,clawctl pwsh, andclawctl --help. PowerShell opens inside the isolated agent, whereopenclawandnodeare available; ClawCtl manages the session from outside it. Restart preserves the session and its data and starts the Gateway if it is not running. An unverified stop prevents a replacement from starting. Open dashboard opens the default browser when you runclawctl open; it requires completed setup and a running Gateway, and does not print authenticated URLs or tokens. - OpenClaw:
openclaw tuiandopenclaw --help. The packagedopenclawcommand forwards to your agent session; insideclawctl pwsh, it runs directly.
The page never executes commands or sends mutation requests. Copy controls announce success only after a clipboard operation succeeds; otherwise they offer manual-copy guidance, leaving the command selected when possible. Invalid isolation reports show no command references.
The report is captured once at plugin creation. It is a launcher-provided
diagnostic, not independent isolation attestation. The launcher now requires an
isolated session and supplies CLAWCTL_GATEWAY_ISOLATION=enabled to its guest
processes. It no longer supports host execution or the old OPENCLAW_SESSION
routing preference; that variable is not accepted as a substitute report here.
The default enables the tab, not isolation, and does not alter launcher execution.
Existing profiles without a plugin decision adopt the new default on the next normal start of the updated Gateway. No configuration migration or automatic restart is added. Explicit user disables, global plugin disablement, denylists, and restrictive allowlists retain OpenClaw's standard precedence; the package does not rewrite them. Direct-Node UI fixtures verify rendering and interactions, not packaged-launcher isolation or installed upgrade behavior.
The same plugin supplies agent instructions on Windows when the launcher report
is exactly enabled. Before supported agent runs, it prepends context explaining
that local GUI is not the user's desktop, user participation needs a supported
text/headless route, and private work should stay private while only intended
deliverables are handed off. Remote or user-session nodes require their own
capability and authorization checks. This is guidance, not isolation enforcement
or a file-export feature; it does not install a skill or overwrite workspace
instructions or configuration.
The plugin does not receive or invent a shared-folder path. Use a supported
client attachment or discover the real session.sharedFolder through
clawctl status --json in the user's normal terminal; that command can start the
recorded session. Agent-side file access alone does not prove user access.
The prompt hook also respects hooks.allowPromptInjection=false and
hooks.allowConversationAccess=false under the plugin's configuration entry.
Coverage and the local-only runtime proof are described in
local development.
Full selected-theme cohesion requires the generic plugin-frame theme forwarding
merged by
openclaw/openclaw#145409.
OpenClaw v2026.9.5 at ec9c1a13db8938e5a3eaa51fca2e981cde2395a9
includes that forwarding and has been qualified with this default-on plugin.
Unsigned and test builds follow the stable-source selection above; that
qualification is not an official runtime approval.
The official-signing policy remains on the release-approved OpenClaw v2026.9.4
baseline (3a9d69db306cd7f081e06254cb89c4bcc14a7107); that approval does not
establish support for this default-on/theme contract. A compatible runtime
requires separate reviewed approval before an official release. Without theme
forwarding, the page uses the browser or operating system light/dark preference
with a safe built-in palette.
scripts\Build-MSIX.ps1 downloads the official Node.js archive matching the
payload's recorded build version and architecture, copies both inputs into
package content, rejects Node.js inside the application payload, 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. -NodeArchivePath can supply an already-downloaded
archive, but its version and architecture must match the payload metadata.
To go from a clean checkout to a registered, runnable package:
.\scripts\Deploy-LocalPackage.ps1This is the development inner loop. It does not build, sign, or install an
MSIX. It acquires the payload and the bundled Node.js runtime, publishes the
NativeAOT launcher, assembles a Developer Mode layout under
artifacts\local-package, registers it with Add-AppxPackage -Register, and
runs clawctl setup so openclaw is immediately usable.
The command is idempotent: re-running with nothing changed reports that the package is already up to date and does nothing, and re-running after a source or payload change rebuilds only what changed. The expanded application is linked into the layout rather than copied, so repeat runs neither re-download nor duplicate hundreds of megabytes.
| Option | Behavior |
|---|---|
-RefreshPayload |
Download the payload again; the previous one is kept until the new one registers successfully |
-PayloadRunId <id> |
Use a specific successful workflow run, reusing a matching cached payload |
-PayloadDirectory <path> |
Read a prepared payload directly, with no GitHub access and no modification; pass it on every run |
-Architecture x64 / arm64 |
Select the architecture; it must be runnable on this device |
-ReplaceExistingInstall |
Remove a conflicting MSIX-installed package first (see below) |
-SkipSetup |
Register without extracting the Node.js runtime |
-Force |
Re-register even when nothing changed |
-Unregister |
Remove the local registration, preserving app data and caches |
Requires Developer Mode, which the script checks before doing any work.
It cannot coexist with an MSIX-installed
OpenClawFoundation.OpenClawGateway. Windows
refuses to replace a packaged install with a local layout, and it cannot
preserve that package's app data across the switch, so the script stops and
explains rather than removing anything implicitly. Pass
-ReplaceExistingInstall to accept that trade.
Run -Unregister before installing a released package. Windows will not
replace a loose registration with a packaged install: Add-AppxPackage fails
with 0x80073CFB, reporting that an unpackaged version is already installed
and a packaged version cannot replace it. This is the same mutual exclusion as
above, in the other direction, and it applies regardless of version. Unregister
first, then install the release:
.\scripts\Deploy-LocalPackage.ps1 -Unregister
Add-AppxPackage -Path .\OpenClawGateway-0.0.0.0-x64.msixThe registered package reads its files from the repository. Deleting
artifacts\local-package, moving the checkout, or deleting the worktree breaks
the registration until the command runs again; -Unregister first if you plan
to remove the checkout. Local builds are unsigned development artifacts and are
never official-signing inputs.
Normal pull-request and push workflows publish unsigned packages for validation. Manual runs support four modes:
unsignedfollows stable or a stable-source override and publishes unsigned MSIX packages;testuses the same source-selection rules and publishes MSIX packages signed with a temporary self-signed certificate plus the public.cerneeded for local installation;storerequires the reviewed immutable commit fromrelease-policy.json, may run only frommain, and publishes permanent unsigned Partner Center submission assets; Microsoft signs them during Store ingestion;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 derive their GitHub tag and four-part numeric MSIX identity
from gatewayTag and msixRevision in release-policy.json. The GitHub tag is
<gateway-tag>-msix.<revision>. The MSIX identity is
year.month.VVPN.0: VV is the two-digit monthly Gateway release sequence,
P is the Gateway correction digit, and N is the MSIX rebuild digit. The
digits are packed numerically into the third component, so leading zeroes are
not written.
| Gateway tag | MSIX revision | GitHub release tag | MSIX version |
|---|---|---|---|
v2026.7.1 |
0 |
v2026.7.1-msix.0 |
2026.7.100.0 |
v2026.7.1 |
1 |
v2026.7.1-msix.1 |
2026.7.101.0 |
v2026.7.1-2 |
0 |
v2026.7.1-2-msix.0 |
2026.7.120.0 |
v2026.7.2 |
0 |
v2026.7.2-msix.0 |
2026.7.200.0 |
v2026.7.12 |
0 |
v2026.7.12-msix.0 |
2026.7.1200.0 |
Gateway release sequences must be 1 through 99. The unsuffixed Gateway tag
uses correction digit 0; correction suffixes -2 through -9 use their
numeric suffix. A -1 suffix remains rejected to match the Gateway release-tag
contract. Set msixRevision from 0 through 9, starting at 0 for each
Gateway tag and incrementing it only when that exact Gateway tag is repackaged.
Decimal place value guarantees Gateway release > Gateway correction > MSIX
rebuild while keeping every component at four digits or fewer and reserving the
fourth component as 0 for Microsoft Store submission.
To prepare an official release, update these policy inputs together in a reviewed pull request:
gatewayTagto the stable upstream Gateway tag;approvedCommitto the immutable commit resolved from that tag;payloadPackageVersionto the version reported by the pinned payload;msixRevisionto0, or increment it for a packaging-only rebuild of the same Gateway tag.
After that pull request merges, manually run Build OpenClaw Gateway MSIX on
main with openclaw_ref set to the approved commit. Use signing_mode=store
for the Partner Center identity, or signing_mode=official only when the Azure
certificate subject exactly matches the reviewed publisher. The workflow derives
the package version and release tag, creates
the tag in this repository, and publishes a GitHub Release with generated
release notes. Each release contains a multi-architecture
OpenClawGateway-<version>.msixbundle as the recommended Store submission, plus
OpenClawGateway-<version>-x64.msix and
OpenClawGateway-<version>-arm64.msix packages for architecture-specific
deployment. Store-mode assets are intentionally unsigned and are not direct
sideload downloads; Partner Center signs them during ingestion. The duplicate
GitHub Actions artifacts remain short-lived transport and diagnostic copies.
The same identity can be used for direct distribution and Microsoft Store
submission; the fourth component is always 0.
The signed v0.0.0.0 and v0.0.0.1 proof releases and the latest production
release retain the former OpenClaw.Gateway identity and remain immutable
transition baselines. Partner Center requires the reserved
OpenClawFoundation.OpenClawGateway identity, so Windows cannot update those
packages in place or retain their packaged LocalState. Pull requests that
change release policy download the hash-pinned standalone x64 and recommended
.msixbundle assets, install each one on a clean GitHub-hosted Windows runner,
verify the legacy identity, remove it, install the candidate through the same
delivery format, and prove that Windows registered the reserved package family
with isolated LocalState. Changes to source-selection scripts also trigger this
check against the selected release. The gate additionally proves fresh
installation of both candidate formats. It refuses to run when a Gateway package is
already registered and removes only packages installed by that test
invocation. It temporarily trusts the ephemeral test-signing certificate in
the local-machine Trusted People store, as required by Windows deployment, and
removes that certificate in finally. The resulting JSON evidence is retained
as a workflow artifact for 90 days. This identity change is the explicitly
approved breaking reset; future releases under the reserved identity must
return to in-place upgrade and LocalState-retention proof.
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. Before an official dispatch, the
certificate profile must issue that exact Partner Center publisher subject;
signature verification fails closed when it does not.
| Data | Default path |
|---|---|
| OpenClaw application files | Read-only MSIX package app directory |
| Bundled Node.js archive | Read-only MSIX package runtime directory |
| Extracted Node.js runtime | %LOCALAPPDATA%\Packages\<package-family>\LocalState\OpenClaw\NodeJS\node-v<version>-win-<architecture> |
| Staged native dependency packages | %LOCALAPPDATA%\OpenClawGatewayMSIX\agent-native\<content-id> (agent account) |
| 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 Node.js from that tree, copies it into package content,
and records every application file's path, length, and SHA-256 in
payload-files.json. It separately validates and hashes the pinned Node.js
archive. Package construction verifies both inputs against the generated MSIX.
Official signing authorization repeats the application inventory and Node.js
archive validation before requesting signing credentials.
At runtime, Windows' MSIX package integrity and read-only enforcement remains
the trust boundary for the application and archive. clawctl setup extracts
the archive into versioned package LocalState; openclaw launches the packaged
app\openclaw.mjs directly with that extracted executable. Neither command
hashes or walks the expanded application inventory.
Native dependency staging does not move that boundary. The copies live in the agent's own profile, which already holds the extracted Node.js runtime and is written and read by the same identity that executes it; nothing is staged into the guest-writable shared workspace. Application code continues to execute from the immutable package, and redirection is gated on a staged file existing, so a package that was not staged resolves exactly as before.
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.