Problem
POST /api/connectors/plugins/install and GET /api/connectors/plugins return 403 in local single-user mode because requirePluginWriteAccess and requirePluginListAccess in connector-plugins.ts use requireConfiguredOwner: true.
This forces DEFAULT_OWNER_USER_ID to be configured even for localhost direct access, which is unnecessary and inconsistent with all other write routes.
Root Cause
Commit ab004e69b (缅因猫/gpt-5.5, F240 Phase B) added requireConfiguredOwner: true reasoning that plugin install "can execute code so needs stricter auth". But this ignores the existing 3-layer defense:
- Session authentication (Layer 1)
- Origin check — same-origin Hub access only (Layer 2)
- Owner gate — only blocks when
DEFAULT_OWNER_USER_ID IS configured and doesn't match (Layer 3)
The correct pattern (established by #794, d6f4cba12) is allowMissingOwner: true — fall through in single-user mode, enforce only when explicitly configured. This is what plugin-routes.ts uses.
Why This Keeps Recurring
The #794 decision ("owner gates fall through in local single-user mode") is NOT documented in any ADR, feature doc, or code comment visible to implementers. When new routes are added, the developer has no reference and defaults to the stricter check.
Preventive action needed: Add a JSDoc/comment on requireCapabilityWriteOwner explaining when to use allowMissingOwner: true vs requireConfiguredOwner: true.
Fix
packages/api/src/routes/connector-plugins.ts lines 72-75 and 89-92:
- return requireCapabilityWriteOwner(userId, {
- requireConfiguredOwner: true,
- missingOwnerError: 'Connector plugin writes require DEFAULT_OWNER_USER_ID to be configured',
- });
+ return requireCapabilityWriteOwner(userId, { allowMissingOwner: true });
Same change for requirePluginListAccess.
Files
packages/api/src/routes/connector-plugins.ts (2 locations)
packages/api/src/config/capabilities/capability-write-guards.ts (add preventive JSDoc)
History
| Commit |
Author |
What |
25f897767 |
mindfn |
fix: allow local capability writes without owner |
9494614b5 |
mindfn |
fix: allow local capability writes without owner env |
d6f4cba12 |
lang |
#794: unified owner gates fall through |
ab004e69b |
缅因猫 |
❌ re-introduced requireConfiguredOwner: true |
7b1f22543 |
缅因猫 |
❌ same for plugin listing |
Problem
POST /api/connectors/plugins/installandGET /api/connectors/pluginsreturn 403 in local single-user mode becauserequirePluginWriteAccessandrequirePluginListAccessinconnector-plugins.tsuserequireConfiguredOwner: true.This forces
DEFAULT_OWNER_USER_IDto be configured even for localhost direct access, which is unnecessary and inconsistent with all other write routes.Root Cause
Commit
ab004e69b(缅因猫/gpt-5.5, F240 Phase B) addedrequireConfiguredOwner: truereasoning that plugin install "can execute code so needs stricter auth". But this ignores the existing 3-layer defense:DEFAULT_OWNER_USER_IDIS configured and doesn't match (Layer 3)The correct pattern (established by #794,
d6f4cba12) isallowMissingOwner: true— fall through in single-user mode, enforce only when explicitly configured. This is whatplugin-routes.tsuses.Why This Keeps Recurring
The #794 decision ("owner gates fall through in local single-user mode") is NOT documented in any ADR, feature doc, or code comment visible to implementers. When new routes are added, the developer has no reference and defaults to the stricter check.
Preventive action needed: Add a JSDoc/comment on
requireCapabilityWriteOwnerexplaining when to useallowMissingOwner: truevsrequireConfiguredOwner: true.Fix
packages/api/src/routes/connector-plugins.tslines 72-75 and 89-92:Same change for
requirePluginListAccess.Files
packages/api/src/routes/connector-plugins.ts(2 locations)packages/api/src/config/capabilities/capability-write-guards.ts(add preventive JSDoc)History
25f8977679494614b5d6f4cba12ab004e69b7b1f22543