Skip to content

fix(plugins): resolve public artifacts from installed plugin roots#98819

Merged
amknight merged 7 commits into
mainfrom
fix/plugin-public-artifacts-installed-roots
Jul 4, 2026
Merged

fix(plugins): resolve public artifacts from installed plugin roots#98819
amknight merged 7 commits into
mainfrom
fix/plugin-public-artifacts-installed-roots

Conversation

@amknight

@amknight amknight commented Jul 2, 2026

Copy link
Copy Markdown
Member

Fixes #98740

What Problem This Solves

After the 2026.6.11 Mattermost plugin externalization, every native /oc_* slash command callback is rejected with 401 by the gateway before the plugin's handler runs. /api/channels/* is a protected plugin-route prefix (src/gateway/security-path.ts), and the only exemption — the channel's gateway-auth-api.js public artifact — was resolved by a bundled-only loader. [email protected] no longer ships dist/extensions/mattermost/** (externalized, bundledDist: false), so the bypass set resolved empty and gateway bearer auth was enforced against Mattermost's callbacks. Outbound registration and WebSocket DMs/@mentions were unaffected, matching the reporter's symptoms exactly.

Why This Change Was Made

The gateway-auth bypass consumer (src/channels/plugins/gateway-auth-bypass.ts) now resolves the artifact through the existing activation-gated facade seam (src/plugin-sdk/facade-runtime.ts) instead of the bundled-only public-surface loader. The consumer uses a new async twin (tryLoadActivatedBundledPluginPublicSurfaceModule) of the existing sync seam: the activation-check runtime is a lazy *.runtime.ts boundary whose sync createRequire/jiti candidate loader only resolves from built dist, so the async call site loads it via dynamic import() (works in both dist and source/vitest) and warms the shared memo for the sync consumers. Internal-only surface — facade-runtime is not a published SDK subpath, so no baseline/docs changes are involved.

  • The facade seam already resolves bundled locations first and falls back to the manifest-registry record of installed plugins (id, folder-basename, and channels-tier matching), which is exactly the externalized @openclaw/mattermost case — its published package ships dist/gateway-auth-api.js.
  • It is activation-gated: a plugin that is denied (plugins.deny), disabled (entries.<id>.enabled=false), or excluded by an allowlist returns null — no bypass paths and, critically, no execution of the disabled plugin's artifact code. Bypass paths grant unauthenticated ingress, so they follow plugin activation, not mere installation.
  • It throws the same Unable to resolve bundled plugin public surface sentinel for activated-but-artifactless channels, so the optional-artifact semantics of every other consumer are untouched.
  • In gateway runtime the registry comes from the pinned plugin-metadata snapshot, so no per-request filesystem scans or raw install-record reads enter the auth path.

src/plugin-sdk/facade-loader.ts additionally applies shouldRejectHardlinkedPluginFiles (src/plugins/hardlink-policy.ts) when the resolved boundary root is an installed plugin root (core-shipped/bundled roots keep hardlinks allowed, Nix store excepted). This closes a pre-existing gap in the facade seam that matters more now that a security-granting artifact resolves through it, and aligns it with the sibling installed-artifact loader in src/secrets/channel-contract-api.ts.

Deliberately not changed: the shared bundled-artifact loader (src/plugins/public-surface-loader.ts). An earlier draft of this PR added an install-record fallback there; an 8-angle review found that broadened all eight artifact surfaces (doctor/secret/message-tool/provider-policy/…) past their intended trust boundaries, executed disabled plugins' artifact code, let store-corruption errors escape into the gateway request path, and re-implemented the facade seam with drifting precedence. That draft was reverted in favor of this consumer-scoped fix. Externalized-plugin support for the other artifact surfaces is explicit follow-up work with per-surface trust review.

Scope note: a bundled channel that is configured but explicitly disabled now also stops contributing bypass paths (previously its artifact loaded regardless of enablement). A disabled plugin registers no routes, so those callbacks 404ed anyway — this only makes the auth surface consistent with plugin lifecycle.

User Impact

Mattermost native slash commands work again on packaged installs ≥2026.6.11 with the externalized @openclaw/mattermost plugin. Disabled/denied plugins can no longer contribute gateway auth bypass paths or have artifact code executed. No behavior change for source checkouts or enabled bundled plugins.

Evidence

Testsgateway-auth-bypass.test.ts re-pointed to the facade seam plus a new activation-blocked case (4/4); facade-loader.test.ts (7/7), facade-runtime.test.ts (17/17 — installed-package dist resolution, rootDir-basename matching, activation blocking, plus new hardlink-policy coverage: installed roots reject hardlinked artifacts, core-shipped roots keep loading them), public-surface-loader.test.ts restored to main (8/8). pnpm build clean (no INEFFECTIVE_DYNAMIC_IMPORT), pnpm check:changed green via Testbox.

Packaged repro matrix (real npm installs, isolated state dirs, gateway token auth, external plugin installed via openclaw plugins install @openclaw/mattermost; probe simulates the Mattermost callback — form-encoded POST, no gateway bearer):

POST /api/channels/mattermost/command

[email protected] (bundled mattermost):        503 plugin JSON  → reaches plugin (pre-regression)
[email protected] (externalized):              401 gateway JSON → the bug (plugin never reached)
[email protected], WITH gateway bearer:        503 plugin JSON  → route alive; only gateway auth blocks MM
this branch (OPENCLAW_DISABLE_BUNDLED_PLUGINS=1
  + external plugin install = packaged topology): 503 plugin JSON → bypass resolved from installed root
this branch, same topology + plugins.deny=
  ["mattermost"]:                                401 gateway JSON → deny removes bypass; log shows the
                                                 operator warning only, no artifact execution

Live Mattermost round-trip (Mattermost team edition 11.7.0 on a separate tailnet host, real bot, this branch in the externalized topology, commands registered by the monitor):

POST /api/v4/commands/execute {"command":"/oc_status"}   (Mattermost-initiated)
→ HTTP 200
→ {"response_type":"ephemeral","text":"Slash commands are not configured for this channel (no allowlist)."}

The MM server invoked the callback; the gateway admitted it via the activation-gated installed-root bypass, the plugin validated the payload token against its startup token and the live MM command record, and channel authorization correctly denied the unallowlisted channel. A bogus-token curl against the same instance returns the plugin's fail-closed 401 "Unauthorized: invalid command token." — the per-command token guard is intact.

(Reproducer note: Mattermost refuses webhook targets on private/CGNAT addresses unless listed in ServiceSettings.AllowedUntrustedInternalConnections; unrelated to this fix but easy to trip over in tailnet/LAN deployments.)

Follow-up work: externalized plugins' other public artifacts (doctor contract, secret contract, message-tool, thread-binding, media inbound roots, health checks) still resolve bundled-only; tracking issue to route each through the activation-gated seam with per-surface trust review.

In-the-wild before/after — a second, independent machine running the reporter's exact environment (packaged openclaw 2026.6.11 (e085fa1) from npm, externalized @openclaw/mattermost at ~/.openclaw/npm/projects/openclaw-mattermost-41605c6faa, Mattermost team edition 11.7.0 in Docker/colima on the same host, multi-account channel config):

BEFORE (2026.6.11 e085fa1):
  POST /api/channels/mattermost/command (bogus token)
  → HTTP/1.1 401 Unauthorized
  → {"error":{"message":"Unauthorized","type":"unauthorized"}}        # gateway auth layer

AFTER (this branch, d2a3021, installed via npm pack tarball over the same global install):
  same probe
  → HTTP/1.1 401 Unauthorized
  → {"response_type":"ephemeral","text":"Unauthorized: invalid command token."}   # plugin fail-closed guard

  Mattermost-initiated: POST /api/v4/commands/execute {"command":"/oc_status"}
  → HTTP 200 {"response_type":"ephemeral","text":"Processing..."}
  gateway log: "mattermost: slash command /oc_status from openclaw in r94p98ut4byhzcbfz14nsukzoo"

Native slash commands went from structurally broken to fully working on that install with no plugin change — only the core fix.

@openclaw-barnacle openclaw-barnacle Bot added gateway Gateway runtime size: S maintainer Maintainer-authored PR labels Jul 2, 2026
@clawsweeper

clawsweeper Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed July 3, 2026, 8:56 PM ET / 00:56 UTC.

Summary
The PR reroutes channel gateway-auth bypass artifact loading through the activation-gated facade resolver for bundled or installed plugin roots and adds facade hardlink-policy coverage.

Reproducibility: yes. Source inspection shows current main still uses bundled-only gateway-auth artifact loading, and the linked issue plus PR body provide packaged-install before/after Mattermost evidence.

Review metrics: 2 noteworthy metrics.

  • Potential merge surface: 6 files changed relative to current main. The PR compare is stale-base noisy, but GitHub's clean potential merge result is limited to the gateway-auth and facade-loader surfaces plus tests.
  • Auth-bypass consumer change: 1 gateway-auth consumer rerouted. The only runtime consumer granting unauthenticated channel callback ingress now resolves through the activation-gated installed-root facade seam.

Stored data model
Persistent data-model change detected: migration/backfill/repair: src/commands/doctor-completion.test.ts, migration/backfill/repair: src/commands/doctor-completion.ts, migration/backfill/repair: src/scripts/test-projects.test.ts, serialized state: scripts/runtime-postbuild.mjs, serialized state: test/scripts/runtime-postbuild.test.ts, unknown-data-model-change: src/commands/doctor-completion.test.ts, and 5 more. Confirm migration or upgrade compatibility proof before merge.

Root-cause cluster
Relationship: fixed_by_candidate
Canonical: #98740
Summary: This PR is the candidate fix for the Mattermost gateway-auth public artifact regression; a separate open issue tracks remaining non-gateway public-artifact surfaces.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

Merge readiness
Overall: 🐚 platinum hermit
Proof: 🦞 diamond lobster
Patch quality: 🐚 platinum hermit
Result: ready for maintainer review.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • none.

Risk before merge

Maintainer options:

  1. Accept Scoped Installed-Root Bypass (recommended)
    Merge after a maintainer explicitly accepts activated installed channel plugins as trusted sources for gateway-auth bypass artifacts.
  2. Keep Broader Artifact Work Separate
    Do not broaden the shared bundled-artifact loader in this PR; leave doctor, secret, message-tool, media, health, and provider-policy surfaces to the separate follow-up.
  3. Pause For Wider Trust Review
    Pause the PR if maintainers want one product or security decision covering all installed public-artifact surfaces before changing gateway-auth alone.

Next step before merge

  • [P2] Human maintainer review is required because the remaining action is accepting a security-sensitive gateway-auth and compatibility boundary, not a narrow automatable repair.

Security
Needs attention: The patch is security-sensitive because it changes which plugin artifact roots can grant unauthenticated gateway callback bypass paths.

Review details

Best possible solution:

Land the narrow six-file merge result after maintainer acceptance of the installed-root gateway-auth trust boundary, while keeping #98842 as the canonical follow-up for other public artifacts.

Do we have a high-confidence way to reproduce the issue?

Yes. Source inspection shows current main still uses bundled-only gateway-auth artifact loading, and the linked issue plus PR body provide packaged-install before/after Mattermost evidence.

Is this the best way to solve the issue?

Yes, with maintainer acceptance. A consumer-scoped activation-gated facade resolver is narrower and safer than adding a broad installed-root fallback to the shared public-surface loader.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against f59310a555d7.

Label changes

Label changes:

  • add status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (live_output): The PR body includes packaged v2026.6.10/v2026.6.11/branch probes plus live Mattermost round-trip output on two hosts showing callbacks reach plugin auth after the fix.
  • remove status: ⏳ waiting on author: Current PR status label is status: 👀 ready for maintainer look.

Label justifications:

  • P1: The PR addresses a latest-release Mattermost regression that blocks native slash-command callbacks on packaged v2026.6.11 installs.
  • merge-risk: 🚨 compatibility: Disabled or denied channel plugins stop contributing bypass paths, and existing externalized installs gain installed-root artifact resolution.
  • merge-risk: 🚨 security-boundary: The diff changes which plugin artifacts can define unauthenticated gateway callback bypass paths.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🦞 diamond lobster and patch quality is 🐚 platinum hermit.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (live_output): The PR body includes packaged v2026.6.10/v2026.6.11/branch probes plus live Mattermost round-trip output on two hosts showing callbacks reach plugin auth after the fix.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body includes packaged v2026.6.10/v2026.6.11/branch probes plus live Mattermost round-trip output on two hosts showing callbacks reach plugin auth after the fix.
Evidence reviewed

Security concerns:

  • [medium] Accept installed-root gateway-auth trust boundary — src/channels/plugins/gateway-auth-bypass.ts:25
    Activated installed channel plugins can now supply gateway-auth bypass paths, so maintainers should explicitly accept the activation-gated trust model and hardlink/root-boundary checks before merge.
    Confidence: 0.86

What I checked:

  • Protected maintainer PR: Live PR metadata shows the author is a repository member, the PR is open and unmerged, and it carries the protected maintainer label, so conservative cleanup should not close it. (fe0ffb80b113)
  • Current main still uses bundled-only gateway-auth artifact resolution: Current main loads gateway-auth-api.js through loadBundledPluginPublicArtifactModuleSync, which resolves only bundled public-surface locations and explains why externalized Mattermost installs lose the bypass path. (src/channels/plugins/gateway-auth-bypass.ts:7, f59310a555d7)
  • PR uses the activation-gated facade seam: The PR awaits tryLoadActivatedBundledPluginPublicSurfaceModule for gateway-auth-api.js, returning null for blocked activation while allowing activated installed plugin roots to supply the artifact. (src/channels/plugins/gateway-auth-bypass.ts:25, fe0ffb80b113)
  • Protected plugin routes remain authenticated outside the bypass set: The gateway still requires AuthorizedGatewayHttpRequest for protected plugin routes unless the request path is in the resolved gateway-auth bypass set. (src/gateway/server-http.ts:386, fe0ffb80b113)
  • Installed-root hardlink policy is added to facade loading: The PR rejects hardlinked artifacts for installed plugin roots while keeping core-shipped and bundled roots on the existing hardlink behavior. (src/plugin-sdk/facade-loader.ts:139, fe0ffb80b113)
  • Actual merge result is narrow despite stale-base PR diff noise: GitHub's potential merge commit is CLEAN and differs from current main in six relevant files: gateway-auth-bypass, server-http, facade-loader, facade-runtime, and their focused tests. (ec0f3aaef1d0)

Likely related people:

  • gumadeiras: Commit 807c664 added the gateway-auth bypass discovery file and wired it into gateway server behavior. (role: introduced gateway-auth bypass fast path; confidence: high; commits: 807c6648f971; files: src/channels/plugins/gateway-auth-bypass.ts, src/gateway/server-http.ts)
  • vincentkoc: Commit f2eca94 externalized Mattermost and related official plugins, and later public-surface loader history also points to Vincent Koc in this area. (role: externalization and public-surface area contributor; confidence: high; commits: f2eca9439189, d96ac02dc625, e085fa1a3ffd; files: extensions/mattermost/package.json, package.json, src/plugins/public-surface-loader.ts)
  • joelnishanth: Current-main history shows recent edits introducing the current facade-loader, facade-runtime, and gateway-auth-bypass file content in the checked-out main history. (role: recent current-main facade/gateway-auth file contributor; confidence: medium; commits: 97e582ec7400; files: src/plugin-sdk/facade-loader.ts, src/plugin-sdk/facade-runtime.ts, src/channels/plugins/gateway-auth-bypass.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. P1 High-priority user-facing bug, regression, or broken workflow. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 security-boundary 🚨 May affect sandboxing, authorization, credentials, or sensitive data. labels Jul 2, 2026
@amknight
amknight marked this pull request as ready for review July 2, 2026 02:15
@amknight

amknight commented Jul 2, 2026

Copy link
Copy Markdown
Member Author

/clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. and removed rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. labels Jul 2, 2026
amknight added a commit that referenced this pull request Jul 2, 2026
Superseded by the activation-gated facade seam in gateway-auth-bypass;
review found the loader-level fallback broadened every artifact surface
past its trust boundary (see PR #98819 discussion).
@amknight

amknight commented Jul 2, 2026

Copy link
Copy Markdown
Member Author

/clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@clawsweeper clawsweeper Bot added rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. and removed rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. labels Jul 2, 2026
@amknight amknight self-assigned this Jul 2, 2026
@clawsweeper clawsweeper Bot added rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. and removed rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. labels Jul 2, 2026
@amknight

amknight commented Jul 3, 2026

Copy link
Copy Markdown
Member Author

Land-ready verification (head 53d118993b)

  • CI: 51 pass / 0 pending / 0 fail. Prior head failures fully attributed: check-lint = unused oxlint directive (fixed in-branch, 4db42b4a09); checks-node ios-run ×4 and maturity-docs = main-side stale-base windows (test/scripts/ios-run.test.ts fails identically on pure main 6a96bbc7ad; fixture repair landed upstream in 6e79ca3cbc) — resolved by rebase, ios-run 4/4 on the branch.
  • Local commands: pnpm test src/plugin-sdk/facade-runtime.test.ts (17/17, incl. new hardlink-policy pair) · facade-loader.test.ts (7/7) · src/channels/plugins/gateway-auth-bypass.test.ts (4/4) · src/gateway/server.plugin-http-auth.test.ts (48/48) · pnpm build (0 INEFFECTIVE_DYNAMIC_IMPORT) · pnpm check:changed green via Blacksmith Testbox (tbx_01kwgc46nxkxtgr0bn4z9kx81j).
  • Before/after proof: packaged 6.10/6.11/fix matrix plus live Mattermost round-trips on two hosts (reporter-identical topology) — details in the PR body Evidence section.
  • Known gaps: broad local gateway e2e lanes are flaky on the dev workstation (proven worse on origin/main: 8 failures vs the branch's 4, same vitest worker OOM) — machine-local, CI is the truth source. No e2e HTTP test asserts the 500 on a throwing artifact (pre-existing posture; unit test covers the rejection).

Adversarial review complete: no open findings; disabled/denied plugins can no longer contribute gateway auth bypass paths or execute artifact code. Follow-up for the remaining bundled-only artifact surfaces: #98842.

@clawsweeper clawsweeper Bot added status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. and removed status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. labels Jul 4, 2026
@amknight
amknight merged commit 342d13a into main Jul 4, 2026
214 of 224 checks passed
@amknight
amknight deleted the fix/plugin-public-artifacts-installed-roots branch July 4, 2026 00:57
@clawsweeper clawsweeper Bot removed the status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. label Jul 4, 2026
@amknight

amknight commented Jul 4, 2026

Copy link
Copy Markdown
Member Author

Merged via squash.

@clawsweeper clawsweeper Bot added the status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. label Jul 4, 2026
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jul 4, 2026
…penclaw#98819)

* fix(plugins): resolve public artifacts from installed plugin roots

Externalized official plugins ship gateway-auth/doctor/secret/message-tool
public artifacts in their installed npm package, but the public-surface
loader only searched bundled locations. On packaged installs this emptied
the gateway auth bypass set for /api/channels/mattermost/command, so every
Mattermost slash callback was rejected 401 before the plugin ran.

Fall back to the plugin's installed package root (install-record index)
when bundled resolution misses; bundled/source locations keep precedence.

Fixes openclaw#98740

* fix(gateway): resolve channel auth-bypass artifacts via activation-gated facade

Route gateway-auth bypass artifact loading through the activation-gated
facade seam so externalized (installed) channel plugins keep their
unauthenticated callback paths, while denied/disabled plugins contribute
nothing and never execute artifact code. Reject hardlinked artifacts for
installed plugin roots in the facade loader per hardlink-policy.

Replaces the earlier install-record fallback in the shared bundled
artifact loader, which broadened every artifact surface past its trust
boundary (see PR review); other externalized-plugin surfaces are openclaw#98842.

Fixes openclaw#98740

* fix(gateway): load facade activation runtime via async lazy import

The sync createRequire/jiti candidate loader for
facade-activation-check.runtime.js only resolves from built dist; under
vitest source runs it cannot follow .js->.ts specifiers or workspace
package subpaths, so the gateway auth-bypass path threw 'Unable to load
facade activation check runtime' (CI shard failure). Add an async
activated-load API backed by dynamic import (vitest- and dist-native),
warm the shared runtime memo, and make the bypass consumer async - its
single caller already awaits behind the config-snapshot promise cache.

* revert(plugins): drop install-record fallback from public-surface loader

Superseded by the activation-gated facade seam in gateway-auth-bypass;
review found the loader-level fallback broadened every artifact surface
past its trust boundary (see PR openclaw#98819 discussion).

* fix(plugin-sdk): drop unused oxlint disable on async facade loader

* test(plugin-sdk): cover facade hardlink policy for installed vs core roots

* test(plugin-sdk): cover facade hardlink policy for installed vs core roots

---------

Co-authored-by: Alex Knight <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling app: ios App: ios app: web-ui App: web-ui commands Command implementations docker Docker and sandbox tooling docs Improvements or additions to documentation extensions: byteplus extensions: minimax extensions: openai extensions: openrouter extensions: qa-lab extensions: together extensions: xai gateway Gateway runtime maintainer Maintainer-authored PR merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 security-boundary 🚨 May affect sandboxing, authorization, credentials, or sensitive data. P1 High-priority user-facing bug, regression, or broken workflow. plugin: file-transfer proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. scripts Repository scripts size: XL status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Mattermost native slash commands return 401 on every callback after 6.11 plugin externalization, even after clean re-registration

1 participant