# 02 安全三面 —— 身份 / 授权 / 保密

vox1000 的安全模型是**三面正交**:一面失效不连带两面,各面独立换挡。
连接建立时三面一次走完(verify → 验签 → 授权闸 → 派发),消费方
不需要自己拼装握手。

## 身份面:PeerId 即公钥,签名环验真身

- `PeerId` = 32 字节 ed25519 公钥;`peer_id_from_seed` / 
  `peer_id_from_public_key` 双构造。
- 呼叫时引擎自动走**签名环**:发起方签
  `"vox1000-dial-v1" ‖ 握手 ‖ 对端身份 ‖ 协议串`,应答方以对端
  宣称公钥(= PeerId)验——**没有 CA,身份即公钥,网络里冒充不了
  别人的 PeerId**。验签失败 = 拒码 1,连接不建立。

## 授权面:CallGate 方言(谁能拨这门)

身份答"你是谁",授权答"准不准你进"。闸 = 三个回调:

```moonbit
pub(open) trait CallGate {
  async resolve(self : Self, peer : PeerId) -> RosterEntry?   // 名册查
  async admit(self : Self, caller : Caller) -> Bool           // 准入判
  async audit(self : Self, event : CallEvent) -> Unit         // 审计记
}
```

`Caller{peer, protocol, from, token}` 一册全供给;token 走
`DIAL_OPEN` 帧,呼叫端 `connect(peer, protocol, token=...)` 携带。

**四档现成预设**:

```moonbit
socket.mount_gate(gate=@core.gate_admit_all())          // 全放(缺省)
socket.mount_gate(gate=@core.gate_admit_none())         // 全拒(维护窗)
socket.mount_gate(gate=@core.gate_admit_allowlist(allow=[peer_bytes]))
socket.mount_gate(gate=@core.gate_audit_collector(log~)) // 审计收集
```

自定义闸 = impl 三回调,引擎零改。**生产现成件**:
`adapter/gate_moongard` 把 [moongard](https://mooncakes.io/docs/#/thy1016/moongard)
access token 接进来,并互核 `sub == hex(PeerId)`(票面上的"谁"必须
等于线上的"谁"):

```moonbit
let gate = @gate_moongard.MoongardGate::new(
  issuer="vox-demo", audience="vox-net",
  ttl_ms=60000L, key=session_key, now=fn() { @async.now() },
)
node.mount_gate(gate=gate)
```

无票 / 票过期 / sub 不符 → 拒码 0(准入拒),审计照记。

## 保密面:secchan 记录层(线上一路密文)

- `Node::new(psk=...)` 配上 PSK,引擎即走 PSK-CR 三消息握手
  (HELLO / SALT / AUTH),HKDF 出**方向密钥**,此后每包 AES-GCM
  密封(seq 确定性 nonce + 滑窗防重放)。本体在
  [moonkcp/secchan](https://mooncakes.io/docs/#/thy1016/moonkcp),
  本仓只座调用面。
- **不配 psk = 明文回放形**(显式 None),仅测试/回放;生产必配。
  PSK 伸展(口令 → 32B 种子)用 Argon2/PBKDF2 在启动期一次性做,
  不要放进每连接路径。

## 一张表带走

| 面 | 回答 | 挡位 | 失效表现 |
|---|---|---|---|
| 身份 | 你是谁 | PeerId(ed25519) | 冒充 = 验签拒(拒码 1) |
| 授权 | 准不准进 | CallGate(token) | 无权 = 准入拒(拒码 0) |
| 保密 | 偷听得到吗 | secchan 记录层 | 密钥不符 = 拒码 2 |

三面互相不知道对方的实现:换闸不动签名,换签名不动记录层。
