跳到正文

Blog

一个自定义域名接入 Appaloft 的过程

从输入 hostname 到 DNS 授权、readback、TLS、路由生效和失败处理,Appaloft 如何把自定义域名接入做成可检查的部署流程。

AppaloftCustom DomainsDNSRoutingDeployment control

用户接入自定义域名时,通常只关心一个结果:app.example.com 能不能打开。

产品界面也很容易把这件事做成一个输入框:填 hostname,复制 CNAME,等待生效。这个体验应该简单,但系统内部不能只把它当成字符串保存下来。一个自定义域名开始接流量之前,Appaloft 需要确认几件事:这个 hostname 属于谁,谁授权了 DNS 改动,目标 route 是什么,证书和网关是否准备好,失败时应该停在哪里。

这篇接着一个部署 URL,应该说清楚什么会变往下讲。上一篇说 URL 本身也有承诺:immutable deployment URL、mutable alias、production alias、custom domain 和 blocked route 不能混在一个 url 字段里。今天只看 custom domain 这一路。

从 hostname 开始

流程的第一步很普通:用户在某个 project、environment 或 resource 里输入一个 hostname。

比如:

pocketbase.example.com

这时系统还不能立刻创建公开 route。它最多可以创建一个 pending domain binding,并开始做几类检查:

  • hostname 格式是否有效;
  • 是否落在平台禁止或保留的后缀里;
  • 当前组织、项目或资源是否有权申请这个 binding;
  • 同一个 hostname 或 path 是否已经被其他 active resource 使用;
  • 目标 deployment、resource 或 static artifact 是否有可路由的 target;
  • 需要用户走自动 DNS 授权,还是展示 manual DNS records。

这些检查看起来琐碎,但它们决定了后面能不能 fail closed。如果两个 resource 同时争用同一个 hostname,默认结果应该是停下来,而不是抢占。如果用户输入的是 Appaloft 官方默认域名后缀,系统也不应该把它当成普通 customer domain。

Appaloft 的 routing docs 里把这类 URL 分开命名:official default subdomain、immutable preview/deployment URL、mutable alias、production alias、custom domain binding。名字多一点,但后续状态会清楚很多。

DNS 授权有两条路

Appaloft 当前为 Cloudflare DNS 设计的自助路径是 Domain Connect。

用户点击 “Connect Cloudflare DNS” 后,Appaloft 生成一份确定性的 DNS plan,浏览器跳到 Cloudflare 的 Domain Connect 授权流程。用户在 provider 页面确认,Cloudflare 应用 template,Appaloft 再回来做 DNS readback。

这个流程里有一个重要边界:Domain Connect 是一次用户授权的 DNS setup flow。Appaloft 不需要为了这个场景长期保存用户的 Cloudflare token。授权完成也不等于 Appaloft 盲目信任浏览器 redirect;最后仍然要通过 DNS readback 证明记录真的出现了。

另一条路是 manual DNS。

如果 provider 不支持 Domain Connect,用户取消授权,或者当前域名只能手动配置,Appaloft 应该展示一组确定性的 DNS records:要添加什么 record,record type 是什么,host/name 怎么填,target 指向哪里,TXT verification 需要什么值。用户改完 DNS 后,再回到 Appaloft 刷新 readback。

这条路慢一些,但很重要。自定义域名不能依赖单一 provider,也不能因为自动授权不可用就鼓励用户复制不透明命令。manual DNS 是 fallback,也是诊断材料。

平台凭证和用户域名要分开

Appaloft 可以拥有自己的 official domain provider。例如 Cloud 的默认访问域名可以由平台控制,走 Appaloft 自己的 wildcard DNS、TLS 和 gateway。

这不代表平台凭证可以替用户管理 example.com

用户的 custom domain 要靠用户侧授权或 DNS ownership proof。Appaloft runtime 里的 Cloudflare token、Worker route、官方后缀配置,只能证明平台能管理 Appaloft-owned zone。它们不能证明某个 tenant 有权把 pocketbase.example.com 指向自己的 resource。

这条边界在 AI 场景里更明显。Agent 可以帮用户发起 “把这个 resource 绑定到这个域名” 的意图,也可以调用 CLI/API 查询 DNS readiness。它不应该拿到长期 provider credential,更不应该绕过产品的 domain binding flow 直接改 DNS。

所以 Appaloft 的 Connections 模型把事情拆开:

  • ConnectorDefinition 描述一种 provider 能力,例如 Cloudflare DNS;
  • Connection 记录某个 owner 已经授权的连接实例;
  • ConnectionCapability 描述这个 connection 能执行的动作,例如 dns.records.apply
  • CredentialGrant 只在对应生命周期里处理授权材料;
  • DNS 页面、CLI alias 和 AI tools 都委托给同一套 connection capability。

用户看到的是一个域名流程,系统内部看到的是 owner-scoped capability。

先生成 DNS plan

当用户准备应用 DNS 改动时,Appaloft 应该先给出 plan。

DNS plan 至少要说清楚:

  • 要创建或验证哪些 records;
  • record 指向哪个 route target;
  • 是否需要 TXT ownership verification;
  • 是否会覆盖已有 record;
  • 是否发现冲突;
  • 哪个 connection 或 manual path 会执行;
  • apply 之后需要怎样 readback;
  • 失败时用户能看到什么诊断。

这个 plan 不应该包含 token、provider 原始响应、内部 gateway secret 或任何像 secret 的值。对于 automatic path,plan 可以告诉用户会跳转到 provider 授权。对于 manual path,plan 可以展示可复制的 DNS records。

如果 plan 发现已有 record 与 Appaloft 请求冲突,默认应该停下。覆盖用户自己的 DNS record 是高风险动作,不能藏在“一键接入”的 happy path 里。

这和 Blueprint install plan 的原则很像:先让用户看到平台准备做什么,再进入 accepted apply。

Readback 决定能不能继续

DNS apply 之后,最容易写错的地方是把 “provider API 返回成功” 当成完成。

Appaloft 更关心 readback。

Domain Connect 完成后,要重新查 DNS。Manual DNS 配好后,也要重新查 DNS。DNS 传播可能需要时间,CAA 或证书签发可能有独立问题,CDN 或 gateway route 也可能还没准备好。产品应该把这些状态拆开:

  • DNS record missing;
  • DNS record present but target mismatch;
  • ownership TXT missing;
  • conflicting active route;
  • TLS pending;
  • route target unavailable;
  • HTTP reachability pending;
  • ready。

这比一句 “waiting for DNS” 细一些,但对用户和支持团队都更有用。用户知道自己是在等传播,还是填错了 target。支持团队能看到 DNS、TLS、route、HTTP readback 卡在哪一层。AI agent 也不会因为某个 shallow check 通过就宣布完成。

路由生效是另一个阶段

DNS ready 后,还要把 domain binding 接到 route target。

一个理想的 route activation flow 大概是:

create route intent
  -> verify ownership or official suffix authority
  -> create DNS/CDN/gateway route
  -> provision/attach TLS
  -> activate route target
  -> verify HTTP reachability
  -> publish route read model

Cloud 当前的生产 routing 还有一些外部系统 gap:真实 DNS/TLS/CDN/ACME/readback 必须有 go-live evidence,不能在文档或文章里写成已经全部自动化完成。已有的规则是:route model 和 readiness 要先把边界表达清楚,真实 provider mutation 只在批准的生产路径里发生。

这也是为什么 Appaloft 不把 domain binding 写成一个简单 boolean。verified: true 不够。更有用的是状态和证据:谁验证了,验证了哪个 hostname,target 是什么,证书状态如何,route readback 是否通过,是否有冲突,是否处于 blocked/disabled。

失败时要安全

自定义域名流程里,失败是常态。DNS 记录还没传播,用户填了 apex 和 subdomain 混合配置,已有记录冲突,证书没签下来,route target 被删除,或者 provider 授权窗口过期。

失败时 Appaloft 应该保留三个原则。

第一,失败不要创建半激活 route。没有 ownership proof,就不要接流量。

第二,诊断要具体,但不能泄露旧 target、内部 host、provider account secret、raw token 或 request body。用户需要知道该改哪条 DNS record,不需要看到平台内部的 gateway 细节。

第三,cleanup 要保守。用户删除 binding 后,平台可以清理自己创建或标记过的资源,但不能按宽泛条件删除用户手工创建的 DNS 数据。能自动清理就显示清理结果,不能自动清理就给出明确的 manual cleanup gap。

这和部署里的 blocked route 规则是一组问题。一个不可用的 domain 也应该安全地不可用。

CLI 和 AI 看到同一条路径

自定义域名经常会出现在 Web Console 之外。

用户可能在 CLI 里跑:

appaloft dns connect example.com --provider cloudflare
appaloft dns plan example.com --hostname pocketbase.example.com --target ...
appaloft domain-binding dns-readiness ...

AI agent 也可能从 MCP tool 或 operation catalog 调用同样的能力。

这些入口不应该各自发明一条逻辑。Web 的 “Connect Cloudflare DNS” 按钮、CLI 的 appaloft dns connect、domain binding readiness、future MCP tool,都应该回到同一套 connector/category/capability、authz、audit、plan、accept、apply 和 readback。

这样做的好处很实际:如果 Web 上会检查冲突,CLI 也会检查;如果 Web 不会打印 secret,agent tool 也不会拿到 secret;如果一个 DNS apply 需要 owner/admin 权限,自动化入口也不能跳过去。

Agent 可以帮助用户走流程,但流程本身仍然属于产品。

用户最后只需要一个清楚状态

完整流程听起来长:

  1. 输入 hostname。
  2. 创建 pending binding。
  3. 检查冲突和权限。
  4. 生成 DNS plan。
  5. 选择 Domain Connect 或 manual DNS。
  6. 应用或等待用户配置 records。
  7. 做 DNS ownership/readback。
  8. 准备 TLS 和 gateway route。
  9. 激活 route target。
  10. 做 HTTP reachability readback。
  11. 发布 ready 状态和诊断。

产品不需要把每一步都变成用户负担。好的界面可以只显示当前要做的动作:连接 DNS、复制 records、刷新检查、等待证书、完成。

但控制面需要保存这些细节。否则出了问题,只会剩下一句 “domain not ready”。这句话无法告诉用户下一步该做什么,也无法让 agent、CLI、workflow 或支持团队继续接手。

Appaloft 想要的自定义域名接入,是一个可以被人和工具共同理解的流程:先确认所有权,再计划 DNS 改动,再 readback,再激活 route。最后用户得到的当然还是一个能打开的 URL。区别在于,这个 URL 背后有证据,有边界,也知道自己什么时候不该接流量。