# Gateway, service, and security checks

> Gateway service, pairing, security, workspace status, auth, health, and supervisor checks (checks 8-17)

- 网址：https://funcoding.ai/agents/openclaw/gateway/doctor/gateway-and-services/
- 来源：OpenClaw 官方文档原文（英文），MIT 许可，同步于 2026-10-11
- 官方原文：https://docs.openclaw.ai/zh-CN/gateway/doctor/gateway-and-services

---
Checks 8-17 cover gateway service migrations, device pairing, security
warnings, workspace status, gateway auth and health, and supervisors.

A valid `openclaw-install-owner.json` marker at the package root identifies an
app-owned installation. Doctor reports the host's name and update hint and leaves
package, bundled UI, and runtime replacement to that host, including when a
standalone CLI inspects an app-owned Gateway service. For OpenClaw.app payloads,
update the app to update the Gateway; normal config and state checks still apply.

After repair, Doctor waits for the managed Gateway within a bounded readiness
budget. If the service manager still reports it running but its listener has not
opened, Doctor completes with a warning and exit code 0. Check
`openclaw gateway status` in a minute. A stopped or failed service still reports a
restoration failure; slow startup does not undo a completed repair.

## Checks 8-17

<details>
<summary>8. Gateway service migrations and cleanup hints</summary>

Run `openclaw doctor` interactively to review legacy gateway services (launchd/systemd/schtasks) and confirm supported cleanup. Explicit repair maintenance skips this separate cleanup flow. Removal is reported separately from installation; use `openclaw gateway install` when the intended native service is missing. Doctor can also scan for extra gateway-like services and print cleanup hints. Profile-named OpenClaw gateway services are considered first-class and are not flagged as "extra."

Cleanup previews include only legacy launchd services and recognized legacy systemd unit names in the user scope. Legacy Windows services, unrecognized Linux unit names, and services in the system scope remain findings for manual review.

Windows extra-service hints use read-only `schtasks /Query` inspection. Node hosts remain visible in diagnostics; discovery alone does not make a service a removal target. Unreadable unrelated Scheduled Tasks do not block discovery or test runtime preparation, regardless of whether they are running. Selected tasks, recognized OpenClaw launchers, and custom aliases with OpenClaw launcher content still report incomplete inspection when their command cannot be verified.

Linux user-service cleanup preserves the unit file if stopping or disabling the service fails. An interrupted status check does not permit file-only removal; that fallback is reported only when `systemctl` is unavailable.

On Linux, if the user-level gateway service is missing but a system-level OpenClaw gateway service exists, doctor does not install a second user-level service automatically. Inspect with `openclaw gateway status --deep` or `openclaw doctor --deep`, then remove the duplicate or set `OPENCLAW_SERVICE_REPAIR_POLICY=external` when a system supervisor owns the gateway lifecycle.

</details>

<details>
<summary>8b. Startup Matrix migration</summary>

When a Matrix channel account has a pending or actionable legacy state migration, doctor (in `--fix` / `--repair` mode) creates a pre-migration snapshot and then runs the best-effort migration steps: legacy Matrix state migration and legacy encrypted-state preparation. Both steps are non-fatal; errors are logged and startup continues. Without explicit repair (`--fix`, `--repair`, or `--yes`), this check is skipped.

</details>

<details>
<summary>8c. Device pairing and auth drift</summary>

Doctor inspects device-pairing state as part of the normal health pass, reporting:

- pending first-time pairing requests
- pending role or scope upgrades for already-paired devices
- public-key mismatch repairs where the device id still matches but the device identity no longer matches the approved record
- paired records missing an active token for an approved role
- paired tokens whose scopes drift outside the approved pairing baseline
- local cached device-token entries for the current machine that predate a gateway-side token rotation or carry stale scope metadata
- a retired `identity/device-auth.json` file that is still present and blocks inspection of locally cached tokens, including in remote Gateway mode; stop the Gateway and run `openclaw doctor --fix` to finish migration or cleanup
- retired `devices/*.json` and `nodes/*.json` stores on a local Gateway; stop the Gateway and run `openclaw doctor --fix` to import device approvals before node capabilities and archive the originals. Existing SQLite records take precedence; unreadable sources remain in place for repair.

Doctor does not auto-approve pair requests or auto-rotate device tokens. It prints the exact next steps:

- inspect pending requests with `openclaw devices list`
- approve the exact request with `openclaw devices approve <requestId>`
- rotate a fresh token with `openclaw devices rotate --device <deviceId> --role <role>`
- remove and re-approve a stale record with `openclaw devices remove <deviceId>`

This distinguishes first-time pairing from pending role/scope upgrades and from stale token/device-identity drift, closing the common "already paired but still getting pairing required" hole.

Node-hosting preconditions are checked only when config requests node execution or a browser node target, allows node commands, opts into remote pairing through CIDRs or SSH verification, or explicitly enables/configures the device-pair plugin. The plugin's bundled default, empty node settings, and deny-only policies do not request hosting. A local-only loopback Gateway therefore receives no node-hosting warning; remote-client profiles leave these checks to the server. Explicit pairing and join-code requests still report an unreachable origin. This diagnostic-only change does not rewrite config or alter update repairs.

</details>

<details>
<summary>9. Security warnings</summary>

Doctor emits a Security note only when it finds a warning, such as a provider open to DMs without an allowlist or a dangerously configured policy. Use `openclaw security audit` for the full security inventory.

Missing multi-agent DM routing ownership is reported as a finding. It does
not stop the remaining channel security checks or pending state migrations.
Configure the reported account binding before expecting that route to work.
Telegram account discovery preserves the legacy default-agent account choice
during upgrade previews without requiring an ambient agent.

</details>

<details>
<summary>10. systemd linger (Linux)</summary>

If running as a systemd user service, doctor ensures lingering is enabled so the gateway stays alive after logout.

</details>

<a id="11-workspace-status-skills-plugins-and-taskflows" />

<details>
<summary>11. Workspace status (skills and plugins)</summary>

Doctor prints problems and actions for the default agent, not healthy-state inventory:

- **Skills**: lists allowed but unusable skill names; use `openclaw skills check` for requirement details and full counts.
- **Plugins**: reports only errored plugin IDs; use `openclaw plugins list` for loaded, imported, disabled, and bundle-plugin inventory.
- **Plugin compatibility warnings**: flags plugins that have compatibility issues with the current runtime.
- **Plugin diagnostics**: surfaces any load-time warnings or errors emitted by the plugin registry.
- **Claude CLI**: reports only binary, authentication, profile, workspace, or project-directory problems; healthy check details are omitted.

</details>

<details>
<summary>11b. Bootstrap file size</summary>

Doctor checks workspace bootstrap candidates (`AGENTS.md`, `SOUL.md`, `IDENTITY.md`, `USER.md`, `BOOTSTRAP.md`, and `MEMORY.md`) against the configured character budget after runtime filtering. Root `BOOTSTRAP.md` is excluded after workspace setup completes. It reports per-file raw vs. injected character counts, truncation percentage, truncation cause (`max/file` or `max/total`), and total injected characters as a fraction of the total budget. When files are truncated or near the limit, doctor prints tips for tuning `agents.defaults.bootstrapMaxChars` and `agents.defaults.bootstrapTotalMaxChars`. `USER.md` is the exception: it has a fixed 4,000-character cap that these settings cannot raise, so doctor names the cap and recommends compacting the file instead. See [User model](https://funcoding.ai/agents/openclaw/concepts/user-model/).

This includes files declared by the bundled `bootstrap-extra-files` hook when a fresh Gateway startup would select it, provided each matched basename is one of those six (for example, `packages/core/AGENTS.md`). Other basenames are ignored. Doctor uses each agent's workspace and limits without importing or running custom hook handlers. It predicts fresh-start selection, not the previous handler generation that a running Gateway can retain after a failed hook reload.

</details>

<details>
<summary>11c. Shell completion</summary>

Doctor checks whether tab completion is installed for the current shell (zsh, bash, fish, or PowerShell):

- If the shell profile uses a slow dynamic completion pattern (`source <(openclaw completion ...)`), doctor upgrades it to the faster cached file variant.
- If completion is configured in the profile but the cache file is missing, doctor regenerates the cache automatically.
- If no completion is configured at all, doctor prompts to install it (interactive mode only; skipped with `--non-interactive`).

Run `openclaw completion --write-state` to regenerate the cache manually.

</details>

<details>
<summary>11d. Stale channel plugin cleanup</summary>

When `openclaw doctor --fix` removes a missing channel plugin, it also removes the dangling channel-scoped config that referenced that plugin: `channels.<id>` entries, heartbeat targets that named the channel, and `agents.*.models["<channel>/*"]` overrides. This prevents Gateway boot loops where the channel runtime is gone but config still asks the gateway to bind to it.

</details>

<details>
<summary>11e. Project clone shape</summary>

Doctor checks stored project clones registered with `source: "cloned"`. It
reports the project name and path, shallow state, every
`remote.*.partialclonefilter` and `remote.*.promisor` key (including URL-keyed
twins), and `extensions.partialclone` when present. Address-like names replace
userinfo with `***` and omit query strings and fragments, including remote-helper
and scp-like addresses that are not standard URLs. Full clones produce no
finding. Missing or unreadable clones are reported as skipped; other projects
are still checked. Agent workspaces and manually registered checkouts are
outside this check.

Each clone uses three local Git reads, each limited to five seconds and
64 KiB of captured output. Doctor does not fetch, repack, or change clone
configuration, even with `--fix`. Partial clones support managed-worktree
object prefetch, but full clones avoid missing-object and shallow-history
failures during checkout and snapshot restore.

For a focused read-only report, run:

```bash
openclaw doctor --lint --only core/doctor/project-clone-shape --json
```

The check also runs in ordinary Doctor and `--lint --all`; it is excluded
from the default lint profile. Lint inspects the registry through Doctor's
private state snapshot.

Follow Doctor's printed commands in a POSIX shell with access to `origin`.
Stop on any failed step. For a shallow partial clone with the usual origin
keys, the sequence is:

```bash
cd /path/to/project-clone
git config --unset-all remote.origin.partialclonefilter
git fetch --refetch --unshallow origin
git rev-list --objects --missing=print --all | grep '^?' | cut -c2- | git fetch origin --no-tags --no-write-fetch-head --recurse-submodules=no --stdin
git config --unset-all remote.origin.promisor
git repack -a -d
```

Unset **every** reported `partialclonefilter` key before refetching, including
keys such as `remote.https://github.com/openclaw/openclaw.git.partialclonefilter`.
Omit `--unshallow` if the repository is not shallow; Git rejects that option
for complete history. Keep promisor settings until missing objects have been
fetched by ID, then unset every reported `promisor` key and, if present,
`extensions.partialclone` before repacking. Doctor prints exact unset commands
for plain remote names without `://`, `::`, or `@`. For address-like entries, follow the local lookup and
unset instructions before continuing; the original key may contain credentials
and must not be copied into shared reports. The redacted name identifies the
entry but is not its literal Git config key.

Git can retain `objects/pack/*.promisor` sidecars after the remote keys are
unset and the repository is repacked. The sidecars are harmless: OpenClaw
determines partial-clone repair availability from Git configuration, not from
those files. To remove the stale on-disk label, first confirm that the missing
object command below prints nothing and that `git fsck` succeeds:

```bash
git rev-list --objects --missing=print --all | sed -n 's/^?//p'
git fsck --full
```

Then move only the sidecars out of the pack directory, keeping them as a
recoverable backup until the next successful Doctor and managed-worktree run:

```bash
promisor_marker_backup="$(git rev-parse --git-dir)/retired-promisor-markers"
mkdir -p "$promisor_marker_backup"
find "$(git rev-parse --git-dir)/objects/pack" -maxdepth 1 -type f -name '*.promisor' -exec mv -n {} "$promisor_marker_backup"/ \;
git fsck --full
```

Rerun Doctor afterward. If history or objects remain missing, recover them
from the original repository. Origin may not contain local-only snapshots;
see [snapshot restore](https://funcoding.ai/agents/openclaw/concepts/managed-worktrees/#snapshots-cleanup-and-restore).

</details>

<details>
<summary>12. Gateway auth checks (local token)</summary>

Doctor checks local gateway token auth readiness.

- If token mode needs a token and no token source exists, doctor offers to generate one.
- If `gateway.auth.token` is SecretRef-managed but unavailable, doctor warns and does not overwrite it with plaintext.
- `openclaw doctor --generate-gateway-token` reports when a healthy SecretRef makes generation unnecessary.
- If a store-backed token resolves to a known redaction placeholder, `--fix` or `--generate-gateway-token` verifies a database backup and regenerates that entry while preserving its SecretRef. Doctor prints the backup path and restart/re-pair guidance. Other external secrets require replacement at their source.

</details>

<details>
<summary>12b. Read-only SecretRef-aware repairs</summary>

Some repair flows need to inspect configured credentials without weakening runtime fail-fast behavior.

- `openclaw doctor --fix` uses the same read-only SecretRef summary model as status-family commands for targeted config repairs.
- Example: Telegram `allowFrom` / `groupAllowFrom` `@username` repair tries to use configured bot credentials when available.
- If the Telegram bot token is configured via SecretRef but unavailable in the current command path, doctor reports that the credential is configured-but-unavailable and skips auto-resolution instead of crashing or misreporting the token as missing.

</details>

<details>
<summary>13. Gateway health check + restart</summary>

Guided Doctor runs a health check and can offer recovery for a local Gateway, subject to service ownership and confirmation. A failed remote health check does not trigger local service recovery, even when the remote URL is a loopback SSH tunnel. Check the remote connection and recover the Gateway on its host. Accepted interactive maintenance resumes the matching service it stopped and preserves an already-stopped service. Explicit repair maintenance resumes the matching service it stopped. After successful standalone `openclaw doctor --fix`, it also starts and verifies an already-stopped managed Gateway whose service targets the current installation. Update-time Doctor leaves activation with the updater. A loaded, enabled macOS job between respawns is not treated as an intentionally stopped service.

</details>

<details>
<summary>13b. Memory search readiness</summary>

Doctor checks whether the configured memory search embedding provider is ready for the default agent. The behavior depends on the configured provider:

- **Explicit local provider**: checks for a local model file or a recognized remote/downloadable model URL. If missing, suggests switching to a remote provider.
- **Explicit remote provider** (`openai`, `voyage`, etc.): verifies an API key is present in the environment or auth store. Prints actionable fix hints if missing.
- **Legacy auto provider**: treats `memorySearch.provider: "auto"` as OpenAI, checks OpenAI readiness, and `doctor --fix` rewrites it to `provider: "openai"`.

When a cached gateway check result is available (gateway was healthy at the time of the check), doctor cross-references its result with the CLI-visible config and notes any discrepancy. Doctor does not start a fresh embedding ping on the default path; use the deep memory status command when you want a live provider check.

Use `openclaw memory status --deep` to verify embedding readiness at runtime.

Embedding-provider readiness is a health check, not a state migration. Gateway startup does not initialize memory embedding providers during readiness checks, so auth-profile SecretRefs can activate afterward. If embeddings remain unavailable, memory sync preserves an existing semantic index rather than replacing it with FTS-only data. Readiness warnings allow degraded startup; errors that leave required state unsafe to read still block Gateway readiness.

</details>

<details>
<summary>14. Channel status warnings</summary>

If the gateway is healthy, doctor runs a channel status check and reports warnings with suggested fixes.

</details>

<details>
<summary>15. Supervisor config audit + repair</summary>

Plain Doctor inspection checks the installed supervisor config (launchd/systemd/schtasks) for missing or outdated defaults (for example systemd network-online dependencies and restart delay) and can offer an interactive repair. Explicit repair maintenance skips this separate service-rewrite phase, but reconciles [eligible installation drift in a previously running service](https://funcoding.ai/agents/openclaw/cli/doctor/recovery/#gateway-service-recovery) through the native installer. Stopped services keep their launcher and stop state. Run `openclaw gateway install --force` from the intended installation to replace the launcher and managed environment.

Before a Linux maintenance stop, policy refresh backs up outdated OpenClaw unit settings, confirms `daemon-reload`, and verifies that the effective `TimeoutStopSec` covers drain and cleanup (currently 330 seconds). This refresh preserves the launcher, environment, and operator drop-ins; an offline service can receive it without being started. A short or unknown resident shutdown budget uses bounded lifecycle drain before stopping. At the update deadline, reported write custody refuses the stop with its owner phase; remaining admitted turns or unknown custody produce a warning and the stop proceeds.

Notes:

- An older installation marker does not make custom native settings safe to replace. Automatic reconciliation repairs only missing or recognized released defaults; unrecognized base-unit, LaunchAgent, or Scheduled Task settings remain unchanged with a diagnostic. Linux operator drop-ins are never rewritten.
- On macOS, reconciliation recognizes the complete default PATH generated by the 2026.4.29 installer and refreshes it to the current service PATH after backing up the definition. Added custom entries and other unrecognized values remain protected, including edits inside the generated environment file. If optional user-bin directories have changed since installation, the old PATH remains unrecognized rather than being discarded.
- If backup or migration subprocess cleanup cannot confirm that owned work stopped, its write-custody blocker remains for the Gateway process lifetime, even after the command reports an error. Automatic maintenance does not clear it on a timeout. The deployment owner must independently verify that the work stopped before replacing the Gateway process.
- Before service-repair consent, Doctor previews the planned runtime, entrypoint, PATH, and any recorded shutdown-budget repair. The preview names runtime and source-checkout findings the repair will retain, and repeats them after an approved repair. If no supported system Node is available, reinstall can still repair other service settings while retaining the recorded runtime; install a supported system Node and rerun Doctor to resolve that finding. A source-checkout launcher is retained when it remains the intended installation.
- `openclaw doctor` prompts before rewriting supervisor config. `openclaw doctor --force` alone remains guided: it allows aggressive repair choices but still requires interactive consent for an eligible service rewrite. It enters maintenance only after guided custody consent and keeps ownership and write-access checks.
- `openclaw doctor --yes` accepts default non-service repair prompts and enters maintenance under the policy-refresh and installation-drift rules above.
- `openclaw doctor --fix` applies recommended repairs without prompts (`--repair` is an alias; `--yes` also enters repair maintenance). It stops the matching managed Gateway before plugin or mutable-state inspection, verifies repairs, and restarts the same service once, even when no changes are needed. It reconciles eligible installation drift, preserves operator launchers, starts only an owned offline service targeting the current installation, and refuses to stop an ancestor Gateway. Ordinary interactive Doctor checks schema compatibility and offers an update before asking for maintenance consent for remaining repairs and keeps individual repair prompts; declining custody skips repairs and continues read-only `--lint` checks in the same invocation, leaving the service and persisted state unchanged. Custom state directories do not adopt native services.
- Explicit repair refuses unavailable service inspection and unmatched services that may still run. After their owner stops them and the native manager confirms they are offline, Doctor repairs its selected state without changing or starting those services. A disabled systemd unit can still be restarting; Doctor checks runtime state as well as installation state.
- An updater's explicit Gateway activation policy leaves stop/restart ownership with the updater. Doctor still requires native proof that the service is offline; a live `update --no-restart` repair fails without stopping or restarting it. Stop the service through its owner before retrying the update. Older update parents without that policy retain ordinary Doctor maintenance.
- `openclaw doctor --fix --force` uses the same policy-refresh and installation-drift rules. Use `openclaw gateway install --force` to request a rewrite; operator-owned systemd drop-ins remain unchanged.
- `OPENCLAW_SERVICE_REPAIR_POLICY=external` keeps doctor read-only for gateway service lifecycle. Have the deployment owner stop the Gateway, run Doctor as the state-owning account, then restart through that owner. The policy skips native maintenance inspection and service mutations, including install/start/restart/bootstrap, supervisor config rewrites, and legacy service cleanup. It keeps Gateway/state coordinators and agent-database lease checks, reports service health, and runs non-service repairs. See [Existing system LaunchDaemons](https://funcoding.ai/agents/openclaw/gateway/#existing-system-launchdaemons).
- Doctor and `gateway status --deep` name unavailable launchd domains, missing systemd user-session buses, and native check access denial separately. Linux guidance covers `XDG_RUNTIME_DIR`, `DBUS_SESSION_BUS_ADDRESS`, and `dbus-user-session`; externally supervised deployments receive the existing policy above. See [Gateway and service recovery](https://funcoding.ai/agents/openclaw/cli/doctor/recovery/).
- Within a live Linux service inspection, OpenClaw can retain the authenticated user manager's private connection if its session bus stops. Typed command and runtime reads continue only for that original manager and unit. A missing initial manager identity or a replaced manager remains unavailable; OpenClaw does not start the bus or select another manager. This read-only recovery does not change service start/stop authority.

- On macOS, a same-label system LaunchDaemon blocks user LaunchAgent install, start, restart, and bootstrap repair. Doctor reports the system owner and stops service recovery; `--force` does not bypass this ownership boundary. See [Existing system LaunchDaemons](https://funcoding.ai/agents/openclaw/gateway/#existing-system-launchdaemons).
- On Linux, doctor does not rewrite command/entrypoint metadata while the matching systemd gateway unit is active. If a stopped unit's command or working directory is overridden by an operator-owned systemd drop-in, inspect it with `systemctl --user cat <unit>.service`, then update or remove the drop-in; rewriting the managed base cannot change the effective launcher. `Environment=` drop-ins remain supported. Doctor also ignores inactive non-legacy extra gateway-like units during the duplicate-service scan so companion service files do not create cleanup noise.
- On Linux, doctor checks authority over the installed and planned service files before persisting a recovered gateway token. If that check blocks service repair, the repair leaves config and token unchanged and reports how to restore inspection access or involve the deployment owner; `--force` cannot bypass it. Unrelated Doctor config repairs are unaffected.
- When service repair must preserve a token recovered from the environment or the old service, Doctor asks for explicit interactive confirmation and defaults to No. It reuses an identical shared-store entry without changing it, or saves a new protected entry through the secret-store owner, then writes only its SecretRef to `gateway.auth.token`. Non-interactive runs, including `--yes`, skip this step with a warning. Update-time Doctor still defers service repair to update finalization.
- Before adding a store entry, Doctor backs up an existing state database; its normal config writer backs up the config before publication. If config publication fails, Doctor skips the service rewrite and retains the saved entry because a committed or concurrent reference may already use it. Resolve the reported failure and rerun Doctor to reuse that entry; do not remove it while references are uncertain.
- If token auth requires a token and `gateway.auth.token` is SecretRef-managed, doctor service install/repair validates the SecretRef but does not persist resolved plaintext token values into supervisor service environment metadata.
- Doctor detects managed `.env`/SecretRef-backed service environment values that older LaunchAgent, systemd, or Windows Scheduled Task installs embedded inline and rewrites the service metadata so those values load from the runtime source instead of the supervisor definition.
- Doctor detects when the service command still pins an old `--port` after `gateway.port` changes and rewrites the service metadata to the current port.
- If token auth requires a token and the configured token SecretRef is unresolved, doctor blocks the install/repair path with actionable guidance.
- If both `gateway.auth.token` and `gateway.auth.password` are configured and `gateway.auth.mode` is unset, doctor blocks install/repair until mode is set explicitly.
- For Linux user-systemd units, doctor token drift checks include both `Environment=` and `EnvironmentFile=` sources when comparing service auth metadata.
- `OPENCLAW_SERVICE_MANAGED_ENV_KEYS` tracks values OpenClaw regenerates from config, `.env`, or SecretRefs; it is not an operator preservation allowlist. Systemd merges operator `Environment=` and `EnvironmentFile=` drop-ins into the effective environment, and `gateway install --force` leaves those drop-ins and their files unchanged. Doctor does not copy their values into the managed base or suggest reinstalling to migrate operator-only managed keys. Unreadable or unverified drop-ins still require inspection. Linux reinstall preserves admitted PATH entries in their existing order, including safe extra directories; missing concrete managed values still report drift. Environment preservation failures show bounded current/installer values for PATH and the Telegram spool timeout, with other values redacted.
- Doctor service repairs refuse to rewrite, stop, or restart a gateway service from an older OpenClaw binary when the config was last written by a newer version. See [Gateway troubleshooting](https://funcoding.ai/agents/openclaw/gateway/troubleshooting/#split-brain-installs-and-newer-config-guard).
- `openclaw gateway install --force` rewrites the managed base unit, but never removes operator-owned systemd drop-ins; it warns if a command or working-directory override remains effective.

</details>

<details>
<summary>16. Gateway runtime + port diagnostics</summary>

Doctor inspects the service runtime (PID, last exit status) and warns when the service is installed but not actually running. It also checks for port collisions on the gateway port (default `18789`) and reports likely causes (gateway already running, SSH tunnel).

</details>

<details>
<summary>17. Gateway runtime best practices</summary>

Doctor accepts Bun 1.4+ runtimes that provide WAL-reset-safe `node:sqlite` and warns when the gateway service runs on an older or unsafe Bun or a version-managed Node path (`nvm`, `fnm`, `volta`, `asdf`, etc.). Supported Bun services, whether recorded or pinned, are retained. A pinned runtime is never migrated. Only an unpinned, unsupported Bun is offered migration to a supported system Node, with interactive approval. Update-driven Doctor runs never migrate the runtime. If no supported Node is available, the service stays on Bun and Doctor warns. Version-manager paths can break after upgrades because the service does not load your shell init. Doctor can offer to migrate an unpinned version-managed Node to a supported system Node install (Homebrew/apt/choco).

When reinstalling an existing but unloaded service without a wrapper or runtime
pin, Doctor defaults its runtime picker to the supported recorded Node or Bun.
Keeping that runtime retains its executable without creating a pin; choosing
the other runtime selects a new executable. An unavailable or unsupported
recorded runtime falls back to the fresh-install default of Node, or the running supported Bun without creating a pin when no supported Node is found and no explicit runtime, pin, or wrapper is set.

Doctor uses that same suggestion for its non-interactive runtime fallback.
Choosing Node explicitly still fails before installation if no supported Node
is available; it does not silently substitute Bun. This does not change when
Doctor requires confirmation to install a service.

Explicit runtime-path pins are retained during service repair.
Doctor still checks their runtime capabilities, but does not migrate a valid
pin away from a version manager. Replace or remove an invalid pin with
`openclaw gateway install --runtime-path <path> --force` or
`openclaw gateway install --runtime node --force`.

Service installation and repair recognize current Node executables named `node`, `nodejs`, or versioned names such as `node24` and `node-24`, including Windows `.exe` variants. Each candidate still has to pass the Node and SQLite capability checks before selection.

Newly installed or repaired macOS LaunchAgents use a canonical system PATH (`/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin`) instead of copying the interactive shell PATH, so Homebrew-managed system binaries stay available while Volta, asdf, fnm, pnpm, and other version-manager directories do not change which Node child processes resolve. Linux services still keep explicit environment roots (`NVM_DIR`, `FNM_DIR`, `VOLTA_HOME`, `ASDF_DATA_DIR`, `BUN_INSTALL`, `PNPM_HOME`) and stable user-bin directories, but guessed version-manager fallback directories are only written to the service PATH when those directories exist on disk.

</details>
