Summary
openclaw gateway install --force resolves every SecretRef in openclaw.json and writes the resolved values as plaintext into the macOS LaunchAgent plist (~/Library/LaunchAgents/ai.openclaw.gateway.plist).
This defeats the purpose of moving secrets out of openclaw.json and into ~/.openclaw/.env using ${ENV:...} references.
It also appears to cause a confusing warning loop where openclaw doctor --fix and openclaw gateway restart report:
Config token differs from service token. Run openclaw gateway install --force to sync the token
…but running install --force just re-embeds the resolved values into the plist again.
Why this is a problem
- Secrets that were intentionally externalized to
~/.openclaw/.env are copied back into a persistent service definition as plaintext.
- This undermines the security/privacy model of
SecretRef usage in openclaw.json.
- It creates a circular remediation path:
- OpenClaw warns that the service token differs from the config token.
- The suggested fix is
openclaw gateway install --force.
- That command hardcodes the resolved secret values into the LaunchAgent plist.
- The warning can still persist even though the gateway itself is healthy.
In practice, the gateway continues to work (RPC probe: ok), so the mismatch warning appears functionally cosmetic but still sends the user into an infinite loop of “fix” steps.
Steps to reproduce
- Move secrets from
openclaw.json into ~/.openclaw/.env.
- Use
SecretRef / env references in openclaw.json, e.g.:
{
"gateway": {
"token": "${ENV:OPENCLAW_GATEWAY_TOKEN}"
}
}
- Run:
openclaw gateway install --force
- Inspect the LaunchAgent plist:
plutil -p ~/Library/LaunchAgents/ai.openclaw.gateway.plist
- Observe that all resolved secrets (for example
OPENCLAW_GATEWAY_TOKEN, ANTHROPIC_API_KEY, TELEGRAM_BOT_TOKEN, etc.) appear as plaintext values in the plist.
- Run:
- Observe the warning:
Config token differs from service token. Run openclaw gateway install --force to sync the token
- Repeat
install --force and/or doctor --fix; the warning can continue indefinitely.
Expected behavior
One of these should happen instead:
openclaw gateway install should preserve SecretRef indirection and configure the LaunchAgent to load environment values at runtime, rather than serializing the resolved values into the plist.
- The LaunchAgent should reference
~/.openclaw/.env (or another env source) so secrets remain external to the plist.
- The “config token differs from service token” check should compare effective/resolved values correctly and avoid warning when the runtime values already match.
Actual behavior
SecretRef values are resolved during gateway install --force.
- The resolved values are embedded into
~/Library/LaunchAgents/ai.openclaw.gateway.plist as plaintext.
- Subsequent restart/doctor flows can warn that the config token differs from the service token even though the gateway is functioning normally.
Environment
- OpenClaw:
2026.3.23 (ccfeecb)
- macOS:
26.3.1 (arm64)
- Node:
25.5.0
- Gateway service type: LaunchAgent
Suggested direction
This feels like both a security bug and a service-install/runtime parity bug.
A robust fix would likely be:
- keep secrets in
.env / external secret sources,
- have the LaunchAgent load them at runtime,
- and make the token consistency check compare effective values rather than the serialized plist representation.
Happy to provide sanitized examples of the generated plist if helpful.
Summary
openclaw gateway install --forceresolves everySecretRefinopenclaw.jsonand writes the resolved values as plaintext into the macOS LaunchAgent plist (~/Library/LaunchAgents/ai.openclaw.gateway.plist).This defeats the purpose of moving secrets out of
openclaw.jsonand into~/.openclaw/.envusing${ENV:...}references.It also appears to cause a confusing warning loop where
openclaw doctor --fixandopenclaw gateway restartreport:…but running
install --forcejust re-embeds the resolved values into the plist again.Why this is a problem
~/.openclaw/.envare copied back into a persistent service definition as plaintext.SecretRefusage inopenclaw.json.openclaw gateway install --force.In practice, the gateway continues to work (
RPC probe: ok), so the mismatch warning appears functionally cosmetic but still sends the user into an infinite loop of “fix” steps.Steps to reproduce
openclaw.jsoninto~/.openclaw/.env.SecretRef/ env references inopenclaw.json, e.g.:{ "gateway": { "token": "${ENV:OPENCLAW_GATEWAY_TOKEN}" } }plutil -p ~/Library/LaunchAgents/ai.openclaw.gateway.plistOPENCLAW_GATEWAY_TOKEN,ANTHROPIC_API_KEY,TELEGRAM_BOT_TOKEN, etc.) appear as plaintext values in the plist.install --forceand/ordoctor --fix; the warning can continue indefinitely.Expected behavior
One of these should happen instead:
openclaw gateway installshould preserveSecretRefindirection and configure the LaunchAgent to load environment values at runtime, rather than serializing the resolved values into the plist.~/.openclaw/.env(or another env source) so secrets remain external to the plist.Actual behavior
SecretRefvalues are resolved duringgateway install --force.~/Library/LaunchAgents/ai.openclaw.gateway.plistas plaintext.Environment
2026.3.23 (ccfeecb)26.3.1(arm64)25.5.0Suggested direction
This feels like both a security bug and a service-install/runtime parity bug.
A robust fix would likely be:
.env/ external secret sources,Happy to provide sanitized examples of the generated plist if helpful.