Skip to content

finding: serve.ts's cloud-connected marketplace arm can silently replace a host config's own marketplace plugins #8357

Description

@os-zhuang

Observation-class finding from #8343. No measured user impact today — filed so it is not rediscovered as a mystery later.

Kernel.use() stores plugins in a Map keyed by plugin.name, so registering a second plugin with the same name replaces the first entry rather than erroring or de-duplicating.

The marketplace wiring block in packages/cli/src/commands/serve.ts mounts MarketplaceProxyPlugin, MarketplaceInstallLocalPlugin, the cloud-connection surface and RuntimeConfigPlugin whenever resolveCloudUrl() is truthy and the kernel is not a runtime host kernel. It does not check whether the loaded host config already wired any of them. A host that mounts its own therefore has it replaced by the CLI's instance, constructed with the CLI's own arguments.

cloud's apps/objectos-ee/objectstack.config.ts is exactly that host: in single-environment mode with a real OS_CLOUD_URL it wires MarketplaceProxyPlugin, MarketplaceInstallLocalPlugin and createCloudConnectionPlugin itself, from the same classes (@objectstack/objectos-runtime re-exports them from @objectstack/cloud-connection, single source since ADR-0008 Phase 2). Today both sides construct them with the same resolveCloudUrl() value, so the replacement is behaviourally invisible. It stops being invisible the moment a host passes anything the CLI does not — a distinct control-plane URL, a custom storageDir, cache tuning.

Note the host-kernel guard does not cover this case: it detects ObjectOSEnvironmentPlugin, which the EE single-environment branch never constructs (only the OS_MULTI_TENANT branch does, via createObjectOSStack).

#8343 added exactly this presence check to the offline arm it introduced, using the existing Serve.providesCapability idiom, because there the replacement would be a genuine downgrade (an off-pinned instance overwriting a host's URL-configured one). The cloud-connected arm was deliberately left as-is: making the host win there is a real behaviour change for connected runtimes and wanted its own card.

Suggested direction

Apply the same identity check to the cloud arm, so a host config that wires its own marketplace plugins keeps them and the CLI auto-wiring stays a fallback for hosts that wire nothing. Alternatively, make Kernel.use() refuse or warn on a duplicate name instead of silently replacing — a broader fix with a much larger blast radius.

Backlink: #8343.


Generated by Claude Code

Metadata

Metadata

Assignees

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions