Skip to main content
An embedding host should supervise the installed openclaw executable, use the Gateway WebSocket protocol as its control plane, and treat the child process as a replaceable runtime. This keeps process ownership, readiness, failure recovery, and upgrades explicit without depending on OpenClaw’s private state layout. For client authentication and reconnect state, read Building a Gateway client.

Start the child with an embedding preset

Use a real node_modules installation and spawn the package executable. A useful baseline for a host that owns discovery, restart, and channel lifecycle is:
Resolve OpenClaw through the installed package as shown; do not assume that a project-local openclaw binary is on the host process’s PATH. The example inherits output so the child cannot block on full stdout or stderr pipes. If the host captures those streams instead, attach consumers immediately after spawning. --allow-unconfigured bypasses only the gateway.mode=local startup guard. It does not write configuration or repair an invalid file. Omit it when the embedding app provisions a normal local configuration through onboarding, the config CLI, or Gateway RPC.

Electron shell snapshot warning

Shell snapshot capture runs process.execPath -e <script> from a login shell. In a normal Node process, process.execPath is the Node executable. Under Electron, it is the Electron binary, which can interpret the invocation as an application launch and show an “Unable to find Electron app” popup. Set OPENCLAW_EXEC_SHELL_SNAPSHOT=0 in the Gateway child’s environment, not only in the renderer process. For the same reason, hostNodeExecutable must point to a real Node runtime rather than Electron’s process.execPath.

Handle invalid config by exit code

Gateway startup uses exit code 78 (EX_CONFIG) for configuration-class startup failures, including an invalid config. Branch on the exit code instead of scraping human-readable stderr:
  1. Run openclaw doctor --fix --yes --non-interactive against the same config and state environment as the Gateway child.
  2. Retry Gateway startup once after doctor exits successfully.
  3. If the child exits 78 again, stop the repair loop and surface the config failure to the user.
Keep stderr for diagnostics, but do not make lifecycle decisions from its wording. After a successful startup, an invalid live config edit is less destructive. The config watcher logs that reload was skipped and continues serving the last accepted in-memory config. Repair the file, then let the watcher accept the next valid snapshot.

Wait for protocol readiness

Use WebSocket signals instead of a log substring:
  1. Open the Gateway WebSocket.
  2. Wait for the connect.challenge event. It proves that the listener accepted the WebSocket and the challenge handshake can begin.
  3. Send connect with the challenge-bound device signature.
  4. Treat hello-ok as application readiness for authenticated RPC.
The challenge is deliberately earlier than full initialization. If startup sidecars are still pending, connect returns a retryable UNAVAILABLE error with details.reason: "startup-sidecars", a bounded retryAfterMs, and then closes with code 1013 and reason gateway starting. Use resolveGatewayStartupRetryAfterMs from @openclaw/gateway-protocol/startup-unavailable or the reference client’s built-in policy, then reconnect.

Interpret restart and shutdown

Before an orderly close, the Gateway broadcasts a shutdown event with reason and restartExpectedMs. A non-null restartExpectedMs means an in-process or supervised restart is expected; null means a terminal shutdown. The subsequent WebSocket close code is 1012 for both cases. The ordinary client close reason is also service restart in both cases, so neither the close code nor the reason distinguishes restart from shutdown. Preserve the preceding shutdown payload when it arrives, and combine it with the host’s own stop intent and the child exit status. If the connection disappears without the event, use normal bounded reconnect and child-supervision policy.

Use RPC instead of state files

Keep the Gateway as the only owner of OpenClaw state. Common embedding operations already have RPC methods: config.get redacts sensitive values and SecretRef identifiers before returning the snapshot. Write methods also return redacted config. A client must treat the redaction sentinel as opaque and use the documented config write contract; it must never expect the Gateway to return plaintext secrets. Do not read or mutate files, SQLite tables, transcript files, or cache directories under ~/.openclaw to implement app features. Those layouts are private runtime implementation details and can move or change without protocol compatibility.

Install; do not flatten

The root openclaw package is not a single-file vendoring target. Bundled runtime files under dist/extensions retain bare self-imports such as openclaw/plugin-sdk/*, while the npm package intentionally excludes per-extension node_modules trees. Install OpenClaw through npm, pnpm, or another normal Node package installation so Node can resolve the package exports and root dependency tree. Spawn the installed openclaw executable. Do not copy only dist, flatten the package into an app bundle, or vendor selected extension files.