fix(secretrefs): resolve external channel contracts#76449
Conversation
|
Codex review: found issues before merge. Summary Reproducibility: yes. Source inspection gives high-confidence current-main paths for the bundled-only channel contract lookup, BlueBubbles SecretRef Next step before merge Security Review findings
Review detailsBest possible solution: Land this PR after maintainer review, a changelog entry, and changed-gate proof, then let it close the linked regressions and supersede the narrower overlapping PRs. Do we have a high-confidence way to reproduce the issue? Yes. Source inspection gives high-confidence current-main paths for the bundled-only channel contract lookup, BlueBubbles SecretRef Is this the best way to solve the issue? Yes, mostly. The generic external channel contract loader plus owner-local guards follows the OpenClaw boundary better than channel-specific core fixes; the missing changelog is the remaining mechanical merge blocker I found. Full review comments:
Overall correctness: patch is correct What I checked:
Likely related people:
Remaining risk / open question:
Codex review notes: model gpt-5.5, reasoning high; reviewed against a7d3da71f79b. |
630e5a4 to
7ec0dbe
Compare
Externalized channel plugins published to npm (e.g. @openclaw/discord since 2026.5.2) keep their compiled secret-contract-api artifact under <rootDir>/dist/, per the package.json `openclaw.runtimeExtensions` convention. The runtime contract loader added in openclaw#76449 only searched the rootDir, so npm-installed plugins silently dropped their channel SecretRef contracts: the runtime snapshot left `channels.<id>.token` as an unresolved SecretRef, the plugin's `isConfigured` check then returned false, and the gateway recorded `error: not configured` without firing the usual channel startup logs. Look in `<rootDir>/dist/` as well as `<rootDir>/`, preferring dist when running from a built openclaw artifact and rootDir when running from source. The new `loads dist/ secret-contract-api sidecars …` test in channel-contract-api.external.test.ts mirrors the real npm-package layout and fails without this change. Refs openclaw#76371. Fixes openclaw#77416.
…#77421) * fix(secretrefs): resolve external channel contracts in dist/ sidecars Externalized channel plugins published to npm (e.g. @openclaw/discord since 2026.5.2) keep their compiled secret-contract-api artifact under <rootDir>/dist/, per the package.json `openclaw.runtimeExtensions` convention. The runtime contract loader added in #76449 only searched the rootDir, so npm-installed plugins silently dropped their channel SecretRef contracts: the runtime snapshot left `channels.<id>.token` as an unresolved SecretRef, the plugin's `isConfigured` check then returned false, and the gateway recorded `error: not configured` without firing the usual channel startup logs. Look in `<rootDir>/dist/` as well as `<rootDir>/`, preferring dist when running from a built openclaw artifact and rootDir when running from source. The new `loads dist/ secret-contract-api sidecars …` test in channel-contract-api.external.test.ts mirrors the real npm-package layout and fails without this change. Refs #76371. Fixes #77416. * docs: credit changelog contributor --------- Co-authored-by: Magpie <magpie@local> Co-authored-by: joshavant <[email protected]>
…openclaw#77421) * fix(secretrefs): resolve external channel contracts in dist/ sidecars Externalized channel plugins published to npm (e.g. @openclaw/discord since 2026.5.2) keep their compiled secret-contract-api artifact under <rootDir>/dist/, per the package.json `openclaw.runtimeExtensions` convention. The runtime contract loader added in openclaw#76449 only searched the rootDir, so npm-installed plugins silently dropped their channel SecretRef contracts: the runtime snapshot left `channels.<id>.token` as an unresolved SecretRef, the plugin's `isConfigured` check then returned false, and the gateway recorded `error: not configured` without firing the usual channel startup logs. Look in `<rootDir>/dist/` as well as `<rootDir>/`, preferring dist when running from a built openclaw artifact and rootDir when running from source. The new `loads dist/ secret-contract-api sidecars …` test in channel-contract-api.external.test.ts mirrors the real npm-package layout and fails without this change. Refs openclaw#76371. Fixes openclaw#77416. * docs: credit changelog contributor --------- Co-authored-by: Magpie <magpie@local> Co-authored-by: joshavant <[email protected]>
…openclaw#77421) * fix(secretrefs): resolve external channel contracts in dist/ sidecars Externalized channel plugins published to npm (e.g. @openclaw/discord since 2026.5.2) keep their compiled secret-contract-api artifact under <rootDir>/dist/, per the package.json `openclaw.runtimeExtensions` convention. The runtime contract loader added in openclaw#76449 only searched the rootDir, so npm-installed plugins silently dropped their channel SecretRef contracts: the runtime snapshot left `channels.<id>.token` as an unresolved SecretRef, the plugin's `isConfigured` check then returned false, and the gateway recorded `error: not configured` without firing the usual channel startup logs. Look in `<rootDir>/dist/` as well as `<rootDir>/`, preferring dist when running from a built openclaw artifact and rootDir when running from source. The new `loads dist/ secret-contract-api sidecars …` test in channel-contract-api.external.test.ts mirrors the real npm-package layout and fails without this change. Refs openclaw#76371. Fixes openclaw#77416. * docs: credit changelog contributor --------- Co-authored-by: Magpie <magpie@local> Co-authored-by: joshavant <[email protected]>
…openclaw#77421) * fix(secretrefs): resolve external channel contracts in dist/ sidecars Externalized channel plugins published to npm (e.g. @openclaw/discord since 2026.5.2) keep their compiled secret-contract-api artifact under <rootDir>/dist/, per the package.json `openclaw.runtimeExtensions` convention. The runtime contract loader added in openclaw#76449 only searched the rootDir, so npm-installed plugins silently dropped their channel SecretRef contracts: the runtime snapshot left `channels.<id>.token` as an unresolved SecretRef, the plugin's `isConfigured` check then returned false, and the gateway recorded `error: not configured` without firing the usual channel startup logs. Look in `<rootDir>/dist/` as well as `<rootDir>/`, preferring dist when running from a built openclaw artifact and rootDir when running from source. The new `loads dist/ secret-contract-api sidecars …` test in channel-contract-api.external.test.ts mirrors the real npm-package layout and fails without this change. Refs openclaw#76371. Fixes openclaw#77416. * docs: credit changelog contributor --------- Co-authored-by: Magpie <magpie@local> Co-authored-by: joshavant <[email protected]>
…openclaw#77421) * fix(secretrefs): resolve external channel contracts in dist/ sidecars Externalized channel plugins published to npm (e.g. @openclaw/discord since 2026.5.2) keep their compiled secret-contract-api artifact under <rootDir>/dist/, per the package.json `openclaw.runtimeExtensions` convention. The runtime contract loader added in openclaw#76449 only searched the rootDir, so npm-installed plugins silently dropped their channel SecretRef contracts: the runtime snapshot left `channels.<id>.token` as an unresolved SecretRef, the plugin's `isConfigured` check then returned false, and the gateway recorded `error: not configured` without firing the usual channel startup logs. Look in `<rootDir>/dist/` as well as `<rootDir>/`, preferring dist when running from a built openclaw artifact and rootDir when running from source. The new `loads dist/ secret-contract-api sidecars …` test in channel-contract-api.external.test.ts mirrors the real npm-package layout and fails without this change. Refs openclaw#76371. Fixes openclaw#77416. * docs: credit changelog contributor --------- Co-authored-by: Magpie <magpie@local> Co-authored-by: joshavant <[email protected]>
…openclaw#77421) * fix(secretrefs): resolve external channel contracts in dist/ sidecars Externalized channel plugins published to npm (e.g. @openclaw/discord since 2026.5.2) keep their compiled secret-contract-api artifact under <rootDir>/dist/, per the package.json `openclaw.runtimeExtensions` convention. The runtime contract loader added in openclaw#76449 only searched the rootDir, so npm-installed plugins silently dropped their channel SecretRef contracts: the runtime snapshot left `channels.<id>.token` as an unresolved SecretRef, the plugin's `isConfigured` check then returned false, and the gateway recorded `error: not configured` without firing the usual channel startup logs. Look in `<rootDir>/dist/` as well as `<rootDir>/`, preferring dist when running from a built openclaw artifact and rootDir when running from source. The new `loads dist/ secret-contract-api sidecars …` test in channel-contract-api.external.test.ts mirrors the real npm-package layout and fails without this change. Refs openclaw#76371. Fixes openclaw#77416. * docs: credit changelog contributor --------- Co-authored-by: Magpie <magpie@local> Co-authored-by: joshavant <[email protected]>
Summary
Change Type (select all)
Scope (select all touched areas)
Linked Issue/PR
Root Cause (if applicable)
plugins.entries, external plugin sidecar secret contracts, or raw SecretRef values reaching late webhook/tool/status paths.Regression Test Plan (if applicable)
src/secrets/channel-contract-api.external.test.ts,src/secrets/runtime-external-channel-origin-discovery.test.ts,extensions/telegram/src/accounts.test.ts,extensions/bluebubbles/src/monitor.webhook-auth.test.ts,src/agents/tools/web-tools.enabled-defaults.test.ts.extensions/discord/src/setup-account-state.test.tsalready covered unresolved Discord setup-state SecretRefs and now keeps that behavior explicit.User-visible / Behavior Changes
SecretRef-backed channel credentials in externalized plugins resolve through the active runtime snapshot again. BlueBubbles webhook auth rejects unresolved SecretRef passwords without crashing, disabled Telegram accounts are skipped before token resolution, and sub-agent late-bound web_search uses the active runtime config.
Diagram (if applicable)
Security Impact (required)
Yes, explain risk + mitigation: the loader now reads secret contract sidecars from loadable external channel plugin roots. It uses plugin metadata ownership checks, loadable-origin filtering where available, root boundary checks, and hardlink rejection for non-bundled plugin files.Repro + Verification
Environment
Steps
pnpm check:changedafter the fix.Expected
Actual
config: undefined; Telegram resolved a disabled account SecretRef; BlueBubbles called.trim()on a SecretRef object; generic channel contract tests failed becauseloadChannelSecretContractApidid not exist and runtime still used the bundled-only loader.pnpm check:changedin Testbox after rebasing onto currentorigin/main.Evidence
Human Verification (required)
pnpm check:changedin Testbox.plugins.entriesstill discovers plugin origins.Review Conversations
Compatibility / Migration
Risks and Mitigations