os package install ./dist/objectstack.json is documented as the air-gapped path — "the compiled artifact is read locally and sent inline — no catalog round-trip, works fully offline" (packages/cli/src/commands/package/install.ts). The plugin that serves it agrees: marketplace-install-local-plugin.ts:463 — "Bypass the cloud-fetch entirely; no OS_CLOUD_URL required".
But the plugin is only registered when a cloud URL exists (packages/cli/src/commands/serve.ts:1652):
constmarketplaceUrl=resolveCloudUrl();if(marketplaceUrl){awaitkernel.use(newMarketplaceProxyPlugin({controlPlaneUrl: marketplaceUrl}));awaitkernel.use(newMarketplaceInstallLocalPlugin({controlPlaneUrl: marketplaceUrl}));…awaitkernel.use(newRuntimeConfigPlugin({controlPlaneUrl: '',singleEnvironment: true,installLocal: true}));}With OS_CLOUD_URL=off — the value the self-hosted EE compose file documents as "完全自托管填 off" — resolveCloudUrl() is empty, the whole block is skipped, and the endpoint never mounts. So the deployment that most needs offline install is the one that cannot have it.
Measured
objectos-ee-deploy (image ghcr.io/objectstack-ai/objectos-ee@sha256:c93b4937…, tag 4.0.5-rc.1), OS_CLOUD_URL=off, container command node node_modules/@objectstack/cli/bin/run.js start (which forks serve):
GET /api/v1/marketplace/install-local → 404 {"error":"Not found"}
POST /api/v1/marketplace/install-local → 404 {"error":"Not found"}
There is no other package-install surface: the served OpenAPI document contains no path matching package|marketplace|install, and the container mounts only /data (the artifact is baked into the image at OS_ARTIFACT_PATH=/repo/cloud/apps/objectos-ee/dist/objectstack.json). OS_ARTIFACT_PATH takes a single artifact, not a directory or a list (packages/runtime/src/default-host.ts:49), so there is also no "drop another bundle in" workaround. The deployment simply cannot install a package by any route.
Second symptom: the Console is told the feature is live
GET /api/v1/runtime/config on the same deployment returns:
{"cloudUrl":"","singleEnvironment":true,
"features":{"installLocal":true,"marketplace":true,"aiStudio":true,"autoPublishAiBuilds":true}, …}installLocal: true with no endpoint behind it. Whatever supplies runtime-config in the EE image is not the RuntimeConfigPlugin from the skipped block, so the flag and the route now disagree — the Console will offer an install affordance that 404s.
Expected
Registration of MarketplaceInstallLocalPlugin should not depend on resolveCloudUrl(). Only the catalog branch needs a control plane, and it already degrades correctly on its own (marketplace-install-local-plugin.ts:494 → 503 MARKETPLACE_UNAVAILABLE, "OS_CLOUD_URL not configured."). Mounting it unconditionally therefore costs nothing and restores the documented offline path; MarketplaceProxyPlugin (a pure cloud proxy) can stay behind the URL check.
And whichever component reports features.installLocal should report what is actually mounted.
Impact
This is the blocking issue for installing any first-party or customer application into a self-hosted EE deployment — the concrete case here was installing HotCRM (app.objectstack.hotcrm) into a running multi-org EE stack, for which no supported path currently exists.
Environment
objectos-ee-deploy (Caddy → app → postgres:16), NODE_ENV=production, OS_TENANCY_POSTURE=isolated, OS_MODE=standalone, OS_CLOUD_URL=off, on http://localhost:8080, observed 2026-08-13. Source references are the current framework main checkout.
os package install ./dist/objectstack.jsonis documented as the air-gapped path — "the compiled artifact is read locally and sent inline — no catalog round-trip, works fully offline" (packages/cli/src/commands/package/install.ts). The plugin that serves it agrees:marketplace-install-local-plugin.ts:463— "Bypass the cloud-fetch entirely; no OS_CLOUD_URL required".But the plugin is only registered when a cloud URL exists (
packages/cli/src/commands/serve.ts:1652):With
OS_CLOUD_URL=off— the value the self-hosted EE compose file documents as "完全自托管填 off" —resolveCloudUrl()is empty, the whole block is skipped, and the endpoint never mounts. So the deployment that most needs offline install is the one that cannot have it.Measured
objectos-ee-deploy(imageghcr.io/objectstack-ai/objectos-ee@sha256:c93b4937…, tag 4.0.5-rc.1),OS_CLOUD_URL=off, container commandnode node_modules/@objectstack/cli/bin/run.js start(which forksserve):There is no other package-install surface: the served OpenAPI document contains no path matching
package|marketplace|install, and the container mounts only/data(the artifact is baked into the image atOS_ARTIFACT_PATH=/repo/cloud/apps/objectos-ee/dist/objectstack.json).OS_ARTIFACT_PATHtakes a single artifact, not a directory or a list (packages/runtime/src/default-host.ts:49), so there is also no "drop another bundle in" workaround. The deployment simply cannot install a package by any route.Second symptom: the Console is told the feature is live
GET /api/v1/runtime/configon the same deployment returns:{"cloudUrl":"","singleEnvironment":true, "features":{"installLocal":true,"marketplace":true,"aiStudio":true,"autoPublishAiBuilds":true}, …}installLocal: truewith no endpoint behind it. Whatever supplies runtime-config in the EE image is not theRuntimeConfigPluginfrom the skipped block, so the flag and the route now disagree — the Console will offer an install affordance that 404s.Expected
Registration of
MarketplaceInstallLocalPluginshould not depend onresolveCloudUrl(). Only the catalog branch needs a control plane, and it already degrades correctly on its own (marketplace-install-local-plugin.ts:494→503 MARKETPLACE_UNAVAILABLE, "OS_CLOUD_URL not configured."). Mounting it unconditionally therefore costs nothing and restores the documented offline path;MarketplaceProxyPlugin(a pure cloud proxy) can stay behind the URL check.And whichever component reports
features.installLocalshould report what is actually mounted.Impact
This is the blocking issue for installing any first-party or customer application into a self-hosted EE deployment — the concrete case here was installing HotCRM (
app.objectstack.hotcrm) into a running multi-org EE stack, for which no supported path currently exists.Environment
objectos-ee-deploy(Caddy → app → postgres:16),NODE_ENV=production,OS_TENANCY_POSTURE=isolated,OS_MODE=standalone,OS_CLOUD_URL=off, onhttp://localhost:8080, observed 2026-08-13. Source references are the currentframeworkmain checkout.