You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
#149 established that scripts/gateway-pre-start.sh must preserve strong gateway.auth.token shapes, explicitly including "a SecretRef object". The shipped predicate handles strong strings and ${ENV_VAR} interpolations correctly, but its SecretRef-object branch checks for the wrong dict shape — so the SecretRef object shape OpenClaw actually stores is classified as weak and rotated to a fresh random token on the next gateway restart.
Steps to reproduce
Current behavior
is_strong_gateway_token in scripts/gateway-pre-start.sh treats a dict as strong when it contains a key namedenv, file, or exec:
Its keys are source, provider, and id — none of which match the predicate's tuple. Result:
Operator migrates gateway.auth.token to a SecretRef via openclaw config set ... --ref-provider ... --ref-source ... --ref-id ... (the same supported path already used for Telegram bot tokens).
Gateway restarts; pre-start classifies the ref object as weak and rewrites the field to secrets.token_hex(32).
The migration is silently undone (config is back to a plaintext literal), and every client is locked out because the new random token is held by nobody.
src/tests/unit/gateway-pre-start-token.test.ts extracts and exercises the real predicate, but its dict cases cover the key-style shape ({"exec": ...} etc.) that OpenClaw does not write, so the suite passes while the canonical shape clobbers.
Expected behavior
Per #149, all of the following should be preserved:
strong plain strings (existing, correct)
${ENV_VAR} interpolations (existing, correct)
SecretRef objects in the shape OpenClaw actually stores: {source, provider, id} with source in env / file / exec (missing)
Why this matters
This is the same failure class #149 fixed for the ${ENV} shape, on the shape the current OpenClaw CLI writes. Any operator who follows the supported openclaw config set --ref-* path for the gateway token gets an immediate, silent revert-plus-lockout on the next restart — worse than the pre-#149 state, because the rotation replaces a working strong token with one no client holds. It also means the only SecretRef style that survives pre-start today is the env-interpolation string, which pushes operators back toward the more fragile env-based approach #149/#150 were moving away from.
Proposed fix
Extend the predicate to accept the canonical shape, keeping the existing branches for compatibility:
ifisinstance(v, dict):
# Canonical OpenClaw SecretRef: {source, provider, id}ifv.get("source") in ("env", "file", "exec") and \
isinstance(v.get("provider"), str) andv.get("provider").strip() and \
isinstance(v.get("id"), str) andv.get("id").strip():
returnTrue# Legacy variant without provider: {source, id}ifv.get("source") in ("env", "file", "exec") and"provider"notinvand \
isinstance(v.get("id"), str) andv.get("id").strip():
returnTrue# Key-style shape (pre-existing behavior, kept for compatibility)returnany(kinvforkin ("env", "file", "exec"))
Extend gateway-pre-start-token.test.ts with canonical-shape cases:
Found while preparing a bounded SecretRef migration for gateway.auth.token on my host, cross-checking the predicate against upstream openclaw/openclawsrc/config/types.secrets.ts. The ${ENV} and strong-string branches were verified correct against the committed test; only the object-shape branch mismatches. Happy to provide the working patch as a starting point.
What happened?
Summary
#149 established that
scripts/gateway-pre-start.shmust preserve stronggateway.auth.tokenshapes, explicitly including "a SecretRef object". The shipped predicate handles strong strings and${ENV_VAR}interpolations correctly, but its SecretRef-object branch checks for the wrong dict shape — so the SecretRef object shape OpenClaw actually stores is classified as weak and rotated to a fresh random token on the next gateway restart.Steps to reproduce
Current behavior
is_strong_gateway_tokeninscripts/gateway-pre-start.shtreats a dict as strong when it contains a key namedenv,file, orexec:OpenClaw's canonical stored SecretRef shape (
src/config/types.secrets.tsupstream, enforced byisSecretRef) is:{ "source": "env" | "file" | "exec", "provider": "<name>", "id": "<id>" }Its keys are
source,provider, andid— none of which match the predicate's tuple. Result:gateway.auth.tokento a SecretRef viaopenclaw config set ... --ref-provider ... --ref-source ... --ref-id ...(the same supported path already used for Telegram bot tokens).secrets.token_hex(32).src/tests/unit/gateway-pre-start-token.test.tsextracts and exercises the real predicate, but its dict cases cover the key-style shape ({"exec": ...}etc.) that OpenClaw does not write, so the suite passes while the canonical shape clobbers.Expected behavior
Per #149, all of the following should be preserved:
${ENV_VAR}interpolations (existing, correct){source, provider, id}withsourceinenv/file/exec(missing)Why this matters
This is the same failure class #149 fixed for the
${ENV}shape, on the shape the current OpenClaw CLI writes. Any operator who follows the supportedopenclaw config set --ref-*path for the gateway token gets an immediate, silent revert-plus-lockout on the next restart — worse than the pre-#149 state, because the rotation replaces a working strong token with one no client holds. It also means the only SecretRef style that survives pre-start today is the env-interpolation string, which pushes operators back toward the more fragile env-based approach #149/#150 were moving away from.Proposed fix
gateway-pre-start-token.test.tswith canonical-shape cases:{source: "exec", provider: "x", id: "y"}→ strong (preserve){source: "env", provider: "default", id: "OPENCLAW_GATEWAY_TOKEN"}→ strong (preserve){source: "exec", id: "y"}(no provider) → strong (preserve){source: "bogus", provider: "x", id: "y"}→ weak (rotate){}and{source: "exec"}(missing id) → weak (rotate)gateway.auth.tokenretains its pre-restart shape class (literal stays literal, ref stays ref), so any future predicate regression surfaces at update time rather than as a lockout.Notes
Found while preparing a bounded SecretRef migration for
gateway.auth.tokenon my host, cross-checking the predicate against upstreamopenclaw/openclawsrc/config/types.secrets.ts. The${ENV}and strong-string branches were verified correct against the committed test; only the object-shape branch mismatches. Happy to provide the working patch as a starting point.ClawBox version
3.1.3
OpenClaw version
2026.6.1
Jetson model / JetPack
Jetson Orin Nano
Logs and screenshots