Skip to content

organization/create 的闸门读的是被降级的 OS_MULTI_ORG_ENABLED,不是权威的 OS_TENANCY_POSTURE —— 只设权威 knob 的有墙部署,引导式「创建工作区」是死路 #5233

Description

@os-zhuang

发现于 cloud#1012 的端到端验收(cloud 侧 pin bump 到 586d6f70 之后,真实 objectstack serve + 真实 HTTP 探针)。这是 cloud#1020 那个缺陷形状的原样重演,只是换了一个站点。

缺陷

packages/plugins/plugin-auth/src/auth-manager.ts 的 organizationHooks.beforeCreateOrganization 用 resolveMultiOrgEnabled() 判断「本部署是不是单组织」:

beforeCreateOrganization: async () => {
  if (!resolveMultiOrgEnabled()) {
    throw new APIError('FORBIDDEN', {
      message: 'Creating additional organizations is disabled on this deployment.',
    });
  }
},

而 packages/types/src/env.ts 里这两个函数的关系是(ADR-0105 D1):

export function resolveMultiOrgEnabled(): boolean {
  const raw = readEnvWithDeprecation('OS_MULTI_ORG_ENABLED', []);
  return String(raw ?? 'false').toLowerCase() !== 'false';   // 只读被降级的布尔,缺省 false
}

export function resolveTenancyPosture(): TenancyPosture {
  // OS_TENANCY_POSTURE 是 canonical knob;只有它 unset 时才回落到上面那个布尔
  ...
  return resolveMultiOrgEnabled() ? 'isolated' : 'single';
}

OS_TENANCY_POSTURE 是权威 knob,OS_MULTI_ORG_ENABLED 是被它取代、只在前者 unset 时才生效的遗留布尔。于是一个只设权威 knob的部署:

  • 组织墙整套挂上(OrganizationsPlugin 注册、tenancy 服务 isolated、ADR-0093 D5 不报警、许可闸门通过);
  • 但 POST /api/v1/auth/organization/create 依然 403 “Creating additional organizations is disabled on this deployment.”

resolveMultiOrgEnabled() 的文档注释还写着「the auth manager's /auth/config feature flag and org-create guard … MUST call this」—— 那句话写在 ADR-0105 D1 把该布尔降级之前,闸门是照着过期契约走的。

复现(真实 serve,非推断)

apps/objectos-ee(cloud 仓)+ 真实 Ed25519 企业许可,两次真实 POST /api/v1/auth/sign-up/email,第二个用户走引导式建工作区:

配置(其余相同) 组织墙 organization/create
OS_TENANCY_POSTURE=isolated 挂上 403 disabled on this deployment
OS_TENANCY_POSTURE=isolated + OS_MULTI_ORG_ENABLED=true 挂上 200 → 用户成为该组织 owner;随后 invite-member / accept-invitation 全部 200,两个自助注册用户互相看不见对方的组织

即:遗留 knob 能用,权威 knob 不能用——正好是声明的契约的反面。

为什么这条要紧(不是纸面洁癖)

cloud#1012 的维护者决策(方案 B)是:自助注册不自动开通组织,org-less 用户由 console 的 RequireOrganization 导到引导式「Create your workspace」自己命名。那条路径就是这个 403。 所以在一台按文档只设 OS_TENANCY_POSTURE=isolated 的 objectos-ee 上,新注册用户既没有组织、也无法创建组织——平台对「新注册之后怎么办」的唯一答案是死路。

sys_member 侧一切正常(membership_policy 的 #5152 落地经复核工作正常),坏的只有这一个闸门。

建议修法

beforeCreateOrganization 改判 resolveTenancyPosture() !== 'single'(意图完全不变——「单组织模式下拒绝创建」——只是改成读权威 knob)。顺带把 resolveMultiOrgEnabled() 那段「every site … MUST call this」的注释订正到 ADR-0105 D1 之后的事实,并普查同一文件/包内其它读该布尔当作「是不是多组织」的站点(objectql/src/registry.ts:735、runtime/src/app-plugin.ts:1045 至少要各自判一次是不是同一类误读)。

对 cloud 控制面无影响:它的 worker 本来就显式设 OS_MULTI_ORG_ENABLED=true,两种读法同解。

出处

  • 探针与判定:cloud#1012 本轮开发(会话 session_015W6nhsDrz6zWQc8je12a1t),未指派,按 Prime Directive chore: version packages #10 归档到本仓。
  • 同形状前案:cloud#1020(EE 许可闸门写 OS_MULTI_ORG_ENABLED 而 serve 读 posture)。当时的结论就是「不能照它的形状再来一次」。

Activity

  1. claude commented on Aug 4, 2026

    @claude
    Contributor

    认领:PM 循环第 15 轮
    会话:session_015W6nhsDrz6zWQc8je12a1t
    分支:claude/issue-5233-org-gate-posture
    Worktree:objectstack-issue-5233
    文件面:packages/plugins/plugin-auth/src/auth-manager.ts(org 闸门 + /auth/config flag)+ packages/types 注释修正(如需)+ 测试 + changeset;域 domain:identity


    Generated by Claude Code

  2. claude commented on Aug 4, 2026

    @claude
    Contributor

    PM 验收(第 15 轮 + 返工,identity 车道):ACCEPT — PR #5267,CI 绿后转 ready 入队

    (原 dev 报告未达 — agent 死于报告前;按 GitHub 实况复核,结论如下)

    • 两个站点一次修齐:beforeCreateOrganization 改判 postureEnforcesWall(resolveTenancyPosture())(与 ADR-0093 D5 boot guard 同 knob 同谓词,消费 spec 既有函数,零 spec 改动);/auth/config 的无服务回落同步换轨。语义边界讲得清楚:判的是「操作者请求的 posture」,与旧布尔同层级,单组织仍拒绝
    • env.ts ±27/13 核为纯注释(授权面内):把带偏两个站点的「MUST call this」过期指引反转为「用它回答 multi-org 就是 bug」,点名 cloud#1020 与 organization/create 的闸门读的是被降级的 OS_MULTI_ORG_ENABLED,不是权威的 OS_TENANCY_POSTURE —— 只设权威 knob 的有墙部署,引导式「创建工作区」是死路 #5233 两个由它引发的缺陷,并写明合法余留调用方 —— 这类「毒注释解毒」与代码修复同 PR 是正确的
    • 测试设计针对盲区:19 例真实 better-auth 管线(修前 10 红);每场景同时断言 /auth/config flag,「flag 必须精确预测路由答案」钉成不变量 —— 本 bug 隐形正因两者从未被比对;兼容表五态齐全(存量部署零配置变更)
    • 返工(engine double 谓词接线,7 行)已过门禁,20 pinned ✓
    • docs 核查产出一单:tenancy-modes.mdx 整页是同族文档漂移(只教被降级布尔,权威 knob 零提及)—— 已代立交 devx 车道(见上)

    下游:cloud PR #1091 已钉的 e2e 在 cloud 下次 pin bump 拾取本修复后翻 200,闭环完成。


    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

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions