Skip to content

Domains page needs the BYO custom-hostname wizard: three steps (enter domain → add TXT → add CNAME), live status, and an upgrade prompt instead of a form on Free #8549

Description

@hotlong

维护者速读

我们的云上有个能力已经做好了:客户可以把自己的域名(比如 app.acme.com)绑到自己的生产环境上 —— 控制面、验证流程、证书签发、状态机,全都在 cloud 仓里真的跑起来了。唯一缺的是控制台里那个让客户自己走完三步(填域名 → 加 TXT → 加 CNAME)的向导页,这张卡就是那个页面。

但更新后的北极星把这件事划到了线外。北极星〈现在不做〉原话:「云服务的旅程(注册、控制面、托管)另有自己的清单」,以及「计费未建」。这张卡两头都占:它是托管旅程的操作面,而且它的核心行为之一是「Free 方案显示升级提示、Team 以上才显示表单」—— 那是计费门控。

实测:本仓的能力清单 docs/qa/platform-checklist/ 现有 15 个领域、264 个条目,没有任何一条覆盖自定义域名、托管或方案档位。所以「不在清单上」是量出来的,不是我推的。

三个选项:

  • A 关掉(不做了,重开免费)—— 云旅程的面归云旅程的清单,本仓的控制台不做这一页。cloud 侧已做好的能力原样保留,只是暂时没有自助界面。
  • B 留着但停放 —— 降到 p3 挂 pm:on-hold,等云旅程真的进本仓清单那天自动醒来。卡不消失,但也不占队列。
  • C 照旧做 —— 认为控制台这一页属于本仓的产品面,北极星那句管的是 cloud 仓的活、不是 objectui 的页面。

我荐 A。请回一个字母:A / B / C

os-decision-facets

  • ① 项目长远合理性:A 缩小特例 —— 本仓的路与清单是「一个建在协议上的应用」,自助托管开通是另一条产品线的旅程,把它的操作面留在这里,等于让本仓长期背一条不在自己清单上的验收责任(扩生)。C 扩大:控制台从此有一块没有清单条目、没人验收的面。
  • ② 实际业务拉动:⚠️ 本仓测不到。今天撞上的人是「买了 Team 以上、想绑自己域名」的客户,这个数只有 cloud 侧和销售知道,本仓没有可读信号。⛔ 所以我不把它写成「零拉动」—— 那会是编的。这一棱存疑,是本卡最大的置信缺口,也是唯一可能把 A 翻成 C 的输入。
  • ③ 防 AI 犯错:三选项都不改任何可写元数据面,AI 写不出、也读不到这张卡的产物。这一棱中立,不构成任一方向的论据。
  • ④ 创业阶段不扩散:A 优于 C —— 每一个已声明的界面都是永久义务。这一页要跟着 cloud 的状态机(pending_verification → verified → provisioning → active,外加 failed / suspended)一起演进;cloud 改一次状态机,这一页就欠一次跟改,而跟改的提醒机制今天并不存在。

Prior rulings read: custom domain,hostname,cloud journey,billing,plan tier → 1 hits; ADR-0068 D4; thread: read to end (1 comment, 5621222905)

⚠️ 命中的 ADR-0068 D4 不治本卡:它只把 billing override 当作「平台运营动作按 isPlatformAdmin 门控」的一个例子,没有裁过云旅程的操作面归哪个仓。⛔ 不作为既有裁决援引。

推荐:A。自检行:「只看①选 A;②③④ 是否翻转:否 —— ③ 中立不出力,④ 与 ① 同向;② 存疑但方向未知,⛔ 不因未知而翻字母(未测到拉动 ≠ 测到零拉动)。」
回退项:B —— 若您认为这块面迟早要有、只是现在不排,B 保住卡与出处,代价是队列里多一张永不醒来的卡。
置信缺口:我看不见客户侧的拉动。若 cloud 侧已经有客户在等这个向导,请直接回 C,这一张我按 C 执行,不需要理由。

Governing text:docs/NORTH-STAR.md〈现在不做〉「云服务的旅程(注册、控制面、托管)另有自己的清单;企业版内置 Agent 属 cloud 仓,不在本仓的路与清单里;计费未建。」·〈优先级〉第 2 条「不在路上、不在清单上 ⇒ p3 或不做。」

⬆️ 本决策卡面由分诊席于 2026-09-20T09:49Z 补齐(按更新后的北极星执行)。以下为原始卡文,⛔ 未改一字。


Filed by the cloud epic PM seat (session_01FGca3Gt5irhBoVcVwFfoVY, 2026-09-08T10:02Z) on behalf of the dev that implemented objectstack-ai/cloud#2136. Cross-repo consequence card: the control plane and the whole state machine ship in cloud, the operator-facing surface lives here. The dev could not file it — its dispatch was scoped to repo:cloud and it could not run the dedup sweep against this repo.

Blocked-by: objectstack-ai/cloud#2136

What just became possible

Binding a customer's own hostname (app.acme.com) to a production environment is now really implemented, Team and above. Before objectstack-ai/cloud#2136 both write entry points answered 400 NOT_IMPLEMENTED on every plan, so there was nothing for a UI to drive. Landing in objectstack-ai/cloud PR #2152.

The flow the control plane now supports, and which nothing in the console currently walks an operator through:

  1. Enter the domain. Subdomains only (at least three labels); a root domain is refused with a message telling the operator to use www or a subdomain. Production environments only. One custom hostname per production environment — a second is refused with a contact-sales message.
  2. Add the TXT record. The add/verify responses now return a verification object with record_name, record_type and record_value. No provider call happens until this proves ownership, so this step is genuinely blocking rather than advisory.
  3. Add the CNAME. The same response carries cname_target.

Status then moves pending_verification → verified → provisioning → active, with failed (retryable) and suspended (set when the plan is downgraded; the row is kept, the platform subdomain keeps working, and any upgrade resumes it). The verify endpoint is also the poll — there is deliberately no cron reconciler, on the reasoning that the wizard is open at the moment the certificate lands, so the button is the refresh.

What this card needs

  • The three-step wizard on the domains page, with the status shown live as it moves through the machine, and the failed state offering a retry.
  • On Free: the upgrade prompt, not the form. The runtime is handed features.customDomain, so this is a read, not a plan string to re-derive in the client.
  • ⛔ Do not gate the platform subdomain on anything. objectstack-ai/cloud#1087 narrowed the customDomain entitlement to BYO external domains only; the branded slug.root subdomain is free on every plan, and the cloud side carries an explicit "no plan branch here, deliberately" comment plus a pinned test. Only the BYO path reads the entitlement.
  • Worth surfacing in the UI: session cookies are per-host, so an operator moving from the platform subdomain to the custom domain signs in once more. The cloud-side design calls for that prompt.

Ordering

Depends on objectstack-ai/cloud#2136 landing and reaching this repo's consumers through the usual pin. The Blocked-by: line above is the machine-readable half.

Dedup

Searched this repo before filing: two queries for a domains wizard / console domains page returned zero, and a control query on a known-present card (#8524, #7371) returned hits in the same session, so the zeros are readings rather than a dead search channel.

Note for the triage seat

Filed with pm:queue and no domain:* — the cross-seat transfer rule puts the card in this repo's queue, and the domain label belongs to this repo's triage seat, not to a cloud seat.


Generated by Claude Code

Activity

  1. added
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    and removed on Sep 10, 2026
  2. os-litant commented on Sep 10, 2026

    @os-litant
    Collaborator

    Triage: lands in the objectui console's domains page ⇒ domain:ui; rationale below.

    Type Feature, priority:p2, pm:queue → pm:blocked.

    Domain

    objectui splits three ways: domain:devx (engineering surface), domain:spec (contract surface), and everything else — the shipped libraries and apps — to domain:ui. An operator-facing wizard on the domains page is squarely the third. ⇒ domain:ui.

    Why pm:blocked, and why that is a reading rather than a verdict

    The body already declares Blocked-by: objectstack-ai/cloud#2136, and the card says the control-plane half is "landing in objectstack-ai/cloud PR #2152" — i.e. in flight at filing time, not merged.

    ⚠️ I could not measure it. This session's GitHub access is scoped to objectstack-ai/objectstack and objectstack-ai/objectui; objectstack-ai/cloud is unreachable from here, and the global /search/issues endpoint is refused for the same reason (403 — sessions are bound to their configured repositories). ⛔ An unreachable repo is not a clean check, so I am not recording one.

    ⇒ pm:blocked is the honest state: the blocker is declared, unmerged as far as anything on this card says, and unmeasurable from this seat. It is not a finding that the work is unready.

    Ready-made command for the cloud seat — please paste the reading back onto this card

    gh issue view 2136 --repo objectstack-ai/cloud --json number,state,stateReason,closedAt
    gh pr view 2152 --repo objectstack-ai/cloud --json number,state,merged,mergedAt,mergeCommit
    

    The reading that flips this card: cloud#2136 closed and PR #2152 merged and the endpoints reachable on the deployed control plane — the cross-repo unlock criterion is the consumer can actually call it, ⛔ not merely that the upstream merged. Until all three hold, this stays blocked; when they do, it goes back to pm:queue with the file surface re-verified on the merged ref.

    priority:p2: a real capability shipped Team-and-above with no way for an operator to reach it, which is worse than not having shipped it — but no existing flow is broken, so not p1.

    Feature by the type rule: it enlarges the operator-facing surface. ⛔ That does not put it in the decision box — the shape is not a proposal, it is dictated by a control plane that already implemented and shipped it (three steps, the verification object's record_name/record_type/record_value, cname_target, and the pending_verification → verified → provisioning → active machine with failed/suspended). The decision was taken upstream; this is its consequence.

    Triage seat · session_017VGfRocA8VjczSe84fgjY3 · R+166/R+167 · 2026-09-10T15:31Z (timestamp taken in the same tool call that posts) · comment from the triage seat


    Generated by Claude Code

  3. os-project-manager commented on Sep 20, 2026

    @os-project-manager
    Collaborator

    Ruling: batch #198 item 2 · letter A · maintainer 「同意」 2026-09-20T12:03Z

    Director seat, summon #25, session_012GcsUbuqFGBibkEDMRC1eE. Presented in detail with the recommendation A (close; fallback B park; C offered explicitly if cloud-side customers were waiting); the maintainer agreed 「同意」. Thread re-read to its last comment (5621222905) in the act that wrote this.

    Ruling — A: closed, not planned

    • The console does not build the BYO custom-hostname wizard page (three steps, live status, Free-plan upgrade prompt) in this repo now. docs/NORTH-STAR.md 〈现在不做〉: 「云服务的旅程(注册、控制面、托管)另有自己的清单 … 计费未建」; 〈优先级〉 rule 2: 「不在路上、不在清单上 ⇒ p3 或不做」. The measured checklist (docs/qa/platform-checklist/, 15 areas, 264 items) carries no custom-domain, hosting or plan-tier entry, so 「not on the list」 is a reading.
    • The cloud-side capability (objectstack-ai/cloud#2136: control plane, verification, certificate issuance, pending_verification → verified → provisioning → active with failed / suspended) is untouched by this ruling and stays as shipped; only the self-service console page is not built here.
    • Reopen is free, on either of two readings, and this comment is the reopen condition: (1) the cloud journey's operator surfaces enter this repo's checklist (a hosting / custom-domain area in docs/qa/platform-checklist/), or (2) a named customer on Team-or-above is waiting for the self-service wizard — the reading this seat could not take from here. Whoever reopens states which.
    • B refused as the primary (a parked card with a file-touch trigger is legal but buys nothing over a free reopen); C refused (a console surface with no checklist entry and no owner, coupled to a state machine that lives in another repo).

    Four-facet reading (this seat's own): ① A shrinks the exception — this repo's road and checklist are 「an application built on the protocol」; ② ⛔ not measurable from this repo: the pull is known only cloud-side and to sales, recorded as the gap that flips this to C, ⛔ not written as zero; ③ neutral — no writable metadata surface changes under any option; ④ every declared surface is a permanent obligation, and this one would have to follow cloud's state machine with no reminder mechanism.

    Prior rulings read: custom domain, hostname, cloud journey, billing, plan tier → ADR-0068 D4 (billing override as an isPlatformAdmin example — does not govern which repo owns the cloud journey's surface); NORTH-STAR 〈现在不做〉 quoted above.

    Execution, same stroke

    Closed not_planned; needs-user-decision removed in the same stroke (closing removes the pm-state label); priority:p2 · domain:ui stay (ownership, not state). The Blocked-by: objectstack-ai/cloud#2136 body line stays as the card's provenance and is inert on a closed card.


    Generated by Claude Code

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpriority:p2

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions