# AgentTeam 组织与权限规范

> Canonical path: `specs/organization/organization.md`
> 使用手册：如果你想看“当前 org 入口和实际操作路径”，请先读 [../../docs/organizations/usage-guide.md](../../docs/organizations/usage-guide.md)。

> 状态：目标设计
> 对齐基线：`docs/architecture-spec.md`
> 适用范围：`agentteam-core`、可选 `agentteam-org` provider、独立业务服务

---

## 1. 范围

本规范定义 AgentTeam 中组织与权限的实现边界，包括：

- `agentteam-core` 必须内建的最小授权内核
- `agentteam-org` 作为可选治理/目录/provider 的职责
- 组织能力如何叠加到现有 workspace/message/runtime 模型之上
- 权限如何在 human、agent、machine、service 之间被表达、判定、签发和传递

本规范不要求在 core 中内建完整企业 RBAC 系统，也不要求所有部署都启用 organization。

---

## 2. 目标

### 2.1 必须满足

- 保持 `agentteam-core` 与 `docs/architecture-spec.md` 一致：
  - core 只承载 `messaging + identity + minimal authz kernel`
- 保持与当前 public workspace role projection 一致：
  - `owner | admin | member` 继续作为 workspace human role 的兼容外露语义
- 支持在不启用完整 organization governance provider 的情况下独立运行；此时系统仍使用最小 personal organization 作为 workspace owning boundary
- 支持未来接入 richer org / directory / policy provider，而不重写 core
- 支持 service install / grant / capability token 作为长期跨服务授权模型
- 启用 `agentteam-org` provider 时，其 provider-owned schema、migrations、schema verify、startup wiring、runtime path、以及回归证据必须和 core 使用同一条 TiDB 路径

### 2.2 不做

- 不在 core 中引入通用的全系统 RBAC 资源目录
- 不在 core 中引入 per-message ACL
- 不在 core 中引入 deny rule / policy DSL / OPA / Rego
- 不让 public API 的热路径依赖远程授权服务
- 不让 machine token 或 agent token 获得 human API 权限

---

## 3. 设计原则

### 3.1 Workspace First

`workspace` 是 AgentTeam 的基础协作边界。

- 成员、频道、DM、thread、machine、agent 都首先归属于 workspace
- organization 是治理叠加层，不替代 workspace

### 3.2 Rich Organization Governance Optional

完整 organization governance 是可选治理能力；但 persisted workspace 必须拥有一个 organization owner。

- 没有完整 governance provider 时，系统必须完整可用，并通过最小 personal organization 承载 workspace ownership
- 启用完整 provider 后，organization 可以提供 people、team、org chart、directory、SSO、workspace grant、provider grant 等治理能力
- core/runtime 不应依赖完整企业治理能力，但 storage 必须依赖“所有 workspace 都属于一个 organization”这一 ownership 前提

### 3.2.1 Optional 不等于独立存储路径

`agentteam-org` 的完整治理能力是 optional provider，但 optional 只表示“不是所有 deployment 都必须启用企业治理能力”，不表示“一旦启用就可以拥有独立的 core storage 兼容层”。

硬规则：

- 若 deployment 没有启用完整 `agentteam-org` governance，core 仍必须通过最小 personal organization 完整可用
- 若 deployment 启用了 `agentteam-org`，则 provider 必须具备 TiDB-compatible 的 schema / startup / runtime / regression 路径
- “provider 是 optional 的”不能作为把 org portability 排除在 TiDB-only runtime contract 之外的理由

### 3.3 Principal / Member 分层

- `principal_id`：全局身份
- `member_id`：workspace 内消息身份

跨 workspace、跨服务、审计、grant、capability 依赖 `principal_id`；
消息作者、@mention、DM 寻址、channel membership、thread 可见性依赖 `member_id`。

### 3.4 Message-Space First

AgentTeam 的核心是消息，因此消息访问控制必须优先建立在消息空间事实之上：

- workspace access
- channel membership
- DM 参与关系
- thread 继承关系

组织/权限 provider 可以补充 grant，但不能替代消息空间本身的语义。

### 3.5 Capability over Implicit Trust

跨服务调用、agent 运行时调用、service 调用不应长期依赖隐式 header 信任。

长期模型应是：

- human token 负责认证
- machine token 负责控制面
- agent token 负责 agent data plane
- service capability token 负责跨服务上下文与 scope

### 3.6 Local Stable IDs

外部 authority 不是主键来源。

AgentTeam 内部所有核心对象都保留本地稳定 ID：

- user / principal
- workspace
- member
- machine
- agent
- service / service_installation
- organization（若启用 provider）

外部系统 ID 只存在于映射或投影层。

---

## 4. 系统边界

| 关注点 | `agentteam-core` | `agentteam-org` provider | 独立业务服务 |
|--------|------------------|-------------------------|--------------|
| Human 认证 / session / token | 是 | 可补充身份源 | 否 |
| Workspace membership | 是 | 可补充来源/治理 | 否 |
| Channel / DM / thread 可见性 | 是 | 不替代 | 否 |
| Act-as 规则 | 是 | 不替代 | 否 |
| Machine / Agent / Service capability issuance | 是 | 否 | 消费 |
| Service install / grant contract | 是 | 可补充审批规则 | 消费 |
| Organization lifecycle | 最小 personal organization bootstrap / ownership | 完整治理生命周期 | 否 |
| Org chart / OrgUnit | 否 | 是 | 否 |
| Org team / org group / nested group | 否 | 是 | 否 |
| Custom role / org-level RBAC | 否 | 是 | 否 |
| External directory / SSO / SCIM | 否 | 是 | 否 |
| Task / Memory / Cron 自身资源授权 | 否 | 可补充策略 | 是 |

---

## 5. 身份模型

### 5.1 Principal 类型

长期至少支持以下 principal：

| Principal Type | 是否全局身份 | 是否直接参与消息 | 说明 |
|----------------|--------------|------------------|------|
| `user` | 是 | 通过 `member` 参与 | 人类账号 |
| `agent` | 是 | 通过 `member` 参与 | AI actor |
| `service` | 是 | 通过 `member` 参与 | Task / Memory / Cron / Org 等服务 |
| `machine` | 是 | 否 | daemon 控制面主体 |

### 5.2 Member 语义

`member` 是 messaging projection，不是 principal 本体。

- 一个 principal 可以在多个 workspace 中拥有多个 member
- human member 是某个 user 在某个 workspace 中的消息身份
- agent member 是某个 agent 在某个 workspace 中的消息身份
- service member 是某个 service installation 在某个 workspace 中的消息身份

### 5.3 当前实现约束

当前仓库中：

- human member 持有 `workspace_role`
- agent member 持有 agent 的消息身份
- service member 尚未完全实现，但必须按与 human / agent 一致的 member 语义扩展

---

## 6. 组织模型

### 6.1 Organization 的位置

organization 是治理对象，不是消息对象。

organization 负责：

- 治理边界
- 组织成员状态
- org-level admin
- org hierarchy / org chart
- external directory / SSO / SCIM 绑定
- workspace ownership 与 grant 策略

organization 不直接替代：

- workspace
- channel
- DM
- thread
- message author identity

### 6.1.1 用户可见术语

组织相关产品面必须使用稳定、可解释的用户术语，不把底层模型名或历史命名直接暴露为主入口标签。

规范要求：

- 用户侧统一使用 `Workspace` 作为协作边界名词；organization 相关页面不应再把 `Server` 作为主标签
- organization 侧的人目录统一对外显示为 `People`
- `organization_unit` 的用户可见入口统一显示为 `Org Chart`
- `Teams` 继续表示扁平协作/授权分组，不承担 org hierarchy 主标签职责

稳定映射关系如下：

- `People` -> `organization_member` 及其 human `organization_member` 治理投影
- `Workspaces` -> workspace ownership、grant 管理与当前授权视图
- `Org Chart` -> `organization_unit` 树与目录层级

历史命名只允许存在于旧数据、历史截图或迁移说明中，不应继续作为 source-of-truth 文案。

### 6.2 Workspace 与 Organization 的关系

规范要求：

- 每个 workspace 必须且只属于一个 organization
- workspace ownership 由 `workspaces.organization_id` 表达，并且必须是 `NOT NULL`
- organization 不要求拥有所有 workspace；一个 deployment 可以有多个 organization，每个 organization 拥有自己的 workspace 集合
- 普通产品流程不支持把 workspace 从一个 organization 迁移到另一个 organization
- workspace 访问权不由 ownership 自动授予全部 organization member，仍由 workspace membership、organization member grant、organization team grant 与 projection 决定

推荐实现：

- `workspaces.organization_id` 是 workspace ownership 的唯一事实来源
- `organization_settings.default_workspace_id` 表达 organization 默认 workspace
- 不再使用独立 workspace binding 表表达 organization 与 workspace 的关系
- 组织侧 workspace 管理主路径应是“在 organization 内创建、列出、管理 workspace”，而不是 bind/unbind 任意已有 workspace

这样可以同时支持：

- 无完整 governance provider 的小规模部署，通过默认 personal organization 提供 workspace ownership
- 单 organization 管理多个 workspace 的企业模式
- 未来 provider shadow / authority 模式

### 6.3 Organization 基础对象

当启用 `agentteam-org` provider 且产品启用 org collaboration 模式时，建议至少具备以下对象：

- `organization`
- `organization_settings`
- `organization_team`
- `organization_member`

可选对象：

- `organization_member_team_membership`
- `organization_unit`
- `organization_external_identity`
- `organization_role` / `organization_role_binding`

其中：

- `organization_member` 是 org 侧对外暴露的人类成员事实源，绑定一个本地 `user`
- `organization_unit` 用于 org chart / 导航 / HR 同步
- `organization_team` 用于协作分组、workspace/channel/service grant 编排
- 两者不是同一对象，也不应在 core 中混用

### 6.3.1 Organization Collaboration Hierarchy

安装 org 组件的含义，是启用可选 `agentteam-org` provider，并在现有 workspace 模型之上叠加组织目录、团队编排与授权投影能力。

规范中的稳定协作层级如下：

```text
organization
  -> organization_member
    -> 1 human principal (`user`)
  -> organization_team
    -> organization_member
  -> organization_unit
    -> organization_member
  -> workspace

workspace
  -> member
  -> machine
  -> installed agent
  -> channel / dm / thread
```

其中：

- org layer 负责人类目录、team/unit 归属、组织内发现、workspace grant 编排
- workspace layer 负责真实消息空间、member 物化、machine runtime、agent 运行实例
- “把 org 中的人加入 workspace” 的稳定语义，是把某个 `organization_member` 加入 workspace，并据此物化 human member

organization 是治理与编排边界，workspace 仍然是聊天、协作与 runtime 的实际边界。

### 6.3.2 Member 模型

`organization_member` 是 organization 内的人类成员事实源。

规范要求：

- 一个 organization member 必须且只绑定一个 human principal（`user`）
- provider 可以额外支持 secondary / derived team membership，但 `primary_team` 是必需事实
- organization member 可以被直接授予 workspace access，也可以通过 team grant 派生获得
- organization member 自身应具备 `status = active | suspended | inactive`

建议至少具备以下字段或等价语义：

- `organization_id`
- `user_id`
- `primary_team_id`
- `status`
- `source = local | invite | sync | sso`
- `default_workspace_role`

补充约束：

- human 通过自身 session/JWT 登录与切换 workspace；agent 永远不复用 human token
- organization member 是 user 在 org 中的治理身份；workspace member 是该 user 在某个 workspace 下的消息投影
- `source = sso` 表示该 organization member 的创建/绑定入口来自 SSO 身份流程；这不自动等价于该成员已完成 org chart、team、unit 的目录同步
- provider 同时接入 SSO 与外部目录时，不应假定二者共享同一个稳定外部标识；认证绑定与目录绑定可以是两套映射关系

### 6.3.3 Member 到 Workspace 的物化规则

把 org 中的人加入 workspace，本质上是把 `organization_member` 加入 workspace，而不是直接在 core 中创建一组脱离 org 的本地 member。

规范要求：

- workspace access 可以直接授予 `organization_member`，也可以授予 `organization_team`
- 当 organization member 获得某个 workspace 的有效 access 后，core 必须确保其 human member 被物化或重新激活
- agent 进入 workspace 必须走 workspace-scoped agent 创建、安装或运行时配置流程
- agent materialize 后，才形成 workspace-scoped 的 agent member、runtime binding、channel membership 与 act-as 事实
- 当前实现可以继续把 `agents` 表视为 workspace-scoped installation projection；org provider 不要求 core 立即把所有 agent 改造成全局表

推荐产品体验：

- 允许一步式流程完成“选择 workspace -> 选择 org member -> 提交”
- agent 管理留在 workspace agent/runtime 体验内，不作为组织成员授权的隐式副作用

workspace access 的稳定准入路径要求固定为：

- org 侧准入只有两类：`direct member grant` 与 `team grant`
- workspace 内的 `Add from Organization` 只是 `direct member grant` 的一个 UI 入口，不是第三种授权模型
- workspace 本地 `Invite External by Email/Mobile` 是独立的 workspace 准入路径；其 target 解析、链接生成与接受校验必须遵守 [`Invite Identity Target Spec`](../identity/invite-identity.md)；接受邀请时必须先确保受邀 user 成为该 workspace 所属 organization 的普通 member，再物化 workspace human member

默认规则建议固定为：

- human access 自动物化
- agent access 不随 organization member grant 自动物化

organization admin 管理 organization-owned workspace access 但不自动获得 workspace 内容访问的完整规则，见 [组织侧 Workspace 访问管理规范](./workspace-access-management.md)。

### 6.3.4 Workspace Switching

workspace switch 是一等产品能力，不依赖 organization 作为当前上下文。

规范要求：

- human 可以在其拥有 access 的所有 workspace 之间切换
- 这些 workspace 可以来自 direct invite、用户 onboarding 自动创建的 personal organization，也可以来自 org-derived grant
- UI 可以按 organization 或 team 分组展示 workspace，但最终被选中的上下文始终是 workspace
- public API 继续以 workspace id 作为上下文选择键，不引入 org header 作为前置条件

这意味着：

- organization 负责“你可以看到哪些 workspace、可以把哪些 org member 加入哪些 workspace”
- workspace 负责“当前你正在哪个协作空间中聊天、操作 machine、运行 agent”

### 6.4 Organization Settings / Governance Defaults

`organization_settings` 用于承载组织级治理默认值。

建议至少包括以下稳定字段：

- `status = active | suspended | deleting | deleted`
- `timezone`
- `join_policy = disabled | admin_invite | invite_code | sso_auto | directory_managed`
- `directory_backend = embedded | external_shadow | external_authority`
- `workspace_bootstrap_policy`
- `default_workspace_id`
- `service_install_policy`

其中：

- `sso_auto` 表示首次成功 SSO 登录后，provider 可以自动创建或绑定本地 `organization_member`；该流程默认只解决“身份进入 organization”，不自动视为已获得 org chart
- `sso_auto` 也可以扩展为“基于已验证外部身份自动加入 organization”，例如：用户通过 vendor SSO 登录，或使用已验证的邮箱/手机号登录后，被 provider 匹配到唯一的目录用户并自动加入对应 organization
- `directory_managed` 表示 team / unit / membership 等目录对象主要由外部目录驱动，本地通常只保留 shadow、explainability 或受限治理能力
- `workspace_bootstrap_policy` 用于决定新加入成员默认获得哪些 workspace grant
- `default_workspace_id` 指向该 organization 的默认 workspace；该字段只表达默认入口，不表达 workspace ownership
- `service_install_policy` 用于决定 service installation 是否需要显式审批、默认由谁审批、默认 scope 是否可自动批准

这些字段属于组织治理默认值，不直接替代 workspace 层配置。

对于低频、非授权热路径配置，provider 可以额外维护稀疏设置层，例如：

- `organization_setting(key, value)`

当 organization 接入外部身份/目录 vendor 时，建议至少允许表达以下 provider 维度：

- `auth_provider`
  - 负责登录、SSO、join / bind
- `directory_provider`
  - 负责 org chart、directory membership、team / unit sync

二者可以是同一个 vendor，也可以不是同一个 vendor。
例如：

- `auth_provider = feishu`
- `directory_provider = feishu`
- `auth_provider = oidc`
- `directory_provider = scim`

这些 provider 维度不要求立即成为 `organization_settings` 的稳定热路径字段；
provider 可以先放在稀疏设置层或独立 binding 表中，只要契约清晰且不泄漏到 core。

但 core 只消费稳定投影字段，不直接依赖 provider 的任意 key-value 配置。

### 6.5 Organization Member 生命周期

`organization_member` 是 human principal 在 organization 中的目录/治理身份。

建议至少具备以下字段或等价语义：

- `organization_member_id`
- `organization_id`
- `user_id`
- `status = active | suspended | inactive`
- `source = local | invite | code | sync | sso`
- `joined_at`
- `invited_by_principal_id?`
- `last_synced_at?`
- `disabled_at?`

规范要求：

- 只有 `active` 的 organization member 才能被投影为 team member、workspace grant、channel grant、service approval 权限
- `suspended` 和 `inactive` 成员不得继续产生新的 `effective_grant`
- organization member 生命周期不等于 workspace member 生命周期
- organization_member 被停用时，不得物理删除历史消息身份

agent roster 和 runtime installation 属于 workspace agent 体系，不属于 organization people 目录。

### 6.6 Organization Bootstrap / Join / Invitation

organization 层需要明确区分三类动作：

1. 系统创建 organization
2. 加入 organization
3. 获得某个 workspace 的访问权

规范要求这三者在模型上分离，但可以在产品体验上合并为一步。

#### 6.6.1 System Bootstrap

用户不通过 browser/public API 自助创建 organization。当前稳定创建来源是用户 onboarding：

- 本地注册或外部身份首次创建本地用户时，系统自动创建 personal organization
- 创建用户的 human 自动成为该 personal organization 的 `org_owner`
- onboarding 同时创建默认 workspace，并写入 `organization_settings.default_workspace_id`

加入已有 organization 的路径仍是 invitation、SSO/provider auto-join 或目录同步投影；这些路径不等价于创建新的 organization。

#### 6.6.2 Join by Invitation

provider 应支持至少一种显式邀请机制：

- email/mobile invite
- invite code
- 或两者同时支持

建议邀请对象具备以下字段或等价语义：

- `organization_id`
- `target_kind?` / `target_identifier?`，当邀请使用 email/mobile target 时必须遵守 [`Invite Identity Target Spec`](../identity/invite-identity.md)
- `code/token`
- `is_active`
- `max_uses`
- `used_count`
- `expires_at?`
- `default_workspace_grants?`
- `default_primary_team_id?`
- `default_additional_team_memberships?`

邀请被接受后，效果应至少分为以下步骤：

1. 创建或绑定 `organization_member`
2. 赋予 `primary_team` 与额外 team membership（若存在）
3. 根据 invitation policy 投影 workspace/team grant

#### 6.6.3 Join by Verified External Identity

当 organization 配置了 `sso_auto` 或等价的自动加入策略时，provider 可以基于“已验证的外部身份”自动把 user 加入 organization。

允许的自动加入入口包括：

- vendor SSO 登录成功
- 用户使用已验证的邮箱登录，而该邮箱可唯一匹配到某个 directory user
- 用户使用已验证的手机号登录，而该手机号可唯一匹配到某个 directory user

自动加入的推荐效果至少包括：

1. 解析登录得到的认证身份或已验证联系信息
2. 在 provider 控制下匹配一个唯一的 organization + directory user
3. 创建或绑定本地 `organization_member`
4. 如 directory sync 已完成，可把该 user 对应的 unit / team 关系落到本地投影

规范要求：

- 自动加入只能使用“已验证”的邮箱、手机号或认证身份，不得使用未验证联系信息
- 自动加入必须要求匹配结果唯一；若同一邮箱/手机号命中多个 organization 或多个 directory user，不得静默加入
- 不得使用 display name 作为自动加入匹配键
- 自动加入 organization 不等于自动加入全部 workspace；workspace access 仍由 team / `effective_grant` 编排
- 若目录同步尚未完成，provider 可以先完成 member bind，再等待后续 directory sync 补齐 org chart / unit membership

#### 6.6.4 Empty Organization Bootstrap

若 organization 处于“已创建但尚无 owner/admin”的空组织状态：

- 首个被接受进入该 organization 的人类 principal 应被提升为 `org_owner` 或 `org_admin`
- 该规则仅用于 bootstrap，不应替代正常的 owner transfer 流程

#### 6.6.5 Join Organization 与 Join Workspace 分离

加入 organization 不自动等于加入所有 workspace。

推荐规则：

- 先建立 `organization_member`
- 再由 policy / invitation / team grant 决定其获得哪些 workspace access
- `workspace_bootstrap_policy` 可以把两步合并成默认体验，但模型上仍然保持分离

### 6.7 Organization 角色

organization 级最小角色建议为：

- `org_owner`
- `org_admin`
- `org_member`

这些角色属于 provider 侧治理语义，不直接替代 workspace 角色。

它们应通过 grant 投影影响：

- 谁可管理 organization
- 谁可管理 organization 拥有的 workspace
- 谁可获得某个 workspace 的管理权限
- 谁可审批 service installation

### 6.8 Org Chart 模型

team 之上的 org hierarchy / org chart 默认由 `organization_unit` 表达。

规范要求：

- `organization_unit` 属于单个 organization
- `organization_unit` 应被视为 organization hierarchy node；产品层可以呈现为部门、事业部、分部、组织节点等语义，但 provider 内部稳定模型仍为 unit
- unit 形成单父层级树；provider 如需兼容外部多父结构，应在 provider 内部消化并投影为单一展示树
- 每个 `organization_member` 可以有一个 primary unit 和零到多个 secondary unit
- unit 可以声明 manager / lead，但该身份默认只具有目录治理含义，不自动拥有 workspace/channel 管理权限
- unit 应支持 `status`、`external_id`、`synced_at` 一类目录同步字段

`organization_unit` 的职责：

- organization hierarchy 的主载体
- 组织结构展示
- 目录导航
- HR / SCIM / 外部目录同步
- 上下级归属与汇报线表达

`organization_unit` 不直接作为 core 授权主体。

### 6.9 Team 模型

team 由 `organization_team` 表达，是 organization 内的协作分组对象。

规范要求：

- `organization_team` 属于单个 organization
- `organization_team` 应具备 `status = active | inactive | archived`
- v1 中 team 的稳定成员锚点应为 `organization_member`
- team 中的成员必须关联该 organization 的 active `organization_member`
- 每个 organization member 必须拥有一个 primary team
- team 不要求负责表达 org hierarchy；当前推荐把 organization 层级保持在 `organization_unit`
- team 可以跨多个 unit 组建
- team 可以为空间化表达某类协作关系，例如：
  - 产品团队
  - 值班团队
  - 运营团队
  - 安全审计团队

建议 team 支持以下来源类型：

- `manual`
- `synced`
- `unit_backed`
- `system`

其中：

- `manual`：手工维护成员
- `synced`：来自外部目录或 IdP
- `unit_backed`：由某个 unit 投影出的 shadow team
- `system`：平台内部维护，例如默认审批团队

建议 team 具备最小治理角色：

- `team_owner`
- `team_admin`
- `team_member`

建议 membership 具备最小来源类型：

- `direct`
- `derived`
- `synced`

并遵循以下规则：

- team membership 默认表达的是 organization member membership；provider 可按需同时投影为 human-facing directory membership
- `manual` team 可由 `team_owner/team_admin` 维护成员
- `synced`、`unit_backed`、`system` team 默认不允许本地任意修改成员
- `derived` membership 应可追溯到其来源 unit / sync rule / policy rule

v1 不要求 core 理解 team nesting。provider 如需 group / nested group，可在内部实现，并最终投影为 team 或 `effective_grant`；但标准的 organization hierarchy 应优先落在 `organization_unit`。

### 6.10 Org Chart 与 Team 的关系

org chart 与 team 必须明确分层，且层级概念优先放在 unit，而不是要求 team 自身承担组织层级：

- `organization_unit` 表达“组织结构中的节点、人属于哪里”
- `organization_team` 表达“人和谁一起协作、一起获得什么授权”

二者的关系如下：

- 一个 organization member 必须属于一个 primary team
- 一个 organization member 通常属于一个 primary unit
- 一个 organization member 可以同时获得多个 additional team membership
- 一个 team 可以跨多个 unit
- 一个 unit 可以零个、一个或多个对应 team
- team 可以保持扁平；provider 若内部有 nested group 或目录组层级，应在 provider 内部收敛后再投影到 team / `effective_grant`

为了支持“按部门授权/共享”的常见体验，规范要求：

- provider 不直接把 `organization_unit` 作为授权主体暴露给 core
- 若产品需要“分享给部门/部门自动获得某 workspace 访问”，provider 应为该 unit 生成或复用一个 `unit_backed team`
- 所有授权最终都通过 team 或 `effective_grant` 投影给 core

这样可以保证：

- org chart 保持为目录模型
- team 保持为协作/授权模型
- core 只消费稳定 grant，不消费 provider 内部结构图

### 6.11 Team 与 Workspace / Channel / Service 的关系

team 是 provider 侧最主要的授权编排对象。

建议关系如下：

- team -> workspace
  - 决定哪些 team member 可访问某 workspace
  - 决定该 organization member 在该 workspace 中投影为 `owner | admin | member` 的哪一类兼容角色
- team -> channel
  - 决定是否应为 team member 物化出的 workspace member 创建/维持 `channel_membership`
  - 决定 team 是否拥有 `channel.manage_members`
- team -> service_installation
  - 决定哪些 team 可审批 installation
  - 决定哪些 team 成员可 invoke 某类 service capability

这些关系可以在 provider 内部表示为 binding、role assignment 或其他模型；
对 core 的稳定输出必须仍然是 `effective_grant`。

### 6.12 外部目录与同步契约

当 provider 接入外部目录、SSO、SCIM 或企业通讯录时，必须先落本地投影，再为 core 生成 grant。

这里必须明确区分两类相关但不同的契约：

- 认证契约：SSO / IdP 登录，用于确认“这个 human 是谁”、建立 session、触发 join / bind
- 目录契约：directory / SCIM / 企业通讯录同步，用于获得 `organization_member`、`organization_unit`、`organization_team` 及其 membership / hierarchy

二者可以来自同一个厂商或同一个企业平台，但 provider 不得因为“来源厂商相同”就把认证绑定与目录绑定混成一个稳定外部键。

规范要求：

- 外部对象不得直接替代本地主键
- provider 必须为 `organization_member`、`organization_unit`、`organization_team` 保留：
  - `external_provider`
  - `external_id`
  - `synced_at`
  - 按需保留 `snapshot/claims`
- `organization_unit` / `organization_team` 上的 `external_provider` / `external_id` 默认表达目录对象身份，而不是 SSO 登录态本身
- SSO 登录成功后，provider 可以创建或绑定本地 `user`、`organization_member`；但这默认不等价于已经完成 org chart 同步
- provider 可以在 SSO 登录或 verified email/mobile 登录后，基于唯一匹配的 directory user 自动加入 organization；但该流程本质上仍是“先 bind 本地 member，再等待或复用 directory projection”
- org chart、unit、team、directory membership 的获得，必须经过 provider 控制的 directory sync 路径并落到本地投影，不得让 core 直接消费远端目录图
- 登录热路径不应承担整棵 org chart 抓取；大规模目录同步应通过异步 worker / sync run 或等价机制完成
- 当认证绑定与目录绑定使用不同稳定 ID 时，provider 应保留两套映射关系（可用独立 identity binding 表或等价结构），而不是强行复用单一 `external_id` 语义
- 同一个 vendor 可以同时提供认证与目录能力，但 provider 仍应在语义上区分 `auth provider` 与 `directory provider`

目录对象匹配优先级建议固定为：

1. 外部稳定 employee/user id
2. 外部 subject/open_id/union_id 一类稳定身份，但仅限 provider 明确保证其与目录对象同域时
3. 已验证 email

不得使用 display name 作为匹配键。

对于认证绑定，provider 可以单独保留 SSO subject / open_id / union_id 一类认证键；
除非 vendor 明确保证认证域与目录域共用稳定主键，否则不应直接拿认证键替代目录对象 id。

自动加入 organization 的匹配优先级建议固定为：

1. 已建立的 auth-binding / identity-binding
2. provider 明确保证稳定的外部 employee/user id
3. 已验证 email
4. 已验证 mobile

匹配必须要求唯一命中；若结果不唯一，应转入显式选择、管理员确认或待后续同步补齐，而不是自动加入错误 organization。

同步规则：

- 外部对象首次出现：创建或绑定本地投影对象
- 外部对象属性变化：upsert 本地投影对象
- 外部对象消失：默认标记 `inactive` 或移除 derived membership，不做硬删除
- 目录同步导致的 team / unit 变化，最终都必须重新编译为 `effective_grant`

provider 可以在同步期间顺带创建/绑定本地 `user` 或 `principal`，但 core 只消费同步后的本地身份与 grant 结果。

### 6.12.1 Vendor 集成模式

provider 应把外部平台视为可替换的 vendor profile，而不是把某一个厂商硬编码成 organization 模型本身。

每个 vendor profile 至少应回答以下问题：

- 认证入口是什么
  - 例如 OIDC、SAML、扫码登录、企业身份代理
- 目录入口是什么
  - 例如 SCIM、企业通讯录 API、Directory API
- 认证稳定键是什么
  - 例如 subject、open_id、union_id
- 目录稳定键是什么
  - 例如 employee_id、directory user id、department id
- 首次绑定如何 bootstrap
  - 例如 已验证 email、手机号、管理员预绑定、外部稳定 id

规范要求：

- `agentteam-org` 应把 vendor 差异吸收到 provider 内部，不把 vendor-specific graph、token 或 id 语义泄漏给 core
- vendor profile 可以声明“auth + directory 由同一个厂商提供”，但这只是一种部署形态，不改变 auth-binding 与 directory-binding 的语义分层
- 如果 vendor 的目录模型比 AgentTeam 当前需要的更复杂，provider 可以只同步当前产品所需的最小子集，不要求一次覆盖全部外部对象类型

### 6.12.2 Feishu 作为首个落地 Vendor Profile

v1 可以把 Feishu 作为首个实现的 vendor profile，但 spec 不应把 organization 模型绑定死在 Feishu 上。

Feishu profile 的定位：

- `auth_provider = feishu`
  - 负责 SSO 登录、human 身份确认、首次进入 organization 的 bind / auto-join
- `directory_provider = feishu`
  - 负责通讯录 / 组织架构同步

Feishu profile 的 org chart 映射建议固定为：

- 飞书 department -> `organization_unit`
- 飞书 department parent relation -> `organization_unit.parent_unit_id`
- 飞书 user -> `organization_member`
- 飞书 department user membership -> `organization_member_unit_membership`
- 需要“按部门授权”时，再由 provider 生成或复用 `unit_backed team`

Feishu v1 的最小同步范围应收敛为：

- department tree
- department 下的直接 user 列表
- user 的稳定目录身份与基础展示字段
- 让这些 user 能被映射到 AgentTeam 的 org member，并进一步参与多 user / 多 agent 的 workspace 协作

Feishu v1 明确不要求一次支持其全部复杂目录能力，例如：

- 所有扩展字段 / 自定义字段
- 所有复杂岗位 / 雇佣关系 / 历史关系
- 所有动态群组 / 复杂权限分区 / 细粒度可见性差异
- 所有目录对象之间的非树状复杂关系

也就是说，Feishu profile 在 v1 的目标不是“完整镜像飞书通讯录”，而是“为 AgentTeam 当前阶段的多 user / 多 agent 协作提供足够稳定的组织结构与人员映射”。

### 6.12.3 Feishu v1 的推荐同步流程

以 Feishu 为例，provider 可以基于其通讯录 API 采用以下最小同步流程：

1. 以一个或多个 configured root department 作为同步起点
2. 通过 department detail / children API 递归获得部门树
3. 将部门树落为本地 `organization_unit` 单父层级
4. 通过按部门列用户接口获取 department 下的直接 user 列表
5. 将 user 落为本地 `organization_member`
6. 将 department-user 关系落为 `organization_member_unit_membership`
7. 如需 bootstrap 已有 user 绑定，可通过 email / mobile -> user id 一类接口辅助匹配
8. 最终仍由 provider 把 unit/team 变化重新编译为 `effective_grant`

对于 Feishu 一类“同一 vendor 同时提供 SSO + 通讯录”的平台，推荐实现顺序为：

1. 先接通 SSO，打通登录与 member bind
2. 再接通通讯录同步，获得 org chart 与 unit membership
3. 最后再补 provider-managed team、unit-backed team、自动 grant 编排等更高阶治理能力

这样可以保证：

- 第一阶段先解决“用户能进组织”
- 第二阶段解决“组织结构与人员归属可见”
- 第三阶段再解决“基于组织结构的协作授权自动化”

理想体验可以进一步扩展为：

- 用户使用 Feishu SSO 登录后，若已能唯一定位到某个 Feishu directory user，则自动加入对应 organization
- 用户使用与 Feishu 通讯录一致且已验证的邮箱/手机号登录后，若能唯一匹配到该 directory user，也可自动加入对应 organization

但 Feishu v1 仍应遵守最小原则：

- 自动加入只解决 organization membership bind
- 不在登录热路径里全量抓取复杂通讯录
- 不因为自动加入而自动放大 workspace 权限

整个过程中，workspace-first 边界保持不变：

- 登录成功不等于自动拥有所有 workspace
- 同步到 unit 不等于直接获得 workspace access
- 所有授权最终仍通过 team 或 `effective_grant` 投影给 core
- active workspace human member 必须拥有其 workspace owning organization 下的 active organization member
- workspace invite 是允许创建 owning organization membership 的明确产品规则；它只授予被邀请的 workspace access，不扩散到该 organization 的其他 workspace，也不能重新启用已被停用的 organization member
- 移出 workspace 只移除该 workspace 的直接访问，不等价于移出 organization
- 移出或 suspend organization member 必须停止新的 org-derived workspace access；已有 org-derived workspace projection 在 reconcile 时撤销

---

## 7. 权限模型

### 7.1 分层

AgentTeam 的权限模型分为三层：

1. `core authz facts`
   - 由 core 直接掌握的授权事实
2. `org/provider grants`
   - 由 optional provider 产生并投影给 core 的治理 grant
3. `capability scopes`
   - 由 core 面向 agent / service 签发的短期能力范围

### 7.2 Core 权威事实

当前与近期的 core 权威事实包括：

- `users.is_admin`
- `members.workspace_role`
- `channel_memberships`
- DM 参与关系
- thread 对父 channel / DM 的继承关系
- `machines` 与 workspace 的归属关系
- `agents` 与 machine / workspace / agent member 的归属关系
- `service_installation` / `grant` / `service member`（目标扩展）

### 7.3 Stable Resource Kinds

core 只需要理解少量 primitive resource kind：

- `workspace`
- `channel`
- `dm`
- `thread`
- `machine`
- `agent`
- `service_installation`

Task / Memory / Cron 等业务资源由对应服务自行建模。

### 7.4 Stable Actions

core authorizer 的稳定 action 集建议固定为：

| Resource Kind | Actions |
|---------------|---------|
| `workspace` | `access`, `manage_members`, `manage_member_roles`, `manage_runtime`, `install_service` |
| `channel` | `read`, `post`, `manage_members` |
| `dm` | `read`, `post` |
| `thread` | `read`, `post` |
| `machine` | `connect`, `manage` |
| `agent` | `operate`, `read_workspace`, `act_as_member` |
| `service_installation` | `approve`, `invoke` |

说明：

- 当前兼容 API 需要的 `owner | admin | member` 是 role 投影，不是长期唯一授权模型
- organization provider 如需更细粒度 policy，应先编译到上述 stable action

### 7.5 Allow-Only

AgentTeam 采用 allow-only：

- 不引入 deny
- 多来源取并集
- 未命中允许事实即拒绝

### 7.6 决策来源

在没有 optional provider 的情况下，core 决策来源固定为：

- `system_admin`
- `workspace_role`
- `channel_membership`
- `dm_participant`
- `thread_inherit`
- `agent_self`
- `machine_self`
- `service_grant`

provider 启用后，新增来源应表现为：

- `org_grant`

而不是让 core 直接理解 provider 内部的 group 图、role 图或资源目录。

### 7.7 Organization 状态门禁

对于属于 organization 的 workspace，organization 状态必须成为授权前置门禁。

规范要求：

- 当 `organization_settings.status != active` 时，provider 不得继续为该 organization 下的 workspace 输出普通 `effective_grant`
- 当 `organization_member.status != active` 时，provider 不得继续为该 principal 输出该 organization 下的普通 `effective_grant`
- core 在处理 `org_grant` 时，必须把 organization/member 状态视为 grant 有效性的前置条件

默认效果：

- `organization.suspended`：阻断该 organization 下普通成员的 workspace / channel / DM / service 常规访问
- `organization_member.suspended|inactive`：阻断该 principal 基于 organization 派生的访问
- 历史消息身份、历史审计记录、历史 member 行不得因此被硬删除

唯一允许的例外是显式声明的系统级 break-glass 或维护路径；
这类路径不能静默扩展成普通业务读写权限。

---

## 8. 消息与运行时授权规则

### 8.1 Workspace

`workspace.access` 是所有 workspace-scoped 操作的前置条件。

当前默认来源：

- human member 存在且 `workspace_role` 有效
- system admin 的显式 break-glass 路径
- 未来 provider 投影的 `org_grant`
- `workspace.manage_member_roles` 默认为 owner-only，以保持当前兼容 API 的成员角色变更语义

workspace 所属 organization 参与普通授权时，还必须满足：

- organization 处于 `active`
- 产生该 grant 的 `organization_member` 处于 `active`
- 产生该 grant 的 `organization_member` 处于 `active`

### 8.2 Channel

`channel.read` / `channel.post` / `channel.invite_members` / `channel.manage_members` 规则：

- `#all`：所有有效 member 自动拥有访问权；不支持直接移除成员，也不存在 transferable owner
- 普通 `private` channel：
  - `channel.read` / `channel.post` 仅限当前已加入成员
  - 未加入的人类成员（包括 workspace owner/admin）不拥有额外可见性，也不拥有额外管理权
- 普通 `public` channel：
  - workspace 有效 member 可以 discover metadata 并执行 join
  - 真正的 `channel.read` / `channel.post` 仍然要求已经加入；pre-join 只能看到 metadata，不能读取消息、task 或成员列表
- `channel.invite_members`：任意已加入的 human member 都可以执行
- `channel.manage_members` 以及相关 channel metadata update / delete / owner transfer：仅 ordinary channel 的单 human owner 可以执行
- organization/provider grant 只能决定某个 principal 是否拥有 `workspace.access`；不能为 private channel 额外合成 workspace-admin backdoor

### 8.3 DM

`dm.read` / `dm.post` 默认仅限 DM 参与双方。

若未来产品改变 agent DM 可见性策略，也必须仍然由 core 以明确规则表达，而不是交给 provider 自由解释。

### 8.4 Thread

`thread.read` / `thread.post` 继承父 `channel` 或父 `dm` 的访问权。

thread 不应拥有独立于父消息空间之外的 ACL。

### 8.5 Machine

- `machine.connect` 只通过 `machineApiKey`
- `machine.manage` 默认为 workspace owner/admin
- machine 不是消息作者，不参与 channel / DM 授权

### 8.6 Agent

- `agent.operate`、`agent.read_workspace` 默认为 workspace owner/admin
- `agent.act_as_member` 仅允许 agent 代表自己的 agent member
- agent token 只允许访问 `/api/v1/workspaces/:workspaceId/agents/:agentId/runtime/*`

### 8.7 Service Installation

- `service_installation.approve` 由 workspace owner/admin 或 provider grant 执行
- `service_installation.invoke` 必须依赖 installation grant 和 capability scope

---

## 9. Act-As 规则

act-as 规则必须由 core 固定，不交给 provider 自由解释。

最小规则：

- user 只能代表自己在目标 workspace 下的 human member
- agent 只能代表自己的 agent member
- service 只能代表该 installation 对应的 service member
- machine 只能建立控制面连接，不能 act as 消息作者

---

## 10. Authorizer Contract

### 10.1 授权输入

core 内部统一使用如下授权输入：

```text
actor_principal_id
actor_principal_type
workspace_id
actor_member_id?
resource_kind
resource_id
action
act_as_principal_id?
act_as_member_id?
installation_id?
requested_scopes[]
request_id
```

其语义是：

> who wants to do what in which workspace as whom

### 10.2 授权输出

core authorizer 应输出统一结构：

```text
allowed
decision_source
resolved_principal_id
resolved_member_id?
scopes[]
deny_code?
```

推荐 `deny_code`：

- `unauthenticated`
- `not_in_workspace`
- `workspace_access_denied`
- `not_channel_member`
- `not_dm_participant`
- `thread_access_denied`
- `act_as_forbidden`
- `scope_missing`

HTTP 层再将其翻译为兼容 API 的 `401/403/404` 语义。

### 10.3 Provider Integration Contract

provider 不应把完整 RBAC 结构直接暴露给 core。

provider 面向 core 的稳定输出应收敛为：

- principal 是否 active
- organization 是否 active
- organization member 是否 active
- workspace ownership 关系
- 某 principal 在某 workspace 上的有效 grant
- 某 principal 是否可审批 installation
- 某 installation 的默认审批策略或 scope 补充

推荐统一为 `effective_grant` 视图或等价接口：

```text
principal_id
organization_id?
workspace_id
resource_kind
resource_id?
actions[]
compatibility_workspace_role?
source
expires_at?
```

其中：

- `compatibility_workspace_role` 仅在 provider 需要为 human workspace access 投影 `owner | admin | member` 时使用
- provider 若不提供该字段，则必须提供足以被 core 确定性映射为兼容 workspace role 的 actions/grant
- `source` 应可区分 `team`、`unit_backed_team`、`sync_rule`、`manual_admin_grant` 等来源，便于审计与回溯

provider 可以内部使用 RBAC、group nesting、role binding、external policy，但 core 只消费 `effective_grant`。

---

## 11. Token 与 Capability 模型

### 11.1 Human Token

Human JWT 只承载认证事实。

建议 claim：

- `sub`
- `sid` / `session_id`
- `exp`
- 可选 `is_admin`
- 可选 `token_version`

不得包含：

- organization 列表
- group 列表
- workspace 角色快照
- 资源权限快照

### 11.2 Refresh Token

refresh token 只用于会话续期，不承载授权信息。

### 11.3 Machine Token

`machineApiKey`：

- 只用于 `/api/v1/workspaces/:workspaceId/daemon/connect?key=...`
- 只代表 machine 控制面主体
- 不能访问 human API
- 不能访问 agent internal API

### 11.4 Agent Token

`agentAuthToken`：

- 只用于 `/api/v1/workspaces/:workspaceId/agents/:agentId/runtime/*`
- 与单个 agent 绑定
- 应在 `agent:start` 周期轮换
- 不得回退到 `machineApiKey`

### 11.5 Service Capability Token

跨服务信任的长期模型为短期 capability token。

最小字段：

- `subject_principal_id`
- `subject_principal_type`
- `subject_member_id`
- `workspace_id`
- `installation_id`
- `scopes`
- `issued_at`
- `expires_at`
- `request_id`

header 可以作为 transport，但不是最终信任模型。

---

## 12. 成员物化与撤权

### 12.1 Human Member 物化

organization member 获得 workspace 有效 access 后，core 必须确保：

- 该 workspace 下存在 human member
- 该 member 自动加入 `#all`

access 来源可以是：

- 当前直接邀请/加入流程
- 未来 provider 投影的 workspace grant

### 12.2 Agent 物化

agent 进入 workspace 时，必须满足以下规则：

- agent 物化必须有明确来源：workspace 创建、direct selection、team default set 或显式审批流程
- agent 物化必须形成可审计的 workspace-scoped installation 事实
- 当前实现中，该 installation 事实可以直接落在现有 `agents` 表与对应 `member` 行上
- agent 若尚未 materialize 到某 workspace，则该 workspace 不应把它视为可消息寻址的 member
- organization member 可以进入 workspace，不等于其相关 agent 已自动进入该 workspace

### 12.3 撤权

用户失去 workspace access 时：

- 新访问应立即失效
- 历史消息身份不得被硬删除

推荐策略：

- 保留 member 行与历史消息
- 通过 access 事实撤销其后续读写权限
- agent installation 遵循 workspace agent 生命周期；撤销 human workspace access 不得隐式删除无关 agent，除非有明确的 workspace agent 策略要求
- 若未来需要显式 disabled 状态，应作为成员生命周期字段引入，而不是删除历史身份

---

## 13. 兼容要求

### 13.1 Workspace Role 兼容

当前 public API / UI 仍要求：

- `owner`
- `admin`
- `member`

因此：

- `members.workspace_role` 继续作为当前兼容外露语义
- 在 provider 模式下，也必须继续投影回这三个兼容角色

### 13.2 Channel Membership 兼容

`channel_memberships` 继续作为 channel participant 的核心事实来源。

provider 可以影响“谁应该拥有某个 channel grant”，但不能替代 core 对 channel membership 的消息语义。

### 13.3 API 与 UI 稳定性

引入 organization/provider 后，不得破坏：

- `/api/v1/*`
- `/socket.io/*`
- `/api/v1/workspaces/:workspaceId/agents/:agentId/runtime/*`

对 Web UI、daemon、和公开 API 可见的角色、可见性、消息空间语义必须保持稳定。

### 13.4 Workspace Switch Contract

当前 workspace 切换语义必须保持成立：

- `/api/v1/workspaces` 仍然返回当前 human 可访问的 workspace 列表
- 这些 workspace 可以包含 org-derived access，但 wire contract 不变
- `X-Workspace-Id` 与现有 workspace selection 逻辑继续作为当前 workspace 上下文

---

## 14. 推荐落地顺序

### Phase 1

- 固化 core authorizer 输入/输出
- 固化 act-as 规则
- 固化 machine / agent / human token 语义

### Phase 2

- 引入 `service_installation` / `grant` / `service member`
- 让跨服务调用使用 capability token

### Phase 3

- 把 scattered role check 收敛到 core authorizer
- 继续使用现有 `workspace_role` / `channel_memberships` / DM facts

### Phase 4

- 引入 optional `agentteam-org` provider
- provider 落地 `organization -> member/team/unit -> workspace grant` 模型
- provider 输出 workspace ownership 视图与 `effective_grant`
- 保持 core 与兼容 API 语义不变

### Phase 5

- 视需要增加 group、org chart、external authority、shadow / authority 模式

---

## 15. 禁止项

以下变更不得作为普通实现细节直接进入 core：

- 在 core 中把 organization 设为所有 workspace 的强制父级
- 在 core 中引入通用 `roles / role_bindings / authz_resources`
- 让 human JWT 携带 workspace / group / permission 快照
- 让 public API 热路径同步依赖远程授权判定
- 让 machine token 或 agent token 获得 human API 权限
- 让独立服务直接共享或直接写 core 业务表

这类变更属于架构边界变化，必须先修改 `docs/architecture-spec.md`。

---

## 16. Reference（Non-Normative）

本规范正文仅定义 AgentTeam 自身设计。以下内容属于参考来源，不是规范前提。

### 16.1 来自 Clawith 的可吸收经验

- JWT 只承载最小认证事实，不承载完整授权快照
- 平台级管理员与组织级管理员应分离
- 外部 authority 不应成为业务主键来源
- richer org / permission provider 可以内部使用 RBAC，但不应直接压进 core

### 16.2 对 Clawith 风格企业 RBAC 的使用方式

若未来 `agentteam-org` provider 需要企业级 RBAC：

- 可以在 provider 内部使用 `Permission = resource × action`
- 可以使用 `Role`、`RoleBinding`、`Group`、`Group Inheritance`
- 可以维护 provider 自己的资源目录或映射表

但 provider 输出给 `agentteam-core` 的仍应是：

- workspace ownership 关系
- principal / organization 状态
- effective grant
- installation 审批与 scope 补充

core 不直接消费 provider 内部的完整 RBAC 图。
