Skip to content

marketplace install-local merges seed datasets but never registers seed-replayer — every organization founded after an install on a walled deployment boots EMPTY #9070

Description

@os-zhuang

Found by the HotCRM single-database multi-tenant acceptance added in objectstack-ai/cloud#1350. Measured on a real objectstack serve boot of apps/objectos-ee, OS_TENANCY_POSTURE=isolated, licensed, OS_CLOUD_URL=off, with the real 342-row HotCRM artifact installed through POST /api/v1/marketplace/install-local.

What happens

  1. The install succeeds: {"success":true,...,"seeded":{"mode":"inline","inserted":342,"errors":0}}. The installer's own organization gets a full copy.
  2. A customer then founds an organization. The per-organization replay logs, once per organization:
WARN [org-scoping] per-org seed: datasets present but no replayer registered {"organizationId":"..."}
  1. That organization holds zero rows of the app that was just installed. Every object, every dataset.

The probe is not blind: the same GET /api/v1/data/crm_account returns the full copy for the installer's organization in the same run.

Mechanism

MarketplaceInstallLocalPlugin.applySideEffects calls mergeSeedDatasetsIntoKernel, which correctly appends the package's data blocks onto the shared seed-datasets service (this is what #3453 fixed). It never calls registerSeedReplayerOnce.

Its own docblock states the assumption that then fails:

the per-org replayer (AppPlugin) replays the whole union on the next sys_organization insert

That holds only when an AppPlugin app with seed data is ALSO mounted on the kernel — registerSeedReplayerOnce lives in AppPlugin's seeder path. On a runtime whose host app declares no data of its own — which is precisely the shape a marketplace install targets, and exactly what apps/objectos-ee is (objects: [], no data) — seed-datasets is populated and seed-replayer is absent. The middleware finds datasets, finds no replayer, and does nothing.

So the merge landed and the consumer never did.

Why this matters

This is the designed distribution path for marketplace apps into a customer's own enterprise runtime. On a walled (isolated / group) deployment it means: install the app, then every customer organization created afterwards signs up to an app with no data at all. The installer's own organization is the only one that ever gets a copy, and it gets it by the install-time inline seed rather than by the replay.

Contrast, same artifact, same boot shape

The composed-artifact boot (OS_COMPOSED_ARTIFACT_URL, objectstack-ai/cloud#1347) mounts the app through createDefaultHostConfig and therefore through AppPlugin, which DOES register the replayer. On that path both tenant organizations receive their own private copy. Same runtime, same artifact, same posture — only the arrival path differs.

Suggested shape of the fix

applySideEffects should register the replayer alongside the merge, using the same registerSeedReplayerOnce helper AppPlugin uses (it is register-once, so a host that already has one is unaffected — that is what the helper is for).

Pinned

apps/objectos-ee/test/hotcrm-install-local.acceptance.ts in objectstack-ai/cloud pins the defect as measured, with the datasets present but no replayer registered line as the tripwire. When this is fixed that test goes RED and must be promoted into a full per-organization assertion, not repaired.

Related: #3453 (the merge half of this, fixed).


Generated by Claude Code

Metadata

Metadata

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions