Skip to content

Draft post: plugins.allow is exclusive — the warning's suggested fix disabled 44 plugins - #54

Merged
clarkbw merged 1 commit into
mainfrom
post/plugins-allow
Aug 29, 2026
Merged

Draft post: plugins.allow is exclusive — the warning's suggested fix disabled 44 plugins#54
clarkbw merged 1 commit into
mainfrom
post/plugins-allow

Conversation

@clarkbw

@clarkbw clarkbw commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Post: 2026-08-11-plugins-allow-exclusive-allowlist-config-warning-suggested-fix-breaks-production-disables-bundled-plugins-openclaw-mcp-bridge-fallback-provider.md

Draft only — merge = publish. The date: and filename carry the drafting date (2026-08-11); bump both at merge time with script/bump.py / script/publish.py.

What it is

Candidate #17 from the 2026-08-11 blog-candidate review. The arc: a config warning printed a ready-to-paste one-entry allowlist every single turn; applying it on the live gateway collapsed enabled plugins 47 → 3, because plugins.allow is an exclusive allowlist over all plugins including the ~46 bundled ones.

The two casualties are the reason it's a post and not a footnote:

  • anthropic-provider — the fallback model. The failure is invisible until the primary is down, i.e. the only moment it matters. A resilience feature that silently stops loading is worse than one you never configured, because you stop budgeting for the failure it was meant to absorb.
  • bundle-mcp — every MCP tool. Servers still launch, still connect, still probe healthy; their tools are just absent from the request. No error.

The subtle bit, and the one I'd have got wrong too: naming channels.telegram auto-activates its plugin even under a restrictive allowlist; naming mcp.servers.<name> does not rescue bundle-mcp. So the lane that keeps working is the one you'd notice, and the lane that goes dark is the one you have to go looking for.

Landing: the durable engineering move wasn't code — it was writing the decision to keep living with the warning into the config as a comment at the exact spot someone would make the change. "Record why you ignored the linter" is the generalizable lesson, alongside "allowlists that silently include the batteries."

Verification of the public claims

OpenClaw is named — precedent is the published 2026-07-29 post and pending #46. Every semantic claim was fetched from docs.openclaw.ai before writing, not recalled:

  • plugins.allow is exclusive — quoted verbatim from docs.openclaw.ai/tools/plugin: "plugins.allow is an exclusive allowlist. Plugin-owned tools outside the allowlist stay unavailable even when tools.allow includes "*"."
  • It covers bundled plugins — this is the load-bearing claim, so it's sourced twice, both from pages where it bites a specific feature rather than from the general plugin page. docs.openclaw.ai/tools/acp-agents: "If plugins.allow is set, it is a restrictive plugin inventory and must include acpx, or the installed ACP backend is intentionally blocked." Plus the Codex setup note requiring the bundled codex id in the allowlist.
  • The warning string — quoted from the docs, which document the exact log line.
  • Auto-activation and its surface list — quoted from docs.openclaw.ai/tools/plugin: "provider/model ref, channel config, CLI backend, or agent harness runtime." The asymmetry argument rests on mcp.servers being absent from that list, which the post states as observed-plus-not-listed rather than as a documented negative.
  • Channel-style activation beats the allowlistdocs.openclaw.ai/tools/browser says an explicit root browser block "activates the bundled browser plugin even under a restrictive plugins.allow", explicitly "matching bundled channel config behavior".
  • The MCP page never mentions the plugin dependency — checked docs.openclaw.ai/tools/mcp directly; it's an argument from a verified absence, and the post frames it that way ("which is exactly why you don't think to check it").

Privacy

Source story is in the private repo; everything is generalised. No hostnames, no IPs, no topology, no private repo names or paths — grepped the post and both SVGs for all of them. "The live box", "a Raspberry Pi next to a SignalK server" is the level of detail. ~/.openclaw appears once as OpenClaw's own documented global plugin root, not as our filesystem. The config snippets are minimal reproductions built from publicly documented keys — none is our file, and the recorded-decision comment in The fix is a freshly written condensed template, not the one on the box.

Figures

Two hand-written SVGs, dark ground, system fonts, no ] or angle brackets in alt text:

  • enabled-plugins-before-after.svg — the result, under the TL;DR: the 47 → 3 bars, the three survivors named with why each survived, and the two critical casualties marked with ✕.
  • activation-asymmetry.svg — the mechanism, in Diagnosis: two lanes, config key → owning plugin → what the agent can do, green through telegram and broken at bundle-mcp.

No third figure — the remaining candidate would have restated the JSON snippets in rounded rectangles.

Checks

python3 script/lint-liquid.py   → post lint: 35 posts clean
bundle exec jekyll build        → clean (only the pre-existing excerpt warnings)

Rendered _site/<slug>/index.html confirmed: both <img src="/assets/img/…"> present, both SVGs shipped to _site/assets/img/<post-slug>/, all four post_url related links resolved to real permalinks, both SVGs parse as well-formed XML, and the <id> / <name> placeholders survived Liquid and kramdown as escaped entities inside the code blocks. No {% raw %} guard needed — the only Liquid in the body is the allowed post_url tags.

Two things to check before merging

  1. The deepseek / anthropic-provider naming. These are public OpenClaw plugin ids and the primary/fallback split is a config preference rather than infrastructure, so I judged it inside the guardrail and kept it — the concreteness is most of what makes the "no fallback" point land. Say the word and I'll swap both for primary-provider / fallback-provider placeholders; it costs about four lines and one figure edit.
  2. "~46 bundled". The 68/47/3 counts are measured on our gateway; the bundled count is inferred as 68 minus the one non-bundled plugin, minus the disabled ones. The post consistently hedges it as "~46" and never uses it in an arithmetic claim — the load-bearing numbers are 47, 3, and 44, all directly observed.

🤖 Generated with Claude Code

https://claude.ai/code/session_01HaMaRk9UijJQMDX48EVvTk

@clarkbw clarkbw added the publish Approved to publish via the drip-publisher routine label Aug 12, 2026
@clarkbw
clarkbw force-pushed the post/plugins-allow branch from 4a97be4 to d7cbefd Compare August 12, 2026 14:51
…disabled 44 plugins

Candidate #17 from the 2026-08-11 blog-candidate review. A config warning
printed a ready-to-paste one-entry allowlist every turn; applying it on a live
agent gateway collapsed enabled plugins 47 -> 3, because plugins.allow is an
exclusive allowlist over all plugins including the ~46 bundled ones. Casualties:
the fallback model provider and bundle-mcp (every MCP tool).

OpenClaw is named — all semantics quoted from docs.openclaw.ai (tools/plugin,
tools/acp-agents, tools/browser), consistent with the published 2026-07-29 post.
Config snippets are minimal reproductions from publicly documented keys.

Two hand-written SVGs: the 47 -> 3 result, and the activation asymmetry
(channel config rescues its plugin, mcp.servers does not).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HaMaRk9UijJQMDX48EVvTk
@clarkbw
clarkbw force-pushed the post/plugins-allow branch from d7cbefd to 459009e Compare August 29, 2026 02:35
@clarkbw
clarkbw merged commit cbe11b5 into main Aug 29, 2026
1 check passed
@clarkbw
clarkbw deleted the post/plugins-allow branch August 29, 2026 02:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

publish Approved to publish via the drip-publisher routine

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant