Meshline
简体中文规范下载 Markdown 原文

中文为规范基准;英文为维护译本。

群组核心对象

群组托管协议

群组 ID

创建者生成新的 16-byte nonce;同一创建者在同一中继下的 nonce 不得重复,推荐使用密码学安全随机源生成。对以下群标识对象的网络绑定 JSON 输入计算 SHA-256:

{
  "$type": "meshline.group.identity",
  "creator": "neo:860833102:...",
  "nonce": "base64url...",
  "relay_id": "0x..."
}

creator 是首次创建群的 owner,relay_id 是选定的托管中继;二者和 nonce 在群生命周期内不变。群 ID 按以下公式派生,其中 group_identity_input 表示上述群标识对象:

group_id = "grp_" + base64url(first_16_bytes(SHA-256(network_bound_json_bytes(group_identity_input))))

group_id 必须匹配 ^grp_[A-Za-z0-9_-]{22}$。名称、简介、成员、容量、邀请策略、事件位置和密钥版本不参与派生。群 ID 与托管中继绑定,但不能从 ID 反推出中继。

GroupState

GroupState 是托管中继维护的当前群状态:

字段 类型 必需 语义与约束
group_id string 是 当前状态所描述的群 ID
name string 是 当前群的名称;必须至少包含一个非空白字符,UTF-8 编码后最多 256 bytes
description string 否 当前群的简介;省略表示没有简介;非空时不得仅包含空白字符,UTF-8 编码后最多 4 KiB(4,096 bytes)
status string 是 群的生命周期状态:active 表示群仍在存续;closed 表示群已永久关闭
owner string 是 群主账户;必须在当前成员投影中恰好出现一次,且对应角色必须为 owner
member_capacity integer 是 该群的成员数上限,必须为正;可以暂时小于成员数,此时保留现有成员但拒绝继续准入
member_count integer 是 当前成员数量,包括 owner 和 administrator;必须为正
invite_policy string 是 群邀请策略;owner 和 administrator 不受该字段限制

invite_policy 的含义为:

值 普通成员可以创建的邀请
administrators 不可以创建邀请
members_targeted 只能创建指定 invitee 的定向邀请
members_shareable 可以创建定向邀请,也可以创建公开邀请

成员投影按群 ID 和账户 ID 区分成员,包含 account、role 和 member_encryption_public_key。客户端从已验证的管理链建立成员资格、角色和公钥。

群存续期间,中继必须保留当前状态和成员投影。改变状态的方法必须使投影更新与对应事件追加原子生效。

GroupEvent

GroupEvent 是托管中继追加的权威时间线条目,用于记录群管理操作、密钥轮换和群消息:

字段 类型 必需 语义与约束
sequence integer 是 中继为该事件分配的唯一时间线位置;创建事件固定为 0,后续值必须严格大于此前时间线头但不要求连续。客户端用它排序、分页和引用群消息
epoch integer 是 该事件完成后群所处的密钥版本;创建事件为 0,消息事件必须等于其信封版本;未推进密钥的事件保持完整群时间线中前一事件的版本
payload object 是 完整事件正文,通过 payload.$type 识别类型;正文结构与处理规则见对应对象或方法定义
accepted_at integer 是 中继接受该事件的 UTC Unix 秒;用于保留期限,不替代 sequence 的排序语义
signer_device_id string 条件 中继接受客户端写请求时验证的设备 ID;客户端发起的事件必须提供并引用 group.sync 响应中的证书,中继主动轮换必须省略

完整群时间线中的 epoch 单调不减,但不要求连续。

消息事件是 payload.$type = meshline.group.message 的事件,可以按群组数据保留与访问规则到期清理。其他事件属于管理事件,连同其验签证书永久保留。

消息过滤或裁剪、以及客户端从中途读取,可能使相邻可见事件并非时间线中的相邻事件。管理事件不受消息访问区间限制且不得裁剪;从创建开始完整同步时,客户端可以取得全部管理事件。密钥版本的处理遵循 group.sync。

邀请创建与撤销、申请提交或拒绝、成员密钥重置请求的提交、替换、撤回或拒绝,以及客户端秘密轮换 prepare 不写入时间线。这些待处理记录由批准者在操作时验证;入群批准事件记录已经授权的账户和公钥,成员密钥重置批准事件记录账户和新公钥。封禁和解除封禁影响后续操作权限,成功调用必须将完整签名请求写入管理链。

中继秘密轮换事件

中继主动更换中继秘密时生成以下 payload:

字段 类型 必需 语义与约束
$type string 是 固定为 meshline.group.key.rotated

新密钥版本由外层 GroupEvent.epoch 表达。该正文由托管中继生成,不携带 device_signature,外层事件必须省略 signer_device_id。客户端验证正文的固定值及事件的密钥版本推进关系。该事件永久保留,但不改变成员、角色、封禁、客户端秘密承诺或管理链头。

管理链

参与事件与前序引用

管理链只连接客户端签署并实际写入时间线的管理事件。消息事件和中继主动轮换不推进这条链。下列正文类型参与管理链:

正文 $type 生效内容
meshline.group.create 建立群、初始 owner 和客户端秘密承诺
meshline.group.application.approval 批准成员加入
meshline.group.update 修改群属性
meshline.group.role.update 修改管理角色
meshline.group.owner.transfer 转让所有权
meshline.group.member.leave 签署账户离群
meshline.group.member.removal 移除目标成员
meshline.group.member.recovery.approval 批准成员新公钥
meshline.group.secret.rotation 更新客户端秘密承诺及可选 owner 公钥
meshline.group.member.ban 封禁目标账户并移除其中的当前成员
meshline.group.member.unban 解除封禁
meshline.group.close 永久关闭群

创建请求是管理链起点,不携带 prev_hash。其余上述请求的签名正文必须携带 prev_hash,其值为本地已验证的上一项管理链摘要,匹配 ^sha256:[A-Za-z0-9_-]{43}$,解码结果必须为 32 bytes 且符合无 padding base64url 的规范编码。prev_hash 与全部业务字段一同由调用设备签署,不能由中继在验签后补充或改写。未写入时间线的请求不推进管理链;消息信封及中继主动轮换正文不定义此字段。

管理链摘要计算

管理事件 E 对完整 payload 的网络绑定 JSON 输入计算哈希,输入保留根 device_signature 和所有未知属性:

management_hash(E) = network_bound_json_hash(E.payload)

输入构造遵循网络绑定 JSON 输入规则。摘要包括正文签名;计算正文签名时只排除根 device_signature。signer_device_id 用于定位验签证书,客户端仍须验证证书、正文签名及所属账户的操作权限;其余外层字段继续按排序、时间和密钥规则校验。下一项管理操作引用 management_hash(E)。

中继提交与并发控制

中继必须验证会话、设备签名及调用权限;创建请求按 group.create 的规则建立链起点。对其他请求,中继必须确认 prev_hash 等于当前管理链头,且操作生效时调用权限与该匹配关系仍然成立。违反 prev_hash 字段约定时返回 bad_request。

上述请求成功时必须追加管理事件并更新管理链头。请求证据、状态、事件,以及时间线头和管理链头必须原子提交。

两个请求引用同一链头时,只能有一个成功;另一个如需重新提交,应先同步新事件、重新核对权限与操作内容,再重新签名。客户端不得只替换摘要而复用旧签名。

客户端验证与恢复

管理链验证与状态重建

客户端第一次从创建事件开始验证:核对预期群 ID 的派生关系、创建者账户、设备证书和建群签名,建立初始状态并计算链头;随后逐项验证 prev_hash、签名、签署账户在前序状态中的成员资格、角色及封禁状态,再按对应方法应用结果并计算新链头。

成员按账户区分,同一账户不得重复;角色为 owner、administrator 或 member。建群事件建立初始 owner;入群批准建立普通成员;角色更新、所有权转让、成员离开、移除或封禁、密钥重置批准和客户端秘密轮换按各方法改变成员状态。成员重新加入时使用新批准的公钥,不能恢复其此前的成员记录。待审批申请或重置请求不改变已生效状态。

封禁集合在创建时为空。客户端通过 group.sync 取得管理事件,按已验证管理链中的封禁和解除封禁请求分别加入和移除目标账户,由此重建封禁名单。

申请者和请求重置密钥的成员签署包含公钥的完整业务请求。批准者独立验证请求签名、设备证书及请求账户绑定后,在批准对象中签署该账户和公钥。其他客户端验证批准事件及批准者在前序状态中的权限。封装客户端秘密时,目标成员公钥的来源与验证必须遵循客户端秘密盒规则。

验证中断与恢复

中间管理事件缺失、无效签名、无权限操作或不能解释的管理扩展会阻止后续状态验证,不得按普通消息的拒绝规则越过它。遇到未知的客户端管理正文类型时,客户端必须暂停管理投影;扩展定义其哈希参与和状态规则后才能继续。客户端可以保留相应事件,便于后续重新处理。未知属性仍参与哈希和签名,但仅按已知方法定义的字段改变状态。

客户端重新加载本地状态时,必须保证已验证管理链头、成员投影、封禁状态及同步位置保持一致;后续处理继续已验证的管理链,不能以中继快照覆盖。管理链校验失败时,停止应用后续管理事件并报告错误;缺失记录可以通过 group.sync 重新请求。