Skip to content

Least-privilege workers by default — naked MCP/plugin isolation is achievable; which rollout do we want? (ping @illegalcall) #2844

Description

@axisrow

Following up on your review of #2621 — first, thank you. I'm genuinely impressed by the depth and care of the review; the trust and seriousness you extended to a contributor's PR is not something I take for granted. You caught a real mismatch between #2195's intent and my implementation, and you're right about it.

Before I implement it, I want to check one product-level decision with you, because you've thought about this project far longer than I have, and I don't want to make this call unilaterally.

My position: ship least-privilege as the default — but I want to talk it through

I'm convinced the right end state is: workers run naked — no MCP, no plugins — unless the user explicitly opts them in. A worker should just do the work it's given; it should not inherit the orchestrator's keys to the safe. That's the principle behind #2195's "workers default to none, opt in explicitly," and you're holding me to it in the review, correctly.

The mechanism exists, so the code is not the blocker:

  • MCP isolation: --strict-mcp-config with no --mcp-config loads zero MCP servers (verified against claude --help and the CLI reference). MCPConfig{Strict: true, Configs: []} already validates and is tested.
  • Plugin isolation: --settings '{"enabledPlugins": {...all false...}}' disables plugins per-session without --bare/--safe-mode (which would also kill AO's hooks). Verified: claude --settings '{"enabledPlugins": {"context7@claude-plugins-official": false}}' plugin list shows Status: ✘ disabled. So plugin least-privilege is achievable too, without breaking AO.

So I'm ready to ship lockdown-by-default.

The one thing giving me pause

The only reason I'm raising this instead of just shipping it: flipping the default silently changes behavior for every existing project. A worker that quietly used a filesystem MCP tool or a plugin now runs half-blind until the user configures worker.mcp.configs / a plugin allowlist — and the user finds out from a failed tool call, not from AO. With ~8k users on the project, that's a lot of quiet breakage delivered in a PR that advertises itself as "add per-role config."

That's not enough to change my mind on the direction — I still think naked-by-default is correct. But it's enough that I want to hear yours before I land it, because there are a few rollout shapes and you'll know which fits the project:

  1. Naked-by-default for everyone + a loud changelog/migration note. Cleanest end state, sharpest transition.
  2. Naked-by-default, but only for newly-created projects — existing projects keep inherit until they migrate. Gentler; more surface.
  3. Naked as an opt-in now (e.g. a per-project lockdown flag, default off), default flips to naked in a later major version. Slowest; least surprise.

I'd vote (1) or (2). But you have the longer view on the project and its users, so: which rollout do you want? Once you pick, I'll implement it in the split per-issue PRs (the combined PR is being split per your one-issue-per-PR point — #2195 backend config, #2230 desktop keep-alive), reference this issue there, and ship the isolation however you decide here.

Tagging you because you're already the reviewer on #2621 and this is the one judgment call in the review I didn't want to make alone.

Metadata

Metadata

Labels

No labels
No labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions