Skip to content

The deploy command and the os dev deploy key can target a self-hosted runtime (Docker Enterprise or Community) — remembered target and credential, install-local underneath, the same plan and Q3 refusal (epic cloud#3652, self-hosted parity) #30888

Closed

Description

@objectstack-fleet

repo:cloud#3 · hotlong · session_01Q4EjjRhUZjdy8VXs6Kkur2

Blocked-by: #30871

Filing gate: ③ a task on the maintainer's word. Epic objectstack-ai/cloud#3652, coordinator session 2026-11-06, verbatim: 「我觉得先把受管软件包的开发循环走顺了」, 「同意,然后第一步先解决我本地开发的软件包怎么能够方便地发布到云端」, then 「还有一个场景,我如果是私有部署的企业版或者社区版,这个发布的流程怎么走?」 / 「docker部署」. On the deploy command (#30871 amendment), verbatim: 「开发人员也记不住啊」, then 「同意」. Rulings on cloud#3652 (2026-11-06T08:33Z), verbatim: Q1 「a 本地 os dev(推荐)」; Q3 「默认拒绝,需显式映射(推荐)」. Q3 reaches self-hosted through install-local (01a).

Carrier: objectstack. The CLI and the self-hosted install door (install-local) are platform code. No cloud part: an unbound self-hosted runtime never calls the control plane.

维护者速读

第一期的部署命令只认云端环境。私有部署(Docker 企业版或社区版)现在要手敲 os package install ./dist/objectstack.json -u <地址> -t <令牌>,每次都要带地址和令牌,默认地址还是 localhost:3000,而官方镜像监听 8080。这张卡让同一个部署命令和 os dev 里的 d 键也能部署到私有部署的运行时:记住目标地址和令牌,底层走现有的 install-local。升级预览和 Q3 的拒绝也一样生效。

Today (objectstack origin/main c50e0dfa)

  1. os package install remembers nothing. (measured)
    • The target is -u/--url (env OS_RUNTIME_URL, default http://localhost:3000). The credential is -t/--token (OS_RUNTIME_TOKEN) or --email/--password (packages/cli/src/commands/package/install.ts:113-136).
    • No stored target or credential is read: no cloud-config, active-environment or home-file read in the command.
    • Other flags: --version (catalog, default latest, :124-127), --timeout, --install-mode (:156), --confirm-global-uniques, --confirm-access-upgrade <token> (:174).
  2. The default URL does not match the official image. The image sets OS_PORT=8080 and EXPOSE 8080 (docker/Dockerfile:117-119), while the install default is port 3000. (measured; whether a typical Compose maps 8080 → 3000 is not read)
  3. Install-local is synchronous. The runtime registers the package in its live kernel and answers hotLoaded (packages/cloud-connection/src/marketplace-install-local-plugin.ts:41-47); the CLI prints a not-hot-loaded answer (install.ts:449). So "wait until live" is the answer itself, and no served-version poll is needed. (measured)
  4. A dev version is not needed for an inline artifact. Install-local takes the artifact inline, and a managed re-install over an installed package is the supported upgrade path (marketplace-install-local-plugin.ts:85-97). There is no catalog version row to collide with. (inferred from the door's contract; not run)
  5. Install-local on the Community image. The image runs os start (docker/Dockerfile:129), which spawns serve. serve mounts MarketplaceInstallLocalPlugin in both the cloud-connected arm and the cloud-off arm (packages/cli/src/commands/serve.ts:4052, :4093), unless the host kernel is a runtime host. (inferred from code, not run; the epic recorded it as "not verified") Enterprise mounts it, or declines it in composed-artifact mode (cloud apps/objectos-ee/objectstack.config.ts:1397, :1658). (measured)
  6. The deploy command (os deploy, working name) and the d key are being added by os package publish: one command from a local checkout to the org's environment — pick the only environment, keep it across login, fail loudly without one, and publish dev builds without hand-bumping the version #30871, cloud target only. (measured on the issue)

Expected

  1. A self-hosted target the CLI remembers. One command records a runtime URL and a credential, for example a token the runtime issued. The lane names the command beside os environments switch. It is stored like the cloud target, with the credential in the same protected file the cloud token uses. The deploy command and the d key use whichever target is active. os package install falls back to it when -u/-t are not given.
  2. Deploy to self-hosted = compile → POST /api/v1/marketplace/install-local with the inline artifact → print the hotLoaded answer. No catalog publish and no dev-version suffix. A runtime bound to cloud may instead go through the catalog; the lane decides whether that is in scope or a follow-up.
  3. The same plan and refusals. The deploy command asks install-local's dry run (01a) and prints the plan exactly as 01d prints cloud's. Declared renamedFrom renames (07, 01b) are printed. Q3's --confirm-upgrade and the existing --confirm-access-upgrade pass through. There is no --rename (decision D3: the mapping lives in the package).
  4. A refused door is named. A runtime that declines install-local (composed-artifact mode) or predates the dry run answers in a sentence naming what to do, and the exit is non-zero.
  5. The default URL question (3000 vs the image's 8080) is settled by the remembered target. The lane says whether the default also changes.

Acceptance

  • Tests with a stubbed runtime: remember a target, then the deploy command with no flags installs into it; the d key path does the same; os package install without -u uses the remembered target; a declined install-local (the EE composed-mode refusal) exits non-zero with its sentence; a Q3 refusal from the dry run stops the deploy.
  • Manual on the official Community image (ghcr.io/objectstack-ai/objectstack, os start): from a fresh scaffold, remember the container's URL and an API key it issued, press d twice with an edit between, and the runtime serves the second build. This also settles the epic's "not verified" row.

Serial: after #30871, which creates the deploy command, the d key and the stored-target plumbing this card extends. The preview half needs 01a; the cloud preview (01c) is not needed. Can run in parallel with 01d, but both edit the deploy command, so the later one merges origin/main.

Dedupe (2026-11-06), run as gh api -X GET search/issues -f q=…:

Suggested-grade: p3 · the maintainer named the self-hosted scenario; it follows the cloud loop and reuses everything Phase 1 builds.

Clause-②: no · CLI commands, flags and a local config file. The install-local door is used as is (its dry run comes from 01a).

🤖 Generated with Claude Code

Activity

  1. added
    enhancementNew feature or request
    repo:cloudCard routed whole to the cloud PM: needs cloud code, a producer or a fact (cloud owns it to closure)
    pm:blockedBlocked by another issue/PR — body carries Blocked-by: #N
    domain:serverDomain lane — seat card indexed by label:pm:seat
    on Nov 6, 2026
  2. objectstack-fleet commented on Nov 6, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: none

    Triage: audit · grade kept · domain:server · pm:blocked · 2026-11-06T10:01Z

    ⛔ Not a claim.

    • Epic objectstack-ai/cloud#3652 (the managed-package dev loop) on the maintainer's word. The framework half is objectstack code, so domain:server, and repo:cloud#3 follows it. The grade the filer set is kept. Blocked-by holds.

    Generated by Claude Code

  3. added
    pm:queueReady for the PM dispatch loop
    and removed
    pm:blockedBlocked by another issue/PR — body carries Blocked-by: #N
    on Nov 6, 2026
  4. objectstack-fleet commented on Nov 6, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#3 · os-elon-musk · session_016WV4cytQjG9X1CrCgytUM5

    Unblocked · 2026-11-06T14:48Z · Blocked-by: #30871 is cleared: #30871 closed as completed at 2026-11-06T14:34Z. Moved pm:blocked → pm:queue by the server seats' unblock scan; the card keeps its triage audit and grade, and is picked through the live server split. ⛔ Not a claim.

  5. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Nov 7, 2026
  6. objectstack-fleet commented on Nov 7, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#4 · marchtian · session_017ihL2dLQEXWGsE2SZ3Qn5F

    Claim: PM loop round 1
    Session: session_017ihL2dLQEXWGsE2SZ3Qn5F
    Account: marchtian
    Branch: claude/issue-30888-deploy-self-hosted
    Worktree: objectstack-issue-30888
    Domain: domain:server
    Seat: domain:server#4
    File surface: packages/cli/src/commands/deploy.ts, packages/cli/src/commands/package/install.ts, packages/cli/src/commands/dev.ts (the d key), packages/cli/src/commands/environments/**, the CLI stored-target/config helpers #30871 added, CLI tests, .changeset/ (stop on breach; explain in the report)
    Container & model: M/L, mode:subagent (in-seat background subagent, the project's standing dev form), model: default (dispatch-gates --tier: no path-derived mandate; PM judgment, default tier), effort: high
    Clause-②: no
    Metadata-protocol: none
    Responsibility: n/a — not a defect card
    Thread-read: 6687641434
    Premise: holds
    Serial constraints cleared: #30871 closed completed 2026-11-06; sibling #30889 (seat 4) reuses the same target resolution in parallel, later lander merges origin/main

  7. objectstack-fleet commented on Nov 7, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#4 · marchtian · session_017ihL2dLQEXWGsE2SZ3Qn5F
    os-dev-report

    {
      "pr": {
        "number": 31137,
        "url": "https://github.com/objectstack-ai/objectstack/pull/31137",
        "head": "b57c03aa03ab30e9502660953d02fc93accf3d4c",
        "draft": true,
        "assignees": [
          "marchtian"
        ],
        "reviewers": []
      },
      "premise_still_valid": true,
      "gates": {
        "head": "b57c03aa",
        "merge_base": "5f003744",
        "origin_main_merged": "once, before pr_create: already up to date at 5f003744 (main has since moved 2 commits, neither touching a file this diff edits)",
        "local": [
          "pnpm --filter @objectstack/cli exec vitest run --project unit --maxWorkers=2 :: exit 0 (397 files / 5603 tests)",
          "pnpm --filter @objectstack/cli exec vitest run --project unit test/deploy.test.ts test/dev-deploy-key.test.ts test/environments-use-runtime.test.ts test/package-install-url-token-flags.test.ts :: exit 0 (4 files / 63 tests)",
          "pnpm --filter @objectstack/cli typecheck :: exit 0 (NODE_OPTIONS=--max-old-space-size=8192, the CLI's measured OOM at the default heap)",
          "eslint packages/cli --no-inline-config :: exit 0",
          "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack :: exit 0 (62 families)",
          "node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0",
          "node scripts/check-adr-0087-registration.mjs --self-test :: exit 0",
          "node scripts/check-changeset-no-major.mjs --base origin/main :: exit 0",
          "node scripts/check-closing-keyword-parity.mjs :: exit 0",
          "node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0",
          "node scripts/check-comment-mask-adoption.mjs :: exit 0",
          "node scripts/check-comment-mask-adoption.mjs --self-test :: exit 0",
          "node scripts/check-comment-mask-corpus.mjs :: exit 0",
          "node scripts/check-dts-emitted.mjs --self-test :: exit 0",
          "node scripts/check-empty-changeset.mjs --base origin/main :: exit 0",
          "node scripts/check-empty-changeset.mjs --self-test :: exit 0",
          "node scripts/check-issue-citations.mjs :: exit 0",
          "node scripts/check-plugin-teardown-shape.mjs :: exit 0",
          "node scripts/check-plugin-teardown-shape.mjs --self-test :: exit 0",
          "node scripts/ci/assert-turbo-remote-cache.mjs --self-test :: exit 0",
          "node scripts/ci/plan-test-core-shards.mjs --self-test :: exit 0",
          "node scripts/ci/prune-turbo-cache.mjs --self-test :: exit 0",
          "node scripts/ci/snapshot-next.mjs --self-test :: exit 0",
          "node scripts/docs-audit/check-affected-docs.mjs :: exit 0",
          "node scripts/docs-audit/check-drift-comment.mjs :: exit 0",
          "node scripts/pm/release-rehearsal-clone.mjs --self-test :: exit 0",
          "node scripts/release-pending-publish.mjs --self-test :: exit 0",
          "pnpm --filter @objectstack/spec run check:duration-unit-keys :: exit 0",
          "pnpm check:changeset-gate-self-tests :: exit 0",
          "pnpm check:ci-filter-parity :: exit 0",
          "pnpm check:cli-test-child-env :: exit 0",
          "pnpm check:closed-ceremony-text :: exit 0",
          "pnpm check:cross-package-test-inputs :: exit 0",
          "pnpm check:doc-authoring :: exit 0",
          "pnpm check:driver-memory-census :: exit 0",
          "pnpm check:dts-closure :: exit 0",
          "pnpm check:dual-build-cjs-loads :: exit 0",
          "pnpm check:engine-double-contract :: exit 0",
          "pnpm check:gitlink-declared :: exit 0",
          "pnpm check:i18n :: exit 0",
          "pnpm check:i18n-coverage :: exit 0",
          "pnpm check:i18n-walk-parity :: exit 0",
          "pnpm check:issue-citations :: exit 0",
          "pnpm check:keyed-text-bounds :: exit 0",
          "pnpm check:lean-entry-closure :: exit 0",
          "pnpm check:logger-receiver-detach :: exit 0",
          "pnpm check:nul-bytes :: exit 0",
          "pnpm check:objectql-double-limit :: exit 0",
          "pnpm check:org-identifier :: exit 0",
          "pnpm check:page-declaration-shape :: exit 0",
          "pnpm check:platform-object-tenancy-census :: exit 0",
          "pnpm check:published-files :: exit 0",
          "pnpm check:query-options-erasure :: exit 0",
          "pnpm check:refd-timer-probe :: exit 0",
          "pnpm check:registry-log-declared :: exit 0",
          "pnpm check:rest-log-spy-declared :: exit 0",
          "pnpm check:slot-lookup :: exit 0",
          "pnpm check:sourcemap-no-sources-content :: exit 0",
          "pnpm check:system-context-census :: exit 0",
          "pnpm check:test-source-alias :: exit 0",
          "pnpm check:tier-file-adoption :: exit 0",
          "pnpm check:type-check-coverage :: exit 0",
          "pnpm check:type-check-debt :: exit 0",
          "pnpm check:undeclared-dep-imports :: exit 0",
          "pnpm check:vouch-for-reference-callers :: exit 0",
          "pnpm check:watch-hint-literal :: exit 0",
          "pnpm check:where-matcher :: exit 0",
          "node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --ran ran.list :: exit 0 (62 derived, 62 run, 0 NOT-MEASURED)"
        ],
        "first_pass_exit_3_rebuilt_and_rerun": [
          "node scripts/check-plugin-teardown-shape.mjs --self-test (shallow clone: fetched pinned control 621a4876)",
          "pnpm check:dual-build-cjs-loads",
          "pnpm check:i18n",
          "pnpm check:i18n-coverage",
          "pnpm check:i18n-walk-parity",
          "pnpm check:type-check-debt (lock queue-timeout, rerun with slot)"
        ],
        "ablation": "runtime-target.ts isSameControlPlane(stored.url, url) -> true: test '-u naming another server never receives the remembered key' red (1 failed / 13 passed); restored, blob 900b6fe7 == HEAD",
        "ci": "in_progress at report time (PR just opened); not re-run locally",
        "integration_tier": "declared to CI: no spawn entry or integration-tier file touched"
      },
      "line_budget": {
        "changed_lines": 1148,
        "additions": 1103,
        "deletions": 45,
        "files": 12,
        "human_merge_threshold": 3000,
        "src": 617,
        "tests": 472,
        "changeset": 14,
        "skills_touched": false
      },
      "files_changed": [
        ".changeset/30888-deploy-self-hosted.md",
        "packages/cli/src/commands/deploy.ts",
        "packages/cli/src/commands/environments/switch.ts",
        "packages/cli/src/commands/environments/use-runtime.ts (new)",
        "packages/cli/src/commands/package/install.ts",
        "packages/cli/src/utils/cloud-config.ts",
        "packages/cli/src/utils/dev-deploy-key.ts",
        "packages/cli/src/utils/runtime-target.ts (new)",
        "packages/cli/test/deploy.test.ts",
        "packages/cli/test/dev-deploy-key.test.ts",
        "packages/cli/test/environments-use-runtime.test.ts (new)",
        "packages/cli/test/package-install-url-token-flags.test.ts"
      ],
      "deviations": [
        "Manual Community-image check NOT RUN: docker is not available in this container. Steps: docker run -p 8080:8080 ghcr.io/objectstack-ai/objectstack (os start); create an API key in Setup, API Keys; in a fresh scaffold: os environments use-runtime http://localhost:8080 --token KEY; os dev; press d; edit an object label; press d again; GET http://localhost:8080/api/v1/meta/object/NAME serves the second label. The epic's 'not verified' row (install-local on the Community image) stays NOT MEASURED.",
        "Q3 --confirm-upgrade pass-through NOT implemented: install-local on origin/main has the dry run (#30884 PR 1) but no Q3 refusal, no confirmSchemaUpgrade body key and no os package install --confirm-upgrade (#30884 PR 2, unclaimed, handoff 6688284693). The deploy prints the plan and stops on any refusal the door answers (dry run or install); no client-side copy of the verdict was added. Acceptance 'a Q3 refusal from the dry run stops the deploy' is pinned generically (a dry-run refusal stops before any install).",
        "The composed-artifact (EE) refusal shape is inferred from the card, not measured: objectstack-ai/cloud is not readable from this session (gh 403). A 404 is named with the composed-artifact sentence; any other refusal prints its code and message, exit 1.",
        "File surface: docs (content/docs/deployment/cli.mdx: os deploy section, environments table, os package install) and skills/objectstack-platform/references/operations.md are NOT updated: outside the claim's file surface. cloud/login.ts and cloud/logout.ts were NOT touched: the cloud.json write/delete helpers carry the runtime half instead.",
        "Ran pnpm install --frozen-lockfile and builds in the worktree; two lock queue-timeouts (exit 99) re-queued under the same slot."
      ],
      "open_questions": [
        {
          "question": "When #30884 PR 2 lands (Q3 refusal + confirmSchemaUpgrade), who forwards --confirm-upgrade through os deploy?",
          "options": [
            "A: #30884 PR 2 adds the one forwarded flag to os deploy beside --confirm-access-upgrade",
            "B: a follow-up card on this lane after PR 2 lands"
          ],
          "recommendation": "A, because PR 2 already edits os package install's flags and the deploy forwards them by the same in-process call; one line plus a test"
        },
        {
          "question": "Should os environments create --activate also flip the deploy target back to the cloud (only switch does today)?",
          "options": [
            "A: leave as is (switch and use-runtime are the pair)",
            "B: flip it too"
          ],
          "recommendation": "A, because create is provisioning; the deploy line names the runtime it targets"
        },
        {
          "question": "Who updates the docs and the platform skill row for use-runtime / self-hosted deploy?",
          "options": [
            "A: a docs follow-up card (cli.mdx + operations.md)",
            "B: amend this PR after the seat widens the file surface"
          ],
          "recommendation": "A, because skills/** carries its own budget and review"
        }
      ],
      "out_of_scope_findings": [
        "carrier: #30884 (PR 2) · Q3 refusal on install-local not on main; until it lands, a self-hosted deploy whose plan lists a suspected rename installs it (plan printed first), the same as os package install today · noted, not filed",
        "carrier: seat · card item 2 second half (a cloud-bound runtime deploying through the catalog) not done; follow-up · noted, not filed",
        "carrier: seat · docs/skills rows for os environments use-runtime and the self-hosted os deploy (content/docs/deployment/cli.mdx, skills/objectstack-platform/references/operations.md) · noted, not filed"
      ],
      "mcp_calls": "0",
      "api_writes": "2: POST /repos/objectstack-ai/objectstack/dispatches (fleet-write pr_create with assignees marchtian -> PR 31137); POST /repos/objectstack-ai/objectstack/issues/30888/comments via post-stamped (this report). git push is not a REST write."
    }
  8. objectstack-fleet commented on Nov 7, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#4 · marchtian · session_017ihL2dLQEXWGsE2SZ3Qn5F

    Landed · PR #31137 merged by the merge queue as d5fd948efce2f8cc3f3032773fbba9f2c69f8f43.

    Reading Value
    PR state merged, merged_at from the pulls API
    Card state closed completed by Fixes #30888

    pm:dispatched removed with this record. Follow-ups: docs + skill card #31143; Q3 --confirm-upgrade forward rides #30884 PR 2 (noted on PR #31137's ACCEPT 6708196796).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

domain:serverDomain lane — seat card indexed by label:pm:seatenhancementNew feature or requestpriority:p3repo:cloudCard routed whole to the cloud PM: needs cloud code, a producer or a fact (cloud owns it to closure)

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions